I have created an Update operation for Linq To Sql (L2S) which works like this:
var rowsaffected = db.Tables.Where(...).Join...Group..
.Update(x => new Table { Column = x.Something });
The problem is that L2S does not allow you to create entities within the query (the line above causes an exception at runtime).
To get around that I create a non-entity class like this:
class TableUpdate : Table { }
and use it in the Update
.Update(x => new TableUpdate { Column = x.Something });
which works fine.
The question is now if it is a good idea to create the TableUpdate class dynamically at runtime and just replace it in the expression tree before sending it to the L2S sql generator?
The class will never be instantiated so there is no code need, just metadata.
I assume performance is not problem (compared to the actual database update) but are there other thins to think about?
- Can there be name collisions if I create the same class several times?
- Can I cache the metadata?
Update
Solved it like this:
private static readonly Dictionary<Type, Type> _noEntityClasses = new Dictionary<Type, Type>(1);
private static ModuleBuilder _module;
private static Type CreateNoEntityClass(Type entityClass) {
lock (_noEntityClasses) {
Type noentityClass;
if (!_noEntityClasses.TryGetValue(entityClass, out noentityClass)) {
if (_module == null) {
var assemblyName = new AssemblyName("LinqToSqlGenerated");
var assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
_module = assemblyBuilder.DefineDynamicModule("NonEntity");
}
var typeBuilder = _module.DefineType(entityClass.Name + "NoEntity", TypeAttributes.Class | TypeAttributes.NotPublic, entityClass);
noentityClass = typeBuilder.CreateType();
_noEntityClasses.Add(entityClass, noentityClass);
}
return noentityClass;
}
}
private static IQueryable<T> RemoveEntity<T>(IQueryable<T> source, DataContext context) {
var mapping = context.Mapping.MappingSource.GetModel(context.GetType());
var noEntityExpression = source.Expression.Visit<MemberInitExpression>(expression => {
if (!mapping.GetMetaType(expression.Type).IsEntity)
return expression;
return Expression.MemberInit(Expression.New(CreateNoEntityClass(expression.Type)), expression.Bindings);
});
return source.Provider.CreateQuery<T>(noEntityExpression);
}
Using RunSharp you can easily create a new class which extends from an existing one.
You could create a class which creates these wrapped classes and keeps track for which types it already constructed the classes. Probably a
Dictionary<Type, Type>, linking the original type to the dynamically created wrapped type.A static function in this class would simply initialize this wrapped type.