user3333134 user3333134 - 1 month ago 25
C# Question

Dynamic LINQ - Entity Framework 6 - Update Records for Dynamic Select

C# rookie. Below is my code, been trying for hours now to get this to update some fields in my DB and tried many different implementations without luck.

// Select all fields to update
using (var db = new Entities())
{
// dbFields are trusted values
var query = db.tblRecords
.Where("id == " + f.id)
.Select("new(" + string.Join(",", dbFields.Keys) + ")");

foreach (var item in query)
{
foreach (PropertyInfo property in query.ElementType.GetProperties())
{
if (dbFields.ContainsKey(property.Name))
{
// Set the value to view in debugger - should be dynamic cast eventually
var value = Convert.ToInt16(dbFields[property.Name]);
property.SetValue(item, value);

// Something like this throws error 'Object does not match target type'
// property.SetValue(query, item);
}
}
}
db.SaveChanges();
}


The above code when run does not result in any changes to the DB. Obviously this code needs a bit of cleanup but i'm trying to get the basic functionality working. I believe what I might need to do is to somehow reapply 'item' back into 'query' but I've had no luck getting that to work no matter what implementation I try i'm always receiving 'Object does not match target type'.

This semi similar issue reaffirms that but isn't very clear to me since i'm using a Dynamic LINQ query and cannot just reference the property names directly. http://stackoverflow.com/a/25898203/3333134

Answer

Entity Framework will perform updates for you on entities, not on custom results. Your tblRecords holds many entities, and this is what you want to manipulate if you want Entity Framework to help. Remove your projection (the call to Select) and the query will return the objects directly (with too many columns, yes, but we'll cover that later).

The dynamic update is performed the same way any other dynamic assignment in C# would be, since you got a normal object to work with. Entity Framework will track the changes you make and, upon calling SaveChanges, will generate and execute the corresponding SQL queries.

However, if you want to optimize and stop selecting and creating all the values in memory in the first place, even those that aren't needed, you could also perform the update from memory. If you create an object of the right type by yourself and assign the right ID, you can then use the Attach() method to add it to the current context. From that point on, any changes will be recorded by Entity Framework, and when you call SaveChanges, everything should be sent to the database :

// Select all fields to update
using (var db = new Entities())
{
    // Assuming the entity contained in tblRecords is named "ObjRecord"
    // Also assuming that the entity has a key named "id"
    var objToUpdate = new ObjRecord { id = f.id };

    // Any changes made to the object so far won't be considered by EF

    // Attach the object to the context
    db.Attach(objToUpdate);

    // EF now tracks the object, any new changes will be applied

    foreach (PropertyInfo property in typeof(ObjRecord).GetProperties())
    {
        if (dbFields.ContainsKey(property.Name))
        {
             // Set the value to view in debugger - should be dynamic cast eventually
             var value = Convert.ToInt16(dbFields[property.Name]);
             property.SetValue(objToUpdate, value);
        }
    }

    // Will only perform an UPDATE query, no SELECT at all
    db.SaveChanges();
}
Comments