在运行时生成类

时间:2011-12-07 11:06:50

标签: c# linq-to-sql reflection code-generation

我为Linq To Sql(L2S)创建了一个更新操作,其工作原理如下:

var rowsaffected = db.Tables.Where(...).Join...Group..
                            .Update(x => new Table { Column = x.Something });

问题是L2S不允许您在查询中创建实体(上面的行在运行时导致异常)。

为了解决这个问题,我创建了一个非实体类:

class TableUpdate : Table { }

并在更新

中使用它
.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);
}

2 个答案:

答案 0 :(得分:1)

使用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 ] );
}

答案 1 :(得分:0)

我使用的一种技术是使用T4 Template生成类似的类。 Oleg博客的链接有一些有用的教程。

从问题描述中,它听起来不像你需要运行时生成,只是编译时。如果你真的需要运行时,T4将无济于事。