如何查询实现接口的所有类型的DbSet?
How to query the DbSets of all types that implement an interface?
我的很多数据模型都使用这个接口:
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)是创建一个泛型方法并通过反射调用它。
例如:
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;
}
IQueryable 是协变的。请参阅 Variance in Generic Interfaces (C#) 对于由该实体类型实现的接口,这允许将 IQueryable<SomeEntity>
强制转换为 IQueryable<InterfaceType>
。
因此,如果您将此方法放在 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;
}
我的很多数据模型都使用这个接口:
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)是创建一个泛型方法并通过反射调用它。
例如:
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;
}
IQueryable 是协变的。请参阅 Variance in Generic Interfaces (C#) 对于由该实体类型实现的接口,这允许将 IQueryable<SomeEntity>
强制转换为 IQueryable<InterfaceType>
。
因此,如果您将此方法放在 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;
}