把舊系統遷移到.Net Core 2.0 日記(10) -- EF core 和之前版本多對多映射區別


EF Core 現在不支持多對多映射,只能做2個一對多映射.

而EF Core 的一對多映射,采用約定大於配置. 默認的外鍵字段名是(引用實體名+主鍵名, 或者引用實體的主鍵名)

 public class Product
    {   
        [Key]
        public int ProdId{ get; set; }

        public String ProdCode{ get; set; }
        public String ProdName{ get; set; }

        public IList<CategoryProduct> CategoryProducts { get; set; }
    }
    public class Category
    {   
        [Key]
        public int CategoryId{ get; set; }
        public String CategoryCode { get; set; }
        public String CategoryName{ get; set; }

        public IList<CategoryProduct> CategoryProducts { get; set; }
    }

例如Product實體里有一個Category類的字段. 對應的SQL語句,就會在Product表里查找CategoryCategoryId的字段,或者是CategoryId的字段

 參考這個 https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships

比如Product和Category 我現在定義Product和Category是多對多關系.

那么實體定義如下:

   /// <summary>
    /// EF CORE 處理Many to Many關系,要轉成兩個 One to Many
    /// https://docs.microsoft.com/en-us/ef/core/modeling/relationships
    /// </summary>
    public class CategoryProduct
    {   
        public int Id { get; set; }
        public int CategoryId{ get; set; }
        public Category Category { get; set; }
        public int ProdId { get; set; }
        public Product Product { get; set; }
    }

在context的OnModelCreating方法指定mapping

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {  
            
            //多對多關系,要手工指定
            modelBuilder.Entity<CategoryProduct>().HasKey(x => new { x.CategoryId, x.ProdId }); //指定中間表主鍵

modelBuilder.Entity<CategoryProduct>().HasOne(cp => cp.Category) .WithMany(c => c.CategoryProducts) .HasForeignKey(cp => cp.CategoryId); modelBuilder.Entity<CategoryProduct>().HasOne(cp => cp.Product) .WithMany(c => c.CategoryProducts) .HasForeignKey(cp => cp.ProdId); }

但是我很困惑,這樣的寫法, 我在Category里的CategoryProducts的Collection 有什么用呢?

比如我要找CategroyId=1的所有產品信息,我覺得最直觀的寫法的是context.categories.Where(t=>t.CategoryId==1).FirstOrDefault().Products.toList()這樣的寫法.

它現在卻要這樣寫

 _context.CategoryProducts.Where(x => x.CategoryId.Equals(1)).Include(x=>x.Product).Select(x=>x.Product).ToList();

或者這樣寫

            var prodIdList = _context.CategoryProducts.Where(x => x.CategoryId.Equals(1)).Select(x=>x.ProdId);
            var qry = _context.Products.Where(x => prodIdList.Contains(x.ProdId)).ToList();

            var result = new { total = qry.Count(), rows = qry.ToList() };
            return Json(result);

這樣需要查詢2次,在NLOG的日志里可以看到

