我已经为Linq To Sql (L2S)创建了一个更新操作,其工作原理如下:
var rowsaffected = db.Tables.Where(...).Join...Group..
.Update(x => new Table { Column = x.Something });
问题是L2S不允许您在查询中创建实体(上面的行在运行时导致异常)。
为了解决这个问题,我创建了一个像这样的非实体类:
class TableUpdate : Table { }
并在Update
中使用它.Update(x => new TableUpdate { Column = x.Something });
可以正常工作
现在的问题是,如果它是一个好主意,在运行时动态地创建TableUpdate
类,只是在将其发送到L2S sql生成器之前将其替换为表达式树?
类永远不会被实例化,所以不需要代码,只需要元数据。
我假设性能不是问题(与实际的数据库更新相比),但是还有其他事情要考虑吗?
- 可以有名称冲突,如果我创建相同的类几次?
- 我可以缓存元数据吗?
是这样解决的:
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);
}
使用RunSharp可以很容易地创建一个从现有类扩展的新类。
您可以创建一个类来创建这些包装类,并跟踪它已经构造了哪些类型的类。可能是Dictionary<Type, Type>
,将原始类型链接到动态创建的包装类型。
该类中的静态函数将简单地初始化该包装类型。
public static object ExtendedClassInstance<T>()
{
Type type = typeof( T );
if ( !_wrappedTypes.Contains( type ) )
{
// Use RunSharp to create the type.
}
return Activator.CreateInstance( _wrappedTypes[ type ] );
}
我使用的一种技术是用T4模板生成类似的类。链接到奥列格的博客有一些有用的教程。
从问题描述来看,似乎不需要运行时生成,只需要编译时生成。如果您确实需要运行时,T4将不起作用。