Add object to Entity Framework dynamically using Reflection

InvisiblePanda picture InvisiblePanda · Jun 5, 2014 · Viewed 9.6k times · Source

In the following code, the type of domainObject varies (but ends with DO, which I trim then to get the corresponding table name). Having the name of the table and its type, I want to update an existing object - its name is the same as the tableName due to the EF - in the database with the new property values from domainObject. Therefore, I have to find the POCO in the table with the same ID first to overwrite this. This is the code so far:

public void Update(object domainObject)
{
  Type type = domainObject.GetType();
  string tableName = type.Name.Substring(0, type.Name.Length - 2);
  PropertyInfo tableProp = typeof(MyDbContext).GetProperty(tableName);
  Type tableType = tableProp.PropertyType;
  Type pocoType = tableType.GetGenericArguments()[0];

  int id = (int)type.GetProperty("ID").GetValue(domainObject);

  using (var context = new MyDbContext())
  {
    object table = tableProp.GetValue(context);
    MethodInfo singleMethod = tableType.GetMethod("Single");
  }
}

Usually, knowing the actual table and not just its type, I would now get the POCO via

var poco = context.TableName.Single(item => item.ID == id);

There's 2 problems here:

(1) Single is an extension method.

(2) I don't have an idea how to get the lambda expression in form of an object to pass it to the Invoke of Single.

Is there any way to do this at all with Reflection, or do I have to work around this? (For example, I could iterate through the items in table and check manually [which would load everything from the DB into memory and thus should be avoided], or maybe configure the EF to do some kind of 'override' whenever I just Add and object whose ID is already present if this is possible). Even supposing I could work around this, I'd still like to know a definitive answer to this question, since it's pretty interesting for me!

Answer

mr100 picture mr100 · Jun 5, 2014

If you want to use reflection and to find given entity by ID then, if ID is primary key this is fairly simple as this is all you have to do:

object entity = context.Set(domainObject.GetType()).Find(id);

If your property is not primary key then you need to do it as follows:

ParameterExpression p = Expression.Parameter(domainObject.GetType());
Expression property = Expression.Property(p, "ID");
Expression c = Expression.Constant(id);
Expression body = Expression.Equal(property, c);
Expression exp = Expression.Lambda(body, new ParameterExpression []{ p });

MethodInfo singleMethod = typeof(Queryable).GetMethods()
    .Single(m => m.Name == "Single" && m.GetParameters().Count() == 2)
    .MakeGenericMethod(domainObject.GetType());

DbSet dbSet = context.Set(domainObject.GetType());
object entity = singleMethod.Invoke(null, new object[]{ dbSet, exp });

First with Expression class you build expression that will be passed to Single method (in your case this will be p => p.ID == id). Then you search proper Single method from Queryable class. The last thing is to invoke this method with proper parameters. This way you may do any linq queries with use of Reflection.