2018-04-15 22:33:56.9887|1|INFO|Microsoft.AspNetCore.Hosting.Internal.WebHost|Request starting HTTP/1.1 GET http://localhost:5001/CRM/Product/ListCategoryProducts/1   
2018-04-15 22:33:57.0437|24|INFO|Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware|No cached response available for this request. 
2018-04-15 22:33:57.2326|1|INFO|Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker|Executing action method FoxCRMCore.Controllers.CRM.ProductController.ListCategoryProducts (FoxCRMCore) with arguments (1) - ModelState is Valid 
2018-04-15 22:33:58.7572|10403|INFO|Microsoft.EntityFrameworkCore.Infrastructure|Entity Framework Core 2.0.2-rtm-10011 initialized 'CRMContext' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: MaxPoolSize=128  
2018-04-15 22:33:59.4957|20101|INFO|Microsoft.EntityFrameworkCore.Database.Command|Executed DbCommand (73ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT `x`.`CategoryId`, `x`.`CategoryCode`, `x`.`CategoryName`, `x`.`CreateDate`, `x`.`CreateUser`, `x`.`IsActive`, `x`.`ModifyDate`, `x`.`ModifyUser`, `x`.`SYSId`
FROM `CRMCategory` AS `x`
WHERE `x`.`CategoryId` = @__id_0 
2018-04-15 22:33:59.7948|20101|INFO|Microsoft.EntityFrameworkCore.Database.Command|Executed DbCommand (9ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT `x`.`ProdId`, `x`.`CreateDate`, `x`.`CreateUser`, `x`.`IsActive`, `x`.`ModifyDate`, `x`.`ModifyUser`, `x`.`ProdCode`, `x`.`ProdFullName`, `x`.`ProdName`, `x`.`SYSId`
FROM `CRMProduct` AS `x`
WHERE `x`.`ProdId` IN (
    SELECT `x0`.`ProdId`
    FROM `CRMCategoryProduct` AS `x0`
    WHERE `x0`.`CategoryId` = @__id_0
) 
2018-04-15 22:33:59.8546|1|INFO|Microsoft.AspNetCore.Mvc.Formatters.Json.Internal.JsonResultExecutor|Executing JsonResult, writing value { total = 2, rows = System.Collections.Generic.List`1[FoxCRMCore.Models.PD.Product] }. 
2018-04-15 22:33:59.8996|2|INFO|Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker|Executed action FoxCRMCore.Controllers.CRM.ProductController.ListCategoryProducts (FoxCRMCore) in 2753.674ms 

 

如果Category和Product是1對多關系.

    public class Product
    {   
        [Key]
        public int ProdId{ get; set; }

        public String ProdCode{ get; set; }
        public String ProdName{ get; set; }
 public int CategoryId { get; set; }
        public Category Category { get; set; } //1對多
    }
    public class Category
    {   
        [Key]
        public int CategoryId{ get; set; }
        public String CategoryCode { get; set; }
        public String CategoryName{ get; set; }
 public IList<Product> Products { get; set; }

    }

在context的OnModelCreating方法指定mapping

            modelBuilder.Entity<Product>().HasOne(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => p.CategoryId);

比如我要找CategroyId=1的所有產品信息,寫法如下:

(因為用了Include,它會一直循環eagerLoad collection, 找Category對應的Products,然后再找Products的Category,如果轉成Json就死循環了.所以用Select只顯示部分字段)

Json.JsonSerializationException: Self referencing loop detected, 另一個方案是在Product的Category字段加上[IgnoreDataMember]  (using System.Runtime.Serialization;)

            var cat = _context.Categories.Where(x => x.CategoryId.Equals(id)).Include(x=>x.Products).FirstOrDefault();
            var qry = cat.Products.Select(x=> new {x.ProdId,
                x.ProdCode,
                x.ProdName,
                x.ProdFullName,
                x.CategoryId,
                x.Category.
                CategoryName});

            var result = new { total = qry.Count(), rows = qry.ToList() };
            return Json(result);

NLOG日志顯示,也是查詢2次

2018-04-15 23:28:54.7770|20101|INFO|Microsoft.EntityFrameworkCore.Database.Command|Executed DbCommand (68ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT `x`.`CategoryId`, `x`.`CategoryCode`, `x`.`CategoryName`, `x`.`CreateDate`, `x`.`CreateUser`, `x`.`IsActive`, `x`.`ModifyDate`, `x`.`ModifyUser`, `x`.`SYSId`
FROM `CRMCategory` AS `x`
WHERE `x`.`CategoryId` = @__id_0
ORDER BY `x`.`CategoryId`
LIMIT 1 
2018-04-15 23:28:54.9476|20101|INFO|Microsoft.EntityFrameworkCore.Database.Command|Executed DbCommand (0ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT `x.Products`.`ProdId`, `x.Products`.`CategoryId`, `x.Products`.`CreateDate`, `x.Products`.`CreateUser`, `x.Products`.`IsActive`, `x.Products`.`ModifyDate`, `x.Products`.`ModifyUser`, `x.Products`.`ProdCode`, `x.Products`.`ProdFullName`, `x.Products`.`ProdName`, `x.Products`.`SYSId`
FROM `CRMProduct` AS `x.Products`
INNER JOIN (
    SELECT `x0`.`CategoryId`
    FROM `CRMCategory` AS `x0`
    WHERE `x0`.`CategoryId` = @__id_0
    ORDER BY `x0`.`CategoryId`
    LIMIT 1
) AS `t` ON `x.Products`.`CategoryId` = `t`.`CategoryId`
ORDER BY `t`.`CategoryId` 

 

EF Core ThenInclude 自動完成提示,不會提示子對象的關聯對象. 你寫完再看,就會提示正確的.
https://github.com/dotnet/roslyn/issues/8237


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM