【问题标题】:How to query the DbSets of all types that implement an interface?如何查询实现接口的所有类型的 DbSet?
【发布时间】:2018-07-29 13:33:01
【问题描述】:

我的很多数据模型都使用这个接口:

public interface IHasPrimaryImageProperty
{
    PrimaryImageDataModel PrimaryImage { get; set; }
    int? PrimaryImageId { get; set; }
}

PrimaryImageDataModel 是:

public class PrimaryImageDataModel
{
    public int Id { get; set; }
    public string ImageFile { get; set; }
    public int TotalItemsUsingImage { get; set; }
}

我想通过对实现IHasPrimaryImageProperty 的所有数据模型执行计数来填充PrimaryImageDataModel.TotalItemsUsingImage

到目前为止,我已经设法获得了实现IHasPrimaryImageProperty 的类型列表。

但我无法获得每种类型的总数。

请参阅下面的示例,了解我想要实现的目标。

public static PrimaryImageDataModel GetImageUsageTotals(PrimaryImageDataModel image)
{
    var typesUsingImage = GetTypesWithPrimaryImageProperty();
    int totalUsingImage = 0;
    foreach (Type typeUsingImage in typesUsingImage)
    {
        // I WOULD LIKE TO DO SOMETHING LIKE THIS
        totalForType = db.Set<typeUsingImage>()
            .Where(x => x.PrimaryImageId == image.Id)
            .Count()

        totalUsingImage += totalForType;
    }
    image.TotalItemsUsingImage = totalUsingImage;
    return image;
}


public static IEnumerable<Type> GetTypesWithPrimaryImageProperty()
{
    var currentAssembly = Assembly.GetExecutingAssembly();
    foreach (Type type in currentAssembly.GetTypes())
    {
        if (type.GetInterfaces().Contains(typeof(IHasPrimaryImageProperty)))
        {
            yield return type;
        }
    }
}

【问题讨论】:

  • EF6 还是 EF Core?
  • 我不常这么说,但在这种情况下,我真的会转向数据库视图。
  • @IvanStoev 对不起,我妈突然来了,不得不暂时弃船!它是 EF6,但我看到你的答案涵盖了两者。谢谢。

标签: entity-framework reflection interface dbset


【解决方案1】:

我看到的最简单的方法(在 EF6 和 EF Core 中都有效)是创建一个泛型方法并通过反射调用它。

例如:

static int CountUsage<T>(DbContext db, PrimaryImageDataModel image)
    where T : class, IHasPrimaryImageProperty
{
    return db.Set<T>()
        .Where(x => x.PrimaryImageId == image.Id)
        .Count();
}

static readonly MethodInfo CountUsageMethodInfo = typeof(YourClass)
   .GetMethod("CountUsage", BindingFlags.NonPublic | BindingFlags.Static);

public static PrimaryImageDataModel GetImageUsageTotals(PrimaryImageDataModel image)
{
    var args = new object[] { db, image };
    image.TotalItemsUsingImage = GetTypesWithPrimaryImageProperty()
        .Sum(type => (int)CountUsageMethodInfo.MakeGenericMethod(type).Invoke(null, args));
    return image;
}

【讨论】:

  • 我对接受另一个答案感到非常难过,因为您的代码完全符合我的要求,并且正是我预期最终会得到的那种东西。 (尽管我不确定如何写)。但大卫的技术是我最终使用的技术,所以接受这一点似乎是正确的。
  • 根本不是问题:) EF 通常在接口和更改 IQueryable 的元素类型方面存在问题,但 David 的技术适用于像 Count 这样的单个聚合查询,因为它没有'不涉及反射(至少对于 EF6),这很好。干杯。
【解决方案2】:

IQueryable 是协变的。请参阅Variance in Generic Interfaces (C#) 这允许将IQueryable&lt;SomeEntity&gt; 强制转换为IQueryable&lt;InterfaceType&gt;,用于该实体类型实现的接口。

所以如果你把这个方法放在你的 EF6 DbContext 类型上:

public IQueryable<T> GetQuery<T>(Type EntityType)
{    
    return (IQueryable<T>)this.Set(EntityType);
}

或者像 EF Core 这样的:

public IQueryable<T> GetQuery<T>(Type EntityType)
{

    var pq = from p in this.GetType().GetProperties()
             where p.PropertyType.IsGenericType
                && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)
                && p.PropertyType.GenericTypeArguments[0] ==  EntityType
             select p;
    var prop = pq.Single();

    return (IQueryable<T>)prop.GetValue(this);

}

那你就可以写了

foreach (Type typeUsingImage in typesUsingImage)
{
    // I WOULD LIKE TO DO SOMETHING LIKE THIS
    totalForType = db.GetQuery<IHasPrimaryImageProperty>(typeUsingImage)
        .Where(x => x.PrimaryImageId == image.Id)
        .Count()

    totalUsingImage += totalForType;
}

【讨论】:

  • 6 个月后,同时这个答案对我非常有用。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-05
  • 1970-01-01
  • 2010-09-06
  • 2015-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多