【问题标题】:Common query for multiple similar entity types in Entity FrameworkEntity Framework 中多个相似实体类型的通用查询
【发布时间】:2017-02-09 17:03:20
【问题描述】:

我有一个场景,我在数据库中有 3 种类型的产品,并且所有产品都有自己单独的表(例如 Product1Product2Product3)。几乎所有产品表都具有相同的架构。我需要在不同的表中获取不同类型的产品。

我目前有 3 种方法来获取产品,每种产品类型一种:

public List<Product1> GetProduct1Data() {
    //....
    context.Product1.Where(..).Tolist();
}

public List<Product2> GetProduct2Data() {
    //....
    context.Product2.Where(..).Tolist();
}

public List<Product3> GetProduct3Data() {
    //....
    context.Product3.Where(..).Tolist();
}

在调用产品时,我有一个 WebApi 方法,它接受产品类型并调用相应的方法:

public IHttpActionResult GetProducts(ProductType product)
{ 
    ///....
    // Ii have to call repositories according to product parameter
}

Entity Framework 有什么方法可以让我只用一种方法选择表吗?

【问题讨论】:

    标签: c# entity-framework linq


    【解决方案1】:

    您可以使用带有接口约束的泛型方法。

    当您拥有这些自动生成的 POCO 类时:

    public partial class Product1 {
        public string Column1 {
            get;
            set;
        }
    
        public string Column2 {
            get;
            set;
        }
    }
    
    
    public partial class Product2 {
        public string Column1 {
            get;
            set;
        }
    
        public string Column2 {
            get;
            set;
        }
    }
    
    
    public partial class Product3 {
        public string Column1 {
            get;
            set;
        }
    
        public string Column2 {
            get;
            set;
        }
    }
    

    您为实体类共有的属性创建一个接口...

    interface IProduct {
        string Column1 {
            get;
            set;
        }
    
        string Column2 {
            get;
            set;
        }
    }
    

    ...您将其应用于生成的类(在新代码文件中 - 类是部分允许您这样做的):

    partial class Product1 : IProduct {};
    partial class Product2 : IProduct {};
    partial class Product3 : IProduct {};
    

    现在您可以为您的查询创建一个通用方法。您可以将其作为扩展方法应用到您的DbSets:

    static class ProductExtensions {
        public static List<T> GetProducts<T>(this DbSet<T> products)
            where T : IProduct {
    
            var productQry =
                from product in products
                where product.Column1 == "Example"
                select product;
            return productQry.ToList();
        }
    }
    

    您可以在DbSets 上使用此扩展方法:

    List<Product1> product1List = context.Product1s.GetProducts();
    List<Product2> product2List = context.Product2s.GetProducts();
    List<Product3> product3List = context.Product3s.GetProducts();
    

    唯一的限制是表中的列确实需要具有相同的名称和类型。 EF 不识别显式接口实现。另一方面,表格不必完全相同。您可以为部分列(必须匹配)定义接口,其余部分可以不同。

    【讨论】:

    • 能否请您详细说明扩展方法?
    • 基本上我对“来自产品中的产品”行感到困惑,您所说的“来自源中的产品”是什么意思?除此之外,它非常好@sefe
    【解决方案2】:

    提供的答案是最优雅的解决方案,但我正在考虑另一种选择:映射到视图。

    如果各种产品表主要用于读取并以聚合方式(一次从多种类型读取),则创建所有它们的视图并映射到它可能会很方便:

    CREATE VIEW ProductAggregated
    AS
    SELECT 1 AS ProdTypeId, ProdId AS ProductId, Name AS ProductName          -- other product columns may come here
    UNION ALL
    SELECT 2 AS ProdTypeId, Id AS ProductId, ProdName AS ProductName          -- different columns names may be harmonized with aliasing
    -- other product types table may be entered here
    

    将 POCO 映射到视图:

    public class ProductAggregated
    {
        public int ProdTypeId { get; set; }
        public string ProductName { get; set; }
        // other properties come here
    }
    

    相关的产品类型可以定义到一个枚举中:

    public enum ProdType
    {
        None = 0,                   -- as default value, if needed
        ProdType1 = 1,
        ProdType2 = 2
        ProdType3 = 3
    }
    

    您可以根据自然条件轻松选择产品:

    // all products
    var allProducts = context.ProductAggregateds.ToList();
    
    // all products of certain types
    var types = new List<int> { (int)ProdType.ProdType1, (int)ProdType.ProdType3 };
    var only13Products = context.ProductAggregateds
        .Where(p => types.Contains(p.ProductTypeId)
        .ToList();
    

    如果创建了新产品类型并且应用程序不处理其特定列,只需将 UNION ALL 添加到视图中,一切都会正常工作。

    【讨论】:

    • 您可以映射到视图。缺点是当你将实体类直接映射到视图时,不能添加导航属性。当您映射到您转换为视图的表时,每次从数据库更新模型时,您都需要重复手动操作,
    • 没错。此外,在数据库优先的方法中,设计者自己无法确定哪个是关键并输出警告。显然,您的解决方案更好(个人而言,我喜欢 C# 方法而不是 SQL 方法),但是如果产品类型很多并且它们与 C# 中的服务层无关,则可能会考虑这样做。它还允许在 SQL 中使用“类似接口”的用法。
    • @Alexei:你是对的警告。但它只会在您更新模型时出现(并一直存在到项目关闭)。当您重新打开项目并编译时,警告将消失。
    • @Sefe - 对我来说它永远存在。重新打开解决方案将隐藏它,直到执行构建。手动指定密钥修复它,直到实体被刷新。
    猜你喜欢
    • 2021-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多