Rolling your own SQL Update on top of the Entity Framework - Part 4
Okay so we've got to the interesting bit at last.
We've hooked up our extension method, we've got a query for filtering the records we are updating, we've got a map from object Properties to database Columns. So now comes the interesting bit...
We now have to build the UPDATE [Table] SET Column = value, .... WHERE Key IN (...) query.
Basically we need to convert the Expression<Func<T,T>>
public SqlCommand GetUpdateCommand(Expression<Func<T, T>> updator){
//Get the information we need from the QUERY
QueryInfo info = GetQueryInfo();
In Part 3 we walked through GetQueryInfo() so lets just move on:
//Make sure that we are doing a new T(){Column1=Value1,Column2=Value2} in the Updator expression
MemberInitExpression expr = updator.Body as MemberInitExpression;
if (expr == null) throw new InvalidOperationException("Attempt to use an unsupported expression: " + updator.ToString());
This bit of code, basically does what it says, it makes sure that the update expression is a MemberInitExpression, i.e. a constructor call, with C# 3.0 style property initialization.
Then we build a list of set statements (one for each Property initialized in the constructor).
To do that we loop through the Bindings, the name of the binding, is the Property name. We then use our fieldMap to lookup the Column name corresponding to that Property. Then we just need a way of converting the MemberBinding to a T-SQL SET statement and a SqlParameter.
List<string> setStatements = new List<string>();
SqlCommand command = new SqlCommand();
foreach (MemberBinding binding in expr.Bindings)
{
string name = binding.Member.Name;
Utilities.Assert(info.C_S_fieldMap.Keys.Contains(name), () => new InvalidOperationException("Attempt to update a Property that is not part of the primary Extent: " + name));
Utilities.Assert(binding.BindingType == MemberBindingType.Assignment, () => new InvalidOperationException("You can only use assignments: " + binding.Member.ToString()));
MemberAssignment assignment = binding as MemberAssignment;
if (assignment.Expression.NodeType == ExpressionType.Constant)
{
ConstantExpression constant = assignment.Expression as ConstantExpression;
command.Parameters.AddWithValue("@" + name, constant.Value);
}
else if (assignment.Expression.NodeType == ExpressionType.Convert)
{
command.Parameters.AddWithValue("@" + name, Utilities.Invoke(assignment.Expression));
}
else
{
throw new NotImplementedException("Currently only constant values are supported: " + assignment.Expression.ToString());
}
setStatements.Add(string.Format("{0}=@{0}", name));
}
As you can see, here I only handle a couple of types of assignment expressions (Constant and Convert expressions), but you can easily add support for more yourself.
Now are are almost done.
We just need to borrow the StoreConnection for our SqlCommand, and assembly the update statement.
command.Connection = Context.StoreConnection;
//Check we are actually doing something:
Utilities.Assert(command.Parameters.Count != 0, () => new InvalidOperationException("You have to update something"));
//Build the update
command.CommandText = string.Format(
"UPDATE {0} SET {1} WHERE {2} IN ({3})",
info.TableName,
string.Join(",",
setStatements.ToArray()),
Context.Keys[0],
info.RestrictingSQL
);
return command;
}
Tada...
And it only took 4 rambling posts and 10 weeks to describe it!
Comments
- Anonymous
February 11, 2008
PingBack from http://msdnrss.thecoderblogs.com/2008/02/11/rolling-your-own-sql-update-on-top-of-the-entity-framework-part-4/ - Anonymous
February 12, 2008
Glad to see the saga drawing to a close!--rjPingback from http://oakleafblog.blogspot.com/2008/02/linq-and-entity-framework-posts-for_11.html - Anonymous
February 21, 2008
There are often times when you want to do an update in SQL without bringing the data into memory first - Anonymous
December 16, 2008
Hi Alex,This looks all good, a lot of the methods/classes are not shown anywhere here, could you provide a download for the complete code?For example, the Utilities class, EnsureOpenConnection etc.Thanks,Jon - Anonymous
July 28, 2009
The comment has been removed