Morks
Morks

Reputation: 352

How to pass a reference to entity class in a function

I have a list of static methods that all essentially do the same thing, just on different tables/ entity classes:

 public static List<FormFieldHistoryViewModel> GetTextLabelHistory(Guid entryId, int? formFieldId)
        {
            List<FormFieldHistoryViewModel> history = new List<FormFieldHistoryViewModel>();
            List<dbo_FormFieldsFileValues_CT> fields = new List<dbo_FormFieldsFileValues_CT>();

            using (var ctx = new Entities())
            {
                fields = ctx.dbo_FormFieldsFileValues_CT.Where(f => f.FormFieldsID == formFieldId && f.EntryID == entryId).ToList();
            }

            foreach (var row in fields)
            {
                var ffhvm = new FormFieldHistoryViewModel();
                ffhvm.DateEdited = row.DateEdited;
                ffhvm.EditedBy = row.EditedBy;
                ffhvm.Value = row.FileName;
                history.Add(ffhvm);
            }

            return history;
        }

Instead of having one method for each table/ entity object, I'd like to just pass a reference to that class as an argument to be used at every place where you see edmxobject. How can I achieve this?

public static List<FormFieldHistoryViewModel> GetTextLabelHistory(Guid entryId, int? formFieldId, type edmxobject)
        {
            List<FormFieldHistoryViewModel> history = new List<FormFieldHistoryViewModel>();
            List<edmxobject> fields = new List<edmxobject>();

            using (var ctx = new Entities())
            {
                fields = ctx.edmxobject.Where(f => f.FormFieldsID == formFieldId && f.EntryID == entryId).ToList();
            }

            foreach (var row in fields)
            {
                var ffhvm = new FormFieldHistoryViewModel();
                ffhvm.DateEdited = row.DateEdited;
                ffhvm.EditedBy = row.EditedBy;
                ffhvm.Value = row.FileName;
                history.Add(ffhvm);
            }

            return history;
        }
}

Upvotes: 0

Views: 622

Answers (2)

Steve Py
Steve Py

Reputation: 34793

To get something like this to work nice and tidy you will need a base interface for your Edited* fields. Something like:

public interface IEditable
{
   DateTime DateEdited { get; }
   string EditedBy { get; } // Assuming it stores a name.
   string FileName { get; }
}

Then each supported class inherit this interface. The method would change to:

public List<FormFieldHistoryViewModel> GetTextLabelHistory<T>(Guid entryId, int? formFieldId) where T : class, IEditable

Then your query to extract common fields from entities would be:

using (var ctx = new Entities())
{
    fields = ctx.Set<T>()
       .Where(f => f.FormFieldsID == formFieldId && f.EntryID == entryId)
       .Select(f => new FormFieldHistoryViewModel
       { 
           DateEdited = f.DateEdited,
           EditedBy = f.EditedBy, 
           Filename = f.Filename
       }).ToList();
    return fields;
}

The issue here though is likely going to be filtering, as this assumes that every table is going to have EntryId and FormFieldId which I assume may not be the case given your example was for a table FormFieldsFileValues.

If you want to do something similar for completely dissimilar entities, the IEditable can help expose the common fields, but you'll probably be out of luck actually querying the appropriate records within the method itself. The same goes for if you have different desired output view models. Again, your example had it returning a view model that had a FileName which aligns with FormFieldFileValues, but this won't suit cases where you want to return different view models.

Generic methods are suitable when the implementation across all supported classes is identical, not just similar. The output of a generic method must either be a value, a single common type, or a generic class associated with the applicable type. We can handle filtering via different sources (Key names, criteria etc.) by designing the method to accept an IQueryable<T> rather than querying inside the method, but this still requires a singular output class applicable across all implementations. For example:

public List<HistoryViewModel> GetHistory<T>(IQueryable<T> query) where T : class, IEditable
{
    string sourceType = typeof(T).Name;
    return query.Select(f => new HistoryViewModel
    {
        SourceType = sourceType,
        SourceId = f.Id,
        DateEdited = f.DateEdited,
        EditedBy = f.EditedBy, 
    }).ToList();
}

Where the IEditable exposes an Id column rather than class specific details, and it is up to the caller to build a query for what to get history view models for.

If you do want different specific history view models to pair up with different unrelated source entities then a Generic implementation likely isn't up to the task.

Upvotes: 0

hijinxbassist
hijinxbassist

Reputation: 4656

You can pass in the type parameter using generics via T.

GetTextLabelHistory<T>(Guid entryId, int? formFieldId) where T : {constraint}

In order to operate on your data context using this type, you can use the Set method of DbContext

myDbContext.Set<T>().SomeOperation()

DbContext.Set()

Generic Constraints

Upvotes: 1

Related Questions