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