I have been using Dapper.NET for a while now. I was just wondering if it's possible to get Dapper to trim strings as it assigns them to the properties of the object.
I currently use LTRIM(RTRIM(fieldname)) in the SQL, and / or value.Trim() in the property setter.
I am however working with a legacy database that uses chars instead of varchar, and I was wondering if there was a way to reduce my time of having to trim everything.
I had a go myself by editing the source code of dapper but ended up breaking other mappings etc so gave in.
Just wondered if anyone had any suggestions that could reduce this overhead. (I may be missing something very simple!)
I am working with C# 3.5 by the way.
I didn't like the idea of modifying Dapper directly. I decided to solve the problem by creating an extension method to wrap Dapper's and just reflect over the result and trim all string properties.
public static class DapperExtensions {
public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null) {
var dapperResult = SqlMapper.Query<T>(cnn, sql, param, transaction, buffered, commandTimeout, commandType);
var result = TrimStrings(dapperResult.ToList());
return result;
}
static IEnumerable<T> TrimStrings<T>(IList<T> objects) {
//todo: create an Attribute that can designate that a property shouldn't be trimmed if we need it
var publicInstanceStringProperties = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.PropertyType == typeof (string) && x.CanRead && x.CanWrite);
foreach (var prop in publicInstanceStringProperties) {
foreach (var obj in objects) {
var value = (string) prop.GetValue(obj);
var trimmedValue = value.SafeTrim();
prop.SetValue(obj, trimmedValue);
}
}
return objects;
}
static string SafeTrim(this string source) {
if (source == null) {
return null;
}
return source.Trim();
}
}
Crucial to my solution (since I wanted to use the same name as Dapper) is how extension method resolution works, which you can read about here.
I am assuming that you want a more natural way to imply the LTRIM(RTRIM()) function through your POCOs such that you don't need to manually type in LTRIM(RTRIM()) every time you want to return that field, giving you the SQL performance without the manual repeated labour.
I have two options for you:
On the Dapper-Dot-Net side:
You could add some code right at the query function stage, performing a replace algorithm on the raw SQL query.
If you have multiple tables in the query, I would consider this out of scope
Otherwise I believe this can be done quite easily without disrupting the rest of the dapper engine.
Process:
First perform an ignorant IndexOf on your table name, ignoring case so you can do a quick pass-through if it's not.
If the table name was found, then perform some more reliable analysis of the query, making sure the table name you found was in fact a table-name in the query. Here you can also ensure there is only one table and no joins in the query.
If there is a * on the select, this can be expanded to the field names based on your poco definition
So now you simply have fields in your select line-up
For all fields, in the select line-up, find the names in your poco definition, where there is a [Trim] attribute, replace the field name with LTRIM(RTRIM({FieldName})) as [{FieldName}]
On the SQL Side:
If you have authorization to create views on the server, then you can write a stored procedure to create/alter(update) a set of views which expose the varchar interface for char fields.
Batch Update - run this everytime there is a schema change:
1. Loop through all user tables
2. Create or Update the corresponding View
UpdateTableView(tableName):
1. Exit if there are no char fields
2. Automate creation of view with cast(LTRIM(RTRIM(..)) as varchar(CHAR_FIELD_SIZE)
Matt,
This can be done quite easy. I made this change to clean the SQL char spaces. I have tested it and my code shows no signs if slowness due to the change.
First make sure that you back up your exiting source code so it will be easier to revert back should you need to.
Next Create the following method:
public static string ReadString(object value) /*** CUSTOM CODE ***/
{
if (value == null || value is DBNull) return null;
return value.ToString().Trim();
}
I always mark all my code changes with /* CUSTOM CODE */ so that I can later find my changes easily
Next find the following method:
public static void SetTypeMap(Type type, ITypeMap map)
now in that method locate the following lines:
if (memberType == typeof (char) || memberType == typeof (char?))
{
il.EmitCall(OpCodes.Call, typeof (SqlMapper).GetMethod(
memberType == typeof (char) ? "ReadChar" : "ReadNullableChar",
BindingFlags.Static | BindingFlags.Public), null);
// stack is now [target][target][typed-value]
}
else
and modify as follow:
if (memberType == typeof (char) || memberType == typeof (char?))
{
il.EmitCall(OpCodes.Call, typeof (SqlMapper).GetMethod(
memberType == typeof (char) ? "ReadChar" : "ReadNullableChar",
BindingFlags.Static | BindingFlags.Public), null);
// stack is now [target][target][typed-value]
}
else if (memberType == typeof(string)) /*** CUSTOM CODE START ***/
{
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ReadString", BindingFlags.Static | BindingFlags.Public), null);
// stack is now [target][target][typed-value]
} /*** CUSTOM CODE END ***/
else
Compile and you are ready to go
Related
I am trying to improve performance in the code below and kinda know how but not sure which is the best approach.
The first hit will take longer but subsequent hits should be quicker. Now, I could cache T (where T is a class) and then check the cache to see if "T" exists, if so - go ahead and get its related information (NamedArguments) and go through each of the NamedArguments and finally if the criteria matches, go ahead and set the value of the current property.
I just want to make it more efficient and performant. Any ideas?
var myProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(prop => Attribute.IsDefined(prop, typeof(MyCustomAttribute)) && prop.CanWrite && prop.GetSetMethod() != null);
foreach (var currentProperty in myProps)
{
foreach (var currentAttributeForProperty in currentProperty.GetCustomAttributesData())
{
foreach (var currentNamedArgument in currentAttributeForProperty.NamedArguments)
{
if (string.Equals(currentNamedArgument.MemberInfo.Name, "PropName", StringComparison.OrdinalIgnoreCase))
{
currentAttribParamValue = currentNamedArgument.TypedValue.Value == null ? null : currentNamedArgument.TypedValue.Value.ToString();
// read the reader for the currentAttribute value
if (reader.DoesFieldExist(currentAttribParamValue))
{
var dbRecordValue = reader[currentAttribParamValue] == DBNull.Value ? null : reader[currentAttribParamValue];
// set it in the property
currentProperty.SetValue(val, dbRecordValue, null);
}
break;
}
}
}
}
DynamicMethods or ExpressionTrees will be much* faster than reflection. You could build a cache of all property getter/setters for a type, and then cache that information in a Dictionary (or ConcurrentDictionary) with type as the key.
Expression Tree Basics
Dynamic Methods
Flow
Discover type information (e.g. on app startup).
Compile dynamic methods for each property (do all properties at once).
Store those methods in a metadata class (example follows).
Cache the metadata somewhere (even a static field is fine, as long as access is synchronized). Use the type as the key.
Get the metadata for the type when needed.
Find the appropriate getter/setter.
Invoke, passing the instance on which you wish to act.
// Metadata for a type
public sealed class TypeMetadata<T> {
// The compiled getters for the type; the property name is the key
public Dictionary<string, Func<T, object>> Getters {
get;
set;
}
// The compiled setters for the type; the property name is the key
public Dictionary<string, Action<T, object>> Setters {
get;
set;
}
}
// rough invocation flow
var type = typeof( T);
var metadata = _cache[type];
var propertyName = "MyProperty";
var setter = metadata[propertyName];
var instance = new T();
var value = 12345;
setter( instance, value );
Example Setter
Excerpted from Dynamic Method Implementation (good article on the subject).
I can't vouch that this exact code works, but I've written very similar code myself. If you aren't comfortable with IL, definitely consider an expression tree instead.
public static LateBoundPropertySet CreateSet(PropertyInfo property)
{
var method = new DynamicMethod("Set" + property.Name, null, new[] { typeof(object), typeof(object) }, true);
var gen = method.GetILGenerator();
var sourceType = property.DeclaringType;
var setter = property.GetSetMethod(true);
gen.Emit(OpCodes.Ldarg_0); // Load input to stack
gen.Emit(OpCodes.Castclass, sourceType); // Cast to source type
gen.Emit(OpCodes.Ldarg_1); // Load value to stack
gen.Emit(OpCodes.Unbox_Any, property.PropertyType); // Unbox the value to its proper value type
gen.Emit(OpCodes.Callvirt, setter); // Call the setter method
gen.Emit(OpCodes.Ret);
var result = (LateBoundPropertySet)method.CreateDelegate(typeof(LateBoundPropertySet));
return result;
}
*25-100x faster in my experience
Reflection is notoriously slow in loops, so some kind of caching would probably help. But to decide what to cache, you should measure. As the famous saying goes: "premature optimization is the root of all evil"; you should make sure that you really need to optimize and what exactly to optimize.
For a more concrete advice, attributes are attached at compile time, so you could cache a type and a list of its propertyInfos for example.
I also had similar problem once - reflection is extreemely slow.
I used caching, like you are planning and performance grow more than 10 times. It was never again be a bottleneck in performance.
I've created similar logic before where I've cached an 'execution plan' for each type encountered. It was definitely faster for subsequent runs but you would have to profile your scenario to see whether it's worth the extra code complexity and memory usage.
I am trying to improve performance in the code below and kinda know how but not sure which is the best approach.
The first hit will take longer but subsequent hits should be quicker. Now, I could cache T (where T is a class) and then check the cache to see if "T" exists, if so - go ahead and get its related information (NamedArguments) and go through each of the NamedArguments and finally if the criteria matches, go ahead and set the value of the current property.
I just want to make it more efficient and performant. Any ideas?
var myProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static).Where(prop => Attribute.IsDefined(prop, typeof(MyCustomAttribute)) && prop.CanWrite && prop.GetSetMethod() != null);
foreach (var currentProperty in myProps)
{
foreach (var currentAttributeForProperty in currentProperty.GetCustomAttributesData())
{
foreach (var currentNamedArgument in currentAttributeForProperty.NamedArguments)
{
if (string.Equals(currentNamedArgument.MemberInfo.Name, "PropName", StringComparison.OrdinalIgnoreCase))
{
currentAttribParamValue = currentNamedArgument.TypedValue.Value == null ? null : currentNamedArgument.TypedValue.Value.ToString();
// read the reader for the currentAttribute value
if (reader.DoesFieldExist(currentAttribParamValue))
{
var dbRecordValue = reader[currentAttribParamValue] == DBNull.Value ? null : reader[currentAttribParamValue];
// set it in the property
currentProperty.SetValue(val, dbRecordValue, null);
}
break;
}
}
}
}
DynamicMethods or ExpressionTrees will be much* faster than reflection. You could build a cache of all property getter/setters for a type, and then cache that information in a Dictionary (or ConcurrentDictionary) with type as the key.
Expression Tree Basics
Dynamic Methods
Flow
Discover type information (e.g. on app startup).
Compile dynamic methods for each property (do all properties at once).
Store those methods in a metadata class (example follows).
Cache the metadata somewhere (even a static field is fine, as long as access is synchronized). Use the type as the key.
Get the metadata for the type when needed.
Find the appropriate getter/setter.
Invoke, passing the instance on which you wish to act.
// Metadata for a type
public sealed class TypeMetadata<T> {
// The compiled getters for the type; the property name is the key
public Dictionary<string, Func<T, object>> Getters {
get;
set;
}
// The compiled setters for the type; the property name is the key
public Dictionary<string, Action<T, object>> Setters {
get;
set;
}
}
// rough invocation flow
var type = typeof( T);
var metadata = _cache[type];
var propertyName = "MyProperty";
var setter = metadata[propertyName];
var instance = new T();
var value = 12345;
setter( instance, value );
Example Setter
Excerpted from Dynamic Method Implementation (good article on the subject).
I can't vouch that this exact code works, but I've written very similar code myself. If you aren't comfortable with IL, definitely consider an expression tree instead.
public static LateBoundPropertySet CreateSet(PropertyInfo property)
{
var method = new DynamicMethod("Set" + property.Name, null, new[] { typeof(object), typeof(object) }, true);
var gen = method.GetILGenerator();
var sourceType = property.DeclaringType;
var setter = property.GetSetMethod(true);
gen.Emit(OpCodes.Ldarg_0); // Load input to stack
gen.Emit(OpCodes.Castclass, sourceType); // Cast to source type
gen.Emit(OpCodes.Ldarg_1); // Load value to stack
gen.Emit(OpCodes.Unbox_Any, property.PropertyType); // Unbox the value to its proper value type
gen.Emit(OpCodes.Callvirt, setter); // Call the setter method
gen.Emit(OpCodes.Ret);
var result = (LateBoundPropertySet)method.CreateDelegate(typeof(LateBoundPropertySet));
return result;
}
*25-100x faster in my experience
Reflection is notoriously slow in loops, so some kind of caching would probably help. But to decide what to cache, you should measure. As the famous saying goes: "premature optimization is the root of all evil"; you should make sure that you really need to optimize and what exactly to optimize.
For a more concrete advice, attributes are attached at compile time, so you could cache a type and a list of its propertyInfos for example.
I also had similar problem once - reflection is extreemely slow.
I used caching, like you are planning and performance grow more than 10 times. It was never again be a bottleneck in performance.
I've created similar logic before where I've cached an 'execution plan' for each type encountered. It was definitely faster for subsequent runs but you would have to profile your scenario to see whether it's worth the extra code complexity and memory usage.
I have a series of methods to write, but I think there is some commonality (well, I know there is). The method I'd like to see will take two things, an object and... well, I'm not so sure about the second one, but probably a string.
The object should be generic, although it can only be from a set list (in this case, that commonality seems to be that they inherit from both INotifyPropertyChanging and INotifyPropertyChanged interfaces).
The string should be the name of a property within the generic object. It should be checked to see if that property exists within that object before being put into use (it would be used as a way to compare the objects by that given property).
So I guess the process would be... generic object gets passed into method (along with property name string). A check to see if the object contains the property. If it does, continue and somehow have access to 'object.PropertyName' where 'PropertyName' was the supplied string.
I don't know if it's easy or possible or even wise, but I know that it would save me some time.
Thanks in advance for any advice you might be able to offer with this.
Edit: Thanks for the all the replies so far guys. Let me clarify some things:
Clarification of 'access'
When I said, "... and somehow have access to 'object.PropertyName'", what I meant was that the method should be able to use that property name as if it were just a property of that object. So, let's say the string passed in was "ClientName", there would be the ability to read (possibly write, although at the moment I don't think so as it's just a check) object.ClientName, if it was determined that existed.
What I'm trying to do
I have a WCF service which accesses an SQL database using Linq. The objects I spoke of are entities, generated from the program SQLMetal.exe, so my objects are things like 'Client', 'User' and this sort of thing. I wrote a method which took a List of entities. This method added only those entities which did not exist within the collection (some could have been duplicates). It figured out which ones were duplicates by checking a property within the entity (which corresponds to data in a column of the database). It's that property which I figured might be variable.
It sounds like you don't really want to check if it's a certain type, and if that is so then you don't have to and its actually easier not to check the type. This shows how to check if the property exists and if it is readable and writeable and shows how to use it after it's found:
private void Form1_Load(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
PropertyInfo info = GetProperty(sb, "Capacity");
//To get the value of the property, call GetValue on the PropertyInfo with the object and null parameters:
info.GetValue(sb, null);
//To set the value of the property, call SetValue on the PropertyInfo with the object, value, and null parameters:
info.SetValue(sb, 20, null);
}
private PropertyInfo GetProperty(object obj, string property)
{
PropertyInfo info = obj.GetType().GetProperty(property);
if (info != null && info.CanRead && info.CanWrite)
return info;
return null;
}
I think only indexer properties can take parameters in C#. And I believe if you wrote properties in VB that take parameters and tried to reference that assembly in C# they would show up as methods instead of properties.
You could also write a function like this that would take 2 objects and a string for a property name and return the result of those properties matching:
private bool DoObjectsMatch(object obj1, object obj2, string propetyName)
{
PropertyInfo info1 = obj1.GetType().GetProperty(propertyName);
PropertyInfo info2 = obj2.GetType().GetProperty(propertyName);
if (info1 != null && info1.CanRead && info2 != null && info2.CanRead)
return info1.GetValue(obj1, null).ToString() == info2.GetValue(obj2, null).ToString();
return false;
}
Comparing the values of the properties might be tricky because it would compare them as objects and who knows how equality will be handled for them. But converting the values to strings should work for you in this case.
If you know the 2 objects are the same type then you can simplify it:
private bool DoObjectsMatch(object obj1, object obj2, string propetyName)
{
PropertyInfo info = obj1.GetType().GetProperty(propertyName);
if (info != null && info.CanRead)
return info.GetValue(obj1, null).ToString() == info.GetValue(obj2, null).ToString();
return false;
}
I think you're looking for something like:
public void SomeMethod<T>(T object, string propName)
where T : INotifyPropertyChanging, INotifyPropertyChanged
(
var type = typeof(T);
var property = type.GetProperty(propName);
if(property == null)
throw new ArgumentException("Property doesn't exist", "propName");
var value = property.GetValue(object, null);
)
Consider the following example LINQ to entity query
from history in entities.foreignuserhistory
select new { history.displayname, login=history.username, history.foreignuserid }
ToTraceString() return string looks like:
SELECT "Extent1"."foreignuserid" AS "foreignuserid",
"Extent1"."displayname" AS "displayname",
"Extent1"."username" AS "username"
FROM "integration"."foreignuserhistory" AS "Extent1"
The problem for me is that columns come in different order from query and do not take aliases like login in the example. Where does Entity Framework store mapping information for anonymous types?
Background: I'm going to develop insert with select operation using LINQ to entity for mass operations.
Update:
Insert with select is not that hard except for an unknown column to property mapping algorithm. One can get table and column names for destination ObjectSet using metadata, build INSERT INTO tableName (column_name1, …) sql statement string and then append some ObjectQuery.ToTraceString SELECT statement. Then create a DbCommand with resulting text using ((EntityConnection)ObjectContext.Connection).StoreConnection and fill command’s parameters from ObjectQuery. So the problem is to find matching column order in inserted and selected records.
Here’s my solution all the way down of privates and internals. It travels with reflection into cached query plan which will exist after ToTraceString call or query execution to get what is called _columnMap. Column map contains ScalarColumnMap objects going in the order of anonymous object’s properties and pointing to the corresponding column position with ColumnPos property.
using System;
using System.Data.Objects;
using System.Reflection;
static class EFQueryUtils
{
public static int[] GetPropertyPositions(ObjectQuery query)
{
// get private ObjectQueryState ObjectQuery._state;
// of actual type internal class
// System.Data.Objects.ELinq.ELinqQueryState
object queryState = GetProperty(query, "QueryState");
AssertNonNullAndOfType(queryState, "System.Data.Objects.ELinq.ELinqQueryState");
// get protected ObjectQueryExecutionPlan ObjectQueryState._cachedPlan;
// of actual type internal sealed class
// System.Data.Objects.Internal.ObjectQueryExecutionPlan
object plan = GetField(queryState, "_cachedPlan");
AssertNonNullAndOfType(plan, "System.Data.Objects.Internal.ObjectQueryExecutionPlan");
// get internal readonly DbCommandDefinition ObjectQueryExecutionPlan.CommandDefinition;
// of actual type internal sealed class
// System.Data.EntityClient.EntityCommandDefinition
object commandDefinition = GetField(plan, "CommandDefinition");
AssertNonNullAndOfType(commandDefinition, "System.Data.EntityClient.EntityCommandDefinition");
// get private readonly IColumnMapGenerator EntityCommandDefinition._columnMapGenerator;
// of actual type private sealed class
// System.Data.EntityClient.EntityCommandDefinition.ConstantColumnMapGenerator
object columnMapGenerator = GetField(commandDefinition, "_columnMapGenerator");
AssertNonNullAndOfType(columnMapGenerator, "System.Data.EntityClient.EntityCommandDefinition+ConstantColumnMapGenerator");
// get private readonly ColumnMap ConstantColumnMapGenerator._columnMap;
// of actual type internal class
// System.Data.Query.InternalTrees.SimpleCollectionColumnMap
object columnMap = GetField(columnMapGenerator, "_columnMap");
AssertNonNullAndOfType(columnMap, "System.Data.Query.InternalTrees.SimpleCollectionColumnMap");
// get internal ColumnMap CollectionColumnMap.Element;
// of actual type internal class
// System.Data.Query.InternalTrees.RecordColumnMap
object columnMapElement = GetProperty(columnMap, "Element");
AssertNonNullAndOfType(columnMapElement, "System.Data.Query.InternalTrees.RecordColumnMap");
// get internal ColumnMap[] StructuredColumnMap.Properties;
// array of internal abstract class
// System.Data.Query.InternalTrees.ColumnMap
Array columnMapProperties = GetProperty(columnMapElement, "Properties") as Array;
AssertNonNullAndOfType(columnMapProperties, "System.Data.Query.InternalTrees.ColumnMap[]");
int n = columnMapProperties.Length;
int[] propertyPositions = new int[n];
for (int i = 0; i < n; ++i)
{
// get value at index i in array
// of actual type internal class
// System.Data.Query.InternalTrees.ScalarColumnMap
object column = columnMapProperties.GetValue(i);
AssertNonNullAndOfType(column, "System.Data.Query.InternalTrees.ScalarColumnMap");
//string colName = (string)GetProp(column, "Name");
// can be used for more advanced bingings
// get internal int ScalarColumnMap.ColumnPos;
object columnPositionOfAProperty = GetProperty(column, "ColumnPos");
AssertNonNullAndOfType(columnPositionOfAProperty, "System.Int32");
propertyPositions[i] = (int)columnPositionOfAProperty;
}
return propertyPositions;
}
static object GetProperty(object obj, string propName)
{
PropertyInfo prop = obj.GetType().GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Instance);
if (prop == null) throw EFChangedException();
return prop.GetValue(obj, new object[0]);
}
static object GetField(object obj, string fieldName)
{
FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null) throw EFChangedException();
return field.GetValue(obj);
}
static void AssertNonNullAndOfType(object obj, string fullName)
{
if (obj == null) throw EFChangedException();
string typeFullName = obj.GetType().FullName;
if (typeFullName != fullName) throw EFChangedException();
}
static InvalidOperationException EFChangedException()
{
return new InvalidOperationException("Entity Framework internals has changed, please review and fix reflection code");
}
}
I think some assertions can be relaxed to check not the exact type but base type containing necessary property.
Is there a solution without reflection?
How the columns are aliased in the query shouldn't matter, and neither should their order. Entity Framework handles populating a new instance of your anonymous type with each result, and that's where you get the alias like login.
As a side note, I think Entity Framework may not work quite how you think. You can't do a select/insert in a single operation like you can using a normal SQL query. Entity Framework will execute your select, return back the results, use those results to create new instances of your entities (or in your case, an anonymous type), and you would then have to use each result to create a new instance of your target type, adding each one to your entity/object context, and finally call save changes on your entity/object context. This will cause an individual insert statement to be executed for each new entity that you've added.
If you want to do it all in a single operation without instantiating a new entity for every record, you'll need to either use a stored procedure that you map in your context, or else execute an in-line SQL query using ObjectContext.ExecuteStoreCommand
UPDATE: Based on your responses, what you're really getting into is closer to meta-programming that relies on your entity model more so than actually using entity framework. I don't know what version of EF you're using (EF 4.0? 4.1 w/ code first and DbContext?), but I've had a lot of success using the C# POCO template with EF 4.0 (the POCO template is a download from the online visual studio gallery). It uses a T4 template to generate POCO classes from the .edmx data model. In your T4 template, you could add methods to your context that would essentially call ExecuteStoreCommand, but the difference would be you can generate the query that gets executed based on your data model. That way any time your data model changes, your query would stay in sync with the changes.
Updated the reflection on this for EF 4.4 (5-RC)
full post at http://imaginarydevelopment.blogspot.com/2012/06/compose-complex-inserts-from-select.html
using this functionality/logic for doing a bulk insert from a select with some parameters provided
int Insert<T>(IQueryable query,IQueryable<T> targetSet)
{
var oQuery=(ObjectQuery)this.QueryProvider.CreateQuery(query.Expression);
var sql=oQuery.ToTraceString();
var propertyPositions = GetPropertyPositions(oQuery);
var targetSql=((ObjectQuery)targetSet).ToTraceString();
var queryParams=oQuery.Parameters.ToArray();
System.Diagnostics.Debug.Assert(targetSql.StartsWith("SELECT"));
var queryProperties=query.ElementType.GetProperties();
var selectParams=sql.Substring(0,sql.IndexOf("FROM "));
var selectAliases=Regex.Matches(selectParams,#"\sAS \[([a-zA-Z0-9_]+)\]").Cast<Match>().Select(m=>m.Groups[1].Value).ToArray();
var from=targetSql.Substring(targetSql.LastIndexOf("FROM [")+("FROM [".Length-1));
var fromAlias=from.Substring(from.LastIndexOf("AS ")+"AS ".Length);
var target=targetSql.Substring(0,targetSql.LastIndexOf("FROM ["));
target=target.Replace("SELECT","INSERT INTO "+from+" (")+")";
target=target.Replace(fromAlias+".",string.Empty);
target=Regex.Replace(target,#"\sAS \[[a-zA-z0-9]+\]",string.Empty);
var insertParams=target.Substring(target.IndexOf('('));
target = target.Substring(0, target.IndexOf('('));
var names=Regex.Matches(insertParams,#"\[([a-zA-Z0-9]+)\]");
var remaining=names.Cast<Match>().Select(m=>m.Groups[1].Value).Where(m=>queryProperties.Select(qp=>qp.Name).Contains(m)).ToArray(); //scrape out items that the anonymous select doesn't include a name/value for
//selectAliases[propertyPositions[10]]
//remaining[10]
var insertParamsOrdered = remaining.Select((s, i) => new { Position = propertyPositions[i], s })
.OrderBy(o => o.Position).Select(x => x.s).ToArray();
var insertParamsDelimited = insertParamsOrdered.Aggregate((s1, s2) => s1 + "," + s2);
var commandText = target + "(" + insertParamsDelimited + ")" + sql;
var result=this.ExecuteStoreCommand(commandText,queryParams.Select(qp=>new System.Data.SqlClient.SqlParameter{ ParameterName=qp.Name, Value=qp.Value}).ToArray());
return result;
}
Is there a way to force LINQ-to-SQL to treat a column as dirty? Globally would suffice....
Basically, I've got a problem with some audit code on a legacy system that I'm talking to with L2S, imagine:
var ctx = new SomeDataContext(); // disposed etc - keeping it simple for illustration
var cust = ctx.Customers.First(); // just for illustration
cust.SomeRandomProperty = 17; // whatever
cust.LastUpdated = DateTime.UtcNowl;
cust.UpdatedBy = currentUser;
ctx.SubmitChanges(); // uses auto-generated TSQL
This is fine, but if the same user updates it twice in a row, the UpdatedBy is a NOP, and the TSQL will be (roughly):
UPDATE [dbo].[Customers]
SET SomeRandomColumn = #p0 , LastUpdated = #p1 -- note no UpdatedBy
WHERE Id = #p2 AND Version = #p3
In my case, the problem is that there is currently a belt-and-braces audit trigger on all tables, which checks to see if the audit column has been updated, and if not assumes the developer is at fault (substituting SUSER_SNAME(), although it could just as readily throw an error).
What I'd really like to be able to do is say "always update this column, even if it isn't dirty" - is this possible?
Based on KristoferA's answer, I ended up with something like below; this is evil and brittle (reflection often is), but may have to suffice for now. The other side of the battle is to change the triggers to behave:
partial class MyDataContext // or a base-class
{
public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode)
{
this.MakeUpdatesDirty("UpdatedBy", "Updated_By");
base.SubmitChanges(failureMode);
}
}
public static class DataContextExtensions
{
public static void MakeUpdatesDirty(
this DataContext dataContext,
params string[] members)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
if (members == null) throw new ArgumentNullException("members");
if (members.Length == 0) return; // nothing to do
foreach (object instance in dataContext.GetChangeSet().Updates)
{
MakeDirty(dataContext, instance, members);
}
}
public static void MakeDirty(
this DataContext dataContext, object instance ,
params string[] members)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
if (instance == null) throw new ArgumentNullException("instance");
if (members == null) throw new ArgumentNullException("members");
if (members.Length == 0) return; // nothing to do
const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
object commonDataServices = typeof(DataContext)
.GetField("services", AllInstance)
.GetValue(dataContext);
object changeTracker = commonDataServices.GetType()
.GetProperty("ChangeTracker", AllInstance)
.GetValue(commonDataServices, null);
object trackedObject = changeTracker.GetType()
.GetMethod("GetTrackedObject", AllInstance)
.Invoke(changeTracker, new object[] { instance });
var memberCache = trackedObject.GetType()
.GetField("dirtyMemberCache", AllInstance)
.GetValue(trackedObject) as BitArray;
var entityType = instance.GetType();
var metaType = dataContext.Mapping.GetMetaType(entityType);
for(int i = 0 ; i < members.Length ; i++) {
var member = entityType.GetMember(members[i], AllInstance);
if(member != null && member.Length == 1) {
var metaMember = metaType.GetDataMember(member[0]);
if (metaMember != null)
{
memberCache.Set(metaMember.Ordinal, true);
}
}
}
}
}
Unfortunately, I think you will have to use a new DataContext
Details at: http://blog.benhall.me.uk/2008/01/custom-insert-logic-with-linq-to-sql.html
You can override the default update behavior. There are 2 ways of doing this
The easiest is to create a stored procedure (if you can't do that on your database, the second method should work) which takes the parameters of your customer object and updates the table:
Create the stored procedure that has a parameter for each property of Customers that needs to be updated.
Import that stored procedure into your Linq To SQL DBML file.
Now you can right click on your customers entity and select "Configure Behavior".
Select your Customers class under the Class dropdown and "Update" on the behavior drop down.
Select the "Customize" radio button and choose the stored procedure you just created.
Now you can map class's properties to the stored procedure.
Now when Linq to SQL tries to update your Customers table, it'll use your stored procedure instead. Just be careful because this will override the update behavior for Customers everywhere.
The second method is to use partial methods. I haven't actually tried this, so hopefully this might just give you some general direction to pursue. In a partial class for your data context, make a partial method for the update (It'll be Update_____ with whatever your class is in the blank. I'd suggest searching in your data context's designer file to make sure you get the right one)
public partial SomeDataContext
{
partial void UpdateCustomer(Customer instance)
{
// this is where you'd do the update, but I'm not sure exactly how it's suppose to work, though. :(
}
}
If you want to go down the [dirty] reflection route, you could try something along the lines of:
1) Override SubmitChanges
2) Go through the change set
3) Use reflection to get hold of the change tracker for each updated object (see What's the cleanest way to make a Linq object "dirty"? )
4) Make the column dirty (there's a dirtyMemberCache field in the StandardTrackedObject class)
The following works for me. Note though that I'm using the linq2sql provider from DevArt, but that may not matter:
MyDataContext dc = new MyDataContext();
Message msg = dc.Messages.Single(m => m.Id == 1);
Message attachingMsg = new Message();
attachingMsg.Id = msg.Id;
dc.Messages.Attach(attachingMsg);
attachingMsg.MessageSubject = msg.MessageSubject + " is now changed"; // changed
attachingMsg.MessageBody = msg.MessageBody; // not changed
dc.SubmitChanges();
This produces the following sql:
UPDATE messages SET messageSubject = :p1, messageBody = :p2 WHERE Id = :key1
So, messageBody is updated even though its value is not changed.
One other change necessary for this, is that for each property (column) of my entity Message, I have set UpdatedCheck = UpdateCheck.Never, except for its ID, which is the primary key.