Генерация классов во время выполнения - PullRequest
2 голосов
/ 07 декабря 2011

Я создал операцию обновления для 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 ]

1 голос
/ 07 декабря 2011

Используя 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 ] );
}
0 голосов
/ 07 декабря 2011

Техника, которую я использовал, - генерировать похожие классы с помощью шаблона T4 .Ссылка на блог Олега содержит несколько полезных руководств.

Из описания проблемы не похоже, что вам нужно генерировать время выполнения, просто время компиляции.Если вам действительно нужно время выполнения, T4 не поможет.

...