У меня есть процесс, который в основном перемещает данные из одной базы данных в другую, и для этого существует целый ряд обработчиков сообщений. Чтобы мой процесс перемещения данных был динамичным c Я хотел разработать обобщенный процесс c с использованием настраиваемого атрибута в моих обработчиках сообщений, что позволило бы мне автоматически управлять миграцией данных.
Каждый обработчик сообщений имеет пользовательский атрибут, например
[BulkImport(EntityModelType = typeof(TblProxyCompany))]
Реализация атрибута следующая: -
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class BulkImportAttribute : Attribute
{
public Type EntityModelType { get; set; }
public Type[] DependentEntityModelTypes { get; set; }
}
Мой процесс миграции данных считывает все обработчики сообщений с этим атрибут, который создает мой список задач. В своем коде я выполняю oop через каждую задачу и отправляю сообщение в очередь локального хранилища, и именно здесь начинаются мои проблемы.
Мне нужно прочитать группу строк из таблицы, определенной в моем атрибуте через тип модели, т.е. TblProxyCompany
, и запрос linq ниже делает это: -
var query = _oltpContext.Query(importItem.EntityModelType)
.Cast<IBulkImport>()
.Skip(skip)
.Take(BATCH_SIZE);
Первая проблема, с которой я столкнулся, заключалась в том, что мой метод расширения Query
возвращает IQueryable без типа модели, и из-за этого Я не мог использовать Skip and Take, поэтому приводил к интерфейсу IBulkImport
.
Итак, я создал интерфейс IBulkImport
и добавил его к каждому типу моей модели и просто преобразовал IQueryable
в IQueryable<IBulkImport>
, и теперь я могу использовать Skip and Take.
I затем обнаружил, что SQL возвращал мои данные в согласованном порядке, и, следовательно, мои пакеты были неправильными, поэтому теперь мне нужно упорядочить запрос по первичному ключу типа модели. К сожалению, хотя 99% моих таблиц имеют первичный ключ Id
, не все из них имеют, поэтому я не мог просто добавить столбец Id
в интерфейс IBulkImport
: /
Я могу получить мой первичный ключ достаточно просто: -
public virtual string GetPrimaryKey(Type entityModelType)
{
return _oltpContext.Model
.FindEntityType(entityModelType)
.FindPrimaryKey().Properties
.Select(x => x.Name).Single();
}
Я нашел множество примеров, касающихся создания методов расширения, позволяющих передавать порядок по столбцам в виде строки, но все они в какой-то момент полагаются на Тип generi c передается с указанием типа.
Я нашел следующий пример: -
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty,
bool desc)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<TEntity>(resultExpression);
}
, но, как вы видите, мне нужно снова передать тип TEntity. Я думаю, что приведение к IBulkImport
- это то место, где начинаются мои проблемы, но я не могу понять, как построить оператор LINQ, который выбирает, ЗАКАЗЫВАЕТ, ВЫБРАЕТ и ВЫБИРАЕТ, когда все, что мне нужно для начала, это тип модели, т.е. TblProxyCompany в качестве параметра против пользовательский атрибут BulkImport
.