在DbSet <t>

时间:2018-09-10 17:54:32

标签: c# linq generics reflection entity-framework-core

我正在尝试扩展自定义种子例程(我知道EF Core 2.1本机支持种子,但是在转换时有blocker)以应用删除。如果数据库中存在一条记录,但种子数据中不再存在一条记录,我想删除它。我没有为每个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();
}

full source/context

.Select(s => idProperties)的两个实例显然不起作用。有没有办法选择要在Id比较器中使用的DbSet<T>的{​​{1}}属性(或主键)?我也对完全不同的方法持开放态度,因为我感觉自己像杂草丛生。

1 个答案:

答案 0 :(得分:1)

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);

,它将从数据库返回匹配项。要获得不匹配的项目,可以将条件简单地反转并用作删除的谓词。请注意,对于单个PK,这将转换为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();
}