【问题标题】:How to query all tables that implement an interface如何查询所有实现接口的表
【发布时间】:2014-10-15 14:41:10
【问题描述】:

我已经为我的一些实体类实现了一个接口:

public partial class Order : IReportable
{
   public string TableName { get { return "Order"; } }
}

public partial class Client: IReportable
{
 public string TableName { get { return "Client"; } }
}


public interface IReportable
{
    string TableName { get; }
}

然后我将它添加到 DbContext:

public virtual DbSet<IReportable> IReportable { get; set; }

当我尝试查询所有实现该接口的表时(如图所示):

var result = from reportabletable in db.IReportable
             where reportabletable.TableName == table_name
            select reportabletable

我得到以下异常:

未映射类型“Report.DataAccess.IReportable”。检查 没有使用 Ignore 方法明确排除该类型 或 NotMappedAttribute 数据注释。验证类型是 定义为一个类,不是原始的或泛型的,并且不继承 来自实体对象。

【问题讨论】:

  • 如果很多 ORM 都提供这种类型的查询,我会感到惊讶
  • @MarcGravell 你是说不可能吗?
  • 检查实体框架的继承策略。如果可能有 DbSet,我不知道,但您可以使用基本抽象类来解决这个问题,也许可以使用虚拟(或抽象)属性 TableName。顺便提一句。有一个属性 TableName - 在此处检查继承策略的链接 - entityframeworktutorial.net/code-first/…

标签: c# asp.net-mvc linq entity-framework


【解决方案1】:

我会选择这样的:

创建这个扩展方法

public static class DbContextExtensions
{
    public static IEnumerable<T> SetOf<T>(this DbContext dbContext) where T : class
    {
        return dbContext.GetType().Assembly.GetTypes()
            .Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface)
            .SelectMany(t => Enumerable.Cast<T>(dbContext.Set(t)));
    }
}

并像这样使用它:

using (var db = new dbEntities())
{
 var result = from reportabletable in db.SetOf<IReportable>()
         where reportabletable.TableName == table_name
        select reportabletable
}

【讨论】:

  • 我喜欢你在这里所做的,但你真的可以依赖在与上下文表相同的程序集中定义的接口吗?从DbContext 类型获取程序集不是更好吗?
  • 你是对的,没有理由这样做。我已经更新了我的答案,并进行了一些更正。
  • 请注意,如果您的模型位于与您的上下文不同的组件中,这也将不起作用。只需要获取 AppDomain 中的所有程序集或类似的东西。
【解决方案2】:

EF 不喜欢将接口直接映射到表。您可以通过使用通用存储库来解决此问题,如Here! 所述

然后使用存储库方法并提供您要查询的表的类型。比如:myRepo.GetAll&lt;myClient.GetType()&gt;();

获取继承该接口的类并为所有这些类运行查询:

var types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes().Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)));
foreach (var mytype in types)
 { // aggregate query results }

希望这会有所帮助!可能有更优雅的解决方案

【讨论】:

  • Type 和泛型不能很好地混合;可以做,但是做起来很麻烦
  • 反射可以通过将类型作为参数传递来调用泛型方法。
【解决方案3】:

首先,MarcGravell 的评论是关于钱的。由您决定要查询哪个表。 我个人浏览了实现接口或具有自定义属性的 poco 类型列表。但是,如果您只想通过 DBContext,这里有一些扩展可以让您访问“名称”。之后您仍需要一次访问该部分上下文。

同样,您可以通过泛型来做到这一点,但您可以直接按照您的建议进行操作。
您将需要迭代类型列表。 例如:

ReportRespository : BaseRespository where t : IReport

检查程序集的某些类型和属性 例如

     /// <summary>
    /// POCOs that have XYZ Attribute of Type  and NOT abstract and not complex
    /// </summary>
    /// <returns></returns>
    public static List<Type> GetBosDirDBPocoList() {
        var result = new List<Type>();
        // so get all the Class from teh assembly that public non abstract and not complex
        foreach (var t in Assembly.GetExecutingAssembly().GetTypes()
                                  .Where(t => t.BaseType != null
                                              && t.IsClass
                                              && t.IsPublic
                                              && !t.IsAbstract
                                              && !t.IsComplexType()
                                              && t.GetMyAttribute() != null)) {


                result.Add(t);
            }
        }
        return result;
    }

     public static GetMyAttribute(this Type T) {
        var myAttr= T.GetCustomAttributes(true)
                      .Where(attribute => attribute.GetType()
                      .Name == "XYZAttr").Cast<BosDir>().FirstOrDefault();

        return myAttr;
    }

扩展

 public static class DalExtensions {
    // DbSet Names is the plural property name in the context
    public static List<string> GetModelNames(this DbContext context) {
        var propList = context.GetType().GetProperties();
        return GetDbSetNames(propList);
    }

    // DbSet Names is the plural property name in the context
    public static List<string> GetDbSetTypeNames<T>() where T : DbContext {
        var propList = typeof (T).GetProperties();
        return GetDbSetNames(propList);
    }

    // DBSet Types is the Generic Types POCO name  used for a DBSet
    public static List<string> GetModelTypes(this DbContext context) {
        var propList = context.GetType().GetProperties();
        return GetDbSetTypes(propList);
    }

    // DBSet Types POCO types as IEnumerable List
    public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
        return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
                                                      .Name.StartsWith("DbSet"))
                         .Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
    }

    // DBSet Types is the Generic Types POCO name  used for a DBSet
    public static List<string> GetDbSetTypes<T>() where T : DbContext {
        var propList = typeof (T).GetProperties();
        return GetDbSetTypes(propList);
    }


    private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
        var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
                                     .Select(p => p.PropertyType.GenericTypeArguments[0].Name)
                                     .ToList();
        return modelTypeNames;
    }

    private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
        var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
                                 .Select(p => p.Name)
                                 .ToList();

        return modelNames;
    }
}

}

【讨论】:

    【解决方案4】:

    已接受的解决方案在 EF Core 中不起作用。 这是我的第一个工作草案

    public IEnumerable<T> SetOf<T>() where T : class
    {
        var firstType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
            .FirstOrDefault(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface);
        if (firstType == null) return new List<T>();
    
        var dbSetMethodInfo = typeof(DbContext).GetMethod("Set");
        var dbSet = dbSetMethodInfo.MakeGenericMethod(firstType);
    
        IQueryable<T> queryable = ((IQueryable)dbSet.Invoke(this, null)).Cast<T>();
    
        return queryable.ToList().Cast<T>();
    }
    

    那么你可以这样使用

    _dbContext.SetOf<ISomeInterface>();
    

    更多信息在这里Expose method DbContext.Set(Type entityType)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-07
      • 2014-04-05
      • 1970-01-01
      • 2010-10-11
      • 1970-01-01
      • 1970-01-01
      • 2019-07-25
      • 2011-03-10
      相关资源
      最近更新 更多