0

I am having trouble with generics. Using examples from here, I want to pass a property for use in EF Core. I have a feeling I am overcomplicating things. Note that I would like to stick with lamba expressions instead of passing something like nameof (which I cannot use in 4.0).

class Foo
{
        public string A { get; set; } // some unique string in DbSet for entity Person
        public string B { get; set; } // some unique string in DbSet for entity Company

        public int GetId<TEntity>(string match, Expression<Func<TEntity, string>> fieldToExamine,  Expression<Func<TEntity, int>> fieldWithId, System.Data.Entity.DbContext context)
            where TEntity : class
        {
            int retval = -1;
            if (!string.IsNullOrEmpty(match))
            {
                var expr = (MemberExpression)fieldToExamine.Body;
                var prop = (PropertyInfo)expr.Member;
                var expr2 = (MemberExpression)fieldWithId.Body;
                var idField = (PropertyInfo)expr2.Member;
                // this works because everything is explicit
                var entity = context.Set<Person>().SingleOrDefault(p => p.PersonName.Equals(match));
                // ... but I want that to be generic
                // how to reference the property to be evaluated?
                //var entity = context.Set<TEntity>().SingleOrDefault(p => ...); // <- HELP?!
                if (entity != null)
                {
                    retval = (int)entity.GetType().GetProperty(idField.Name).GetValue(entity, null);
                }
            }
            return retval;
        }
}

static void Main(string[] args)
{
// .. omitted database stuff ..
// (for Person) what I want is to match the Name field (here: p.Name) of the Person entity to the Foo property value (here: f.A) and return the database id stored in p.PersonId
int x = f.GetId<Person>(f.A, p => p.PersonName, p => p.PersonId, context);
// same for Company, but different values and fields
int y = f.GetId<Company>(f.B, p => p.CompanyName, p => p.CompanyId, context);
}
Rno
  • 784
  • 1
  • 6
  • 16

1 Answers1

1

Person class and Company class will need to implement either abstract class or interface.

public abstract class BaseEntity
{
    public abstract int Id { get; set; }
    public abstract string Name { get; set; }
}

public class Person : BaseEntity
{
    public int PersonId { get; set; }
    public override string Name { get; set; }

    public override int Id
    {
        get { return PersonId; }
        set { PersonId = value; }
    }
}

public class Company : BaseEntity
{
    public int CompanyId { get; set; }
    public override string Name { get; set; }

    public override int Id
    {
        get { return CompanyId; }
        set { CompanyId = value; }
    }
}

Then you can pass p.Name and p.CompanyId.

var entity = context.Set<TEntity>().SingleOrDefault(p => p.Name.Equals(match));
Win
  • 61,100
  • 13
  • 102
  • 181
  • I have those classes already in place. I want to pass whatever of their properties to `SingleOrDefault()`. My question is about how to write `SingleOrDefault(p=>[?passed in property?].Equals(input))` – Rno Sep 05 '18 at 22:41
  • You need to use reflection; I do not have the code. You might get the idea on how to implement it from [this](https://stackoverflow.com/a/34908081/296861) answer and Jon Skeet's original answer. – Win Sep 05 '18 at 22:50
  • 1
    I figured it out, and I was overthinking it. I just needed to pass the expression to be evaluated in SingleOrDefault() from the calling method. – Rno Sep 06 '18 at 10:27