Search code examples
c#asp.net-mvclinqgenericssystem.reflection

C# - Method with Generic Property


I've seen plenty of examples of how to use generic Objects in C#, but can I have a generic property for a static object?

I have 2 distinct lists of Providers:

List<Providers> OldProviders
List<Providers> NewProviders

OldProviders has providers from the database, and NewProviders has providers the user has selected in the View. If any OldProviders aren't in NewProviders, I want to remove them from the database. If any NewProviders aren't in OldProviders, I want to add them to the database. Simple.

One problem: Provider has 2 properties that correspond to foreign keys important for comparison: StaffID and LicenseID (both integers) Depending on the choice the user makes, I only care about one of these IDs.

Currently I have 2 sets of helper methods which are completely identical (6 total helpers), except for whether they compare using StaffID or LicenseID. It technically works, but I don't want to maintain it.

Here's an example:

private bool DeleteOldStaffProviders(List<Provider> oldSelectedStaff, List<Provider> newSelectedStaff)
        {
            foreach (var oldSelected in oldSelectedStaff)
            {
                bool remove = newSelectedStaff.SingleOrDefault(n => n.StaffID == oldSelected.StaffID) == null;
                if (remove)
                {
                    //<remove from database, return false if failure>
                }
            }
            return true;
        }

Is there a way I can rewrite this method with a generic parameter reference? i.e.

newSelectedStaff.SingleOrDefault(n => n.T == oldSelected.T) 

where T is either LicenseID or StaffID.

I've seen a lot of examples of people using LINQ and reflection for things kinda similar to this, but I can't seem to figure it out. If you could baby me through it, that would be appreciated.


Solution

  • Yes, use another parameter of type Func<Provider,int> to act as the selector:

    private bool DeleteOldStaffProviders(List<Provider> oldSelectedStaff, List<Provider> newSelectedStaff, Func<Provider,Provider,bool> selector)
    {
        foreach (var oldSelected in oldSelectedStaff)
        {
            bool remove = newSelectedStaff.SingleOrDefault(n => selector(n) == selector(oldSelected)) == null;
            if (remove)
            {
                //<remove from database, return false if failure>
            }
        }
        return true;
    }
    

    usage

    DeleteOldStaffProviders(oldStaff, newStaff, x => x.StaffID);
    DeleteOldStaffProviders(oldStaff, newStaff, x => x.LicenseID);
    

    You could do it another way - but I think its more clunky. Pass in a Func<Provider,Provider,bool> like this:

    DeleteOldStaffProviders(oldStaff, newStaff, (o,n) => o.StaffID == n.StaffID);
    

    And change a line to:

    bool remove = newSelectedStaff.SingleOrDefault(n => selector(n,oldSelected))