Применить удаляет во время пользовательских семян DbSet <T> - PullRequest
0 голосов
/ 10 сентября 2018

Я пытаюсь расширить мою пользовательскую подпрограмму (я знаю, что EF Core 2.1 изначально поддерживает заполнение, но у меня есть блокировщик в преобразовании) для применения удалений.Если запись существует в базе данных, но больше не существует в начальных данных, я хочу удалить ее.Вместо того чтобы писать собственную процедуру удаления для каждого DbSet, я пытаюсь реализовать ее с помощью обобщений (и, возможно, отражения при необходимости).

Моя первая попытка:

private static void Delete<TEntity>(DbContext dbContext, IEnumerable<TEntity> seedRows) where TEntity : class, IBaseEntity
{
    var toRemove = dbContext.Set<TEntity>().Except(seedRows);
    dbContext.RemoveRange(toRemove);
    dbContext.SaveChanges();
}

Однако, посколькуTEntity содержит некоторые свойства, которые являются нулевыми в начальных данных (например, временные метки, сгенерированные при добавлении), я не могу сравнить целые сущности в вызове Except() (в любом случае со средством сравнения равенства по умолчанию).Я действительно беспокоюсь только о сравнении первичного ключа.

Моя работа над решением этой проблемы приведена ниже.TEntity может иметь первичный ключ простого столбца Id или это может быть сопоставление «многие ко многим» со сложным первичным ключом, равным двум <EntityName>Id с.IBaseEntity в настоящее время не имеет никакой информации Id / первичного ключа, поскольку она реализуется как базовыми объектами, так и объектами "многие ко многим / соединениям".

private static void Delete<TEntity>(DbContext dbContext, IEnumerable<TEntity> seedRows) where TEntity : class, IBaseEntity
{
    var idProperties = typeof(TEntity).GetProperties().Where(p => p.Name.Contains("Id"));
    var toRemove = dbContext.Set<TEntity>().Select(s => idProperties).Except(seedRows.Select(s => idProperties));
    dbContext.RemoveRange(toRemove);
    dbContext.SaveChanges();
}

полный источник /context

Однако два экземпляра .Select(s => idProperties), очевидно, не работают.Есть ли способ выбрать свойства Id (или, альтернативно, первичный ключ) для DbSet<T> для использования в компараторе Except()?Я также открыт для совершенно другого подхода, так как я чувствую, что я в сорняках.

1 Ответ

0 голосов
/ 10 сентября 2018

Метаданные EF Core предоставляют всю необходимую информацию.

Вместо отражения вы можете использовать класс Expression для динамического построения таких критериев, как этот (псевдокод):

(seedRows1.Key1 == e.Key1 && seedRows1.Key2 == e.Key2 ... && seeedRows1.KeyM == e.KeyM)
||
(seedRows2.Key1 == e.Key1 && seedRows2.Key2 == e.Key2 ... && seeedRows2.KeyM == e.KeyM)
...
||
(seedRowsN.Key1 == e.Key1 && seedRowsN.Key2 == e.Key2 ... && seeedRowsN.KeyM == e.KeyM);

, который вернул бы соответствующие элементы из БД. Чтобы получить несоответствующие элементы, критерии можно просто инвертировать и использовать в качестве предиката для удаления. Обратите внимание, что для одного ПК это будет соответствовать NOT IN (...) критериям SQL.

Приведение в действие:

private static void Delete<TEntity>(DbContext dbContext, IEnumerable<TEntity> seedRows)
    where TEntity : class//, IBaseEntity
{
    var entityType = dbContext.Model.FindEntityType(typeof(TEntity));
    var entityPK = entityType.FindPrimaryKey();
    var dbEntity = Expression.Parameter(entityType.ClrType, "e");
    Expression matchAny = null;
    foreach (var entity in seedRows)
    {
        var match = entityPK.Properties
            .Select(p => Expression.Equal(
                Expression.Property(dbEntity, p.PropertyInfo),
                Expression.Property(Expression.Constant(entity), p.PropertyInfo)))
            .Aggregate(Expression.AndAlso);
        matchAny = matchAny != null ? Expression.OrElse(matchAny, match) : match;
    }
    var dbQuery = dbContext.Set<TEntity>().AsQueryable();
    if (matchAny != null)
    {
        var predicate = Expression.Lambda<Func<TEntity, bool>>(Expression.Not(matchAny), dbEntity);
        dbQuery = dbQuery.Where(predicate);
    }
    var dbEntities = dbQuery.ToList();
    if (dbEntities.Count == 0) return;
    dbContext.RemoveRange(dbEntities);
    dbContext.SaveChanges();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...