如何查询所有实现某个接口的表格

6

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

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数据注释明确排除。请验证该类型被定义为类,不是基元或泛型,并且不继承自EntityObject。

如果很多ORM提供了那种类型的查询,我会感到惊讶。 - Marc Gravell
@MarcGravell,你是在说这不可能吗? - enb081
1
检查Entity Framework的继承策略。我不确定是否可能有DbSet <Interface>,但您可以使用基础抽象类,也许具有虚拟(或抽象)属性TableName来解决这个问题。顺便说一句,有一个称为TableName的属性 - 在此处检查继承策略的链接 - http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx - a.farkas2508
4个回答

9
我会选择以下方法:
创建这个扩展方法。
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
}

1
我喜欢你在这里做的事情,但是你真的能够依赖于接口与上下文表在相同的程序集中定义吗?从DbContext类型获取程序集不是更好吗? - Enigmativity
1
你说得对,没有理由反对。我已经更新了我的回答并进行了一些修正。 - natenho
只需注意,如果您的模型与上下文不在同一个程序集中,这也无法正常工作。只需要获取AppDomain中的所有程序集或类似内容即可。 - GraemeMiller

2

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

然后使用存储库方法并提供要查询的表的类型。例如:myRepo.GetAll<myClient.GetType()>();

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

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

希望这能帮到您!可能有更加优雅的解决方案。

类型和泛型不太搭配;虽然可以做到,但是实现起来比较麻烦。 - Marc Gravell
反射可以通过将类型作为参数传递来调用泛型方法。 - Tengiz

1
首先,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;
    }
}

}


0

被接受的解决方案在 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>();

更多信息请参见公开方法DbContext.Set(Type entityType)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接