【问题标题】:Defining linked entites with POCO classes使用 POCO 类定义链接实体
【发布时间】:2015-08-22 03:35:39
【问题描述】:

我使用 EF Code First 课程。

我有一个名为Request的实体:

public class Request
{
    [Key]
    public virtual int RequestID { get; set; }
    ...
}

我需要互相链接Request

例如:

如果我们有下面定义的 2 个链接:

  • RequestID 1 链接到 RequestID 2
  • RequestID 1 链接到 RequestID 3

那么...

如果我们询问链接了哪些请求:

  • (A) for 1 : 结果是 2,3
  • (B) for 2 : 结果是 1,3
  • (C) 对于 3:结果是 1,2

说明:

  • 对于 (A) 这很容易找到结果,因为链接是“直接”的
  • 对于 (B) 和 (C),这有点复杂,因为通过 1 的“路径”检索链接。我不知道我是否清楚。

我的问题:定义链接模型类的最佳方式是什么?之后,如何查询(LINQ)这些模型类以检索如上所示的结果?

一开始,我认为链接模型类如下:(不知道这是不是一个好主意)

public class RequestLinked
{
    [Key, Column(Order = 0)]
    [ForeignKey("Request")]
    public int RequestID { get; set; }

    [Key, Column(Order = 1)]
    [ForeignKey("RequestRelated")]
    public int RequestRelatedID { get; set; }

    public virtual Request Request { get; set; }
    public virtual Request RequestRelated { get; set; }
}

【问题讨论】:

    标签: linq entity-framework ef-code-first


    【解决方案1】:

    您可能正在寻找以下结构:

    实体

    public class Request
    {
        [Key]
        public virtual int RequestID { get; set; }
        ...
        // Suppose that "1" is related to "2" and "3"
        // "1" -> "2" (1 is the left side of relationship; 2 is the right side)
        // "1" -> "3" (1 is the left side of relationship; 3 is the right side)
    
        // For "1" it will return "2" and "3"
        // For "2" it will return nothing
        // For "3" it will return nothing
        public virtual ICollection<RequestLinked> RequestsLinked { get; set; }
    
        // For "1" it will return nothing
        // For "2" it will return "1"
        // For "3" it will return "1"
        public virtual ICollection<RequestLinked> RequestsLinkedThisRequest { get; set; }
    }
    

    映射

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Request>()
            .HasKey(i => i.RequestID);
    
        modelBuilder.Entity<RequestLinked>()
            .HasKey(i => new {i.RequestID, i.RequestRelatedID });
    
        modelBuilder.Entity<Request>()
            .HasMany(i => i.RequestsLinked)
            .WithRequired(i => i.RequestRelated )
            .HasForeignKey(i => i.RequestRelatedID )
            .WillCascadeOnDelete(false);
    
        modelBuilder.Entity<Request>()
            .HasMany(i => i.RequestsLinkedThisRequest )
            .WithRequired(i => i.Request)
            .HasForeignKey(i => i.RequestID)
            .WillCascadeOnDelete(false);
    
    
        base.OnModelCreating(modelBuilder);
    }
    

    查看此链接Entity Framework - Fluent APi - Create table with 2 FK

    希望对你有帮助!

    【讨论】:

    • 你能给我一些关于你的解决方案的解释吗?无论如何,谢谢。
    • 看看代码里面的cmets;这种关系适合你的情况吗?
    • 谢谢,我明天试试,并与您保持联系。
    • 在这种情况下,您有两个导航属性。第一个将返回当前对象位于关系左侧的情况。第二个将返回当前对象位于关系右侧的情况。看看帖子末尾的链接。它可能有用。
    【解决方案2】:

    我认为您的模型很好,但问题的性质很复杂,我认为没有有效的 LINQ 查询来检索路径。因为检索路径需要递归地左右移动请求。我认为你能做的最好的事情就是使用 SQL 递归查询。像这样的:

    WITH cte AS ( 
                SELECT RequestLink.*, cast('(' + cast(RequestId as nvarchar(max)) + ',' 
                         + cast(RequestRelatedId as nvarchar(max))+')' as nvarchar(max)) Path 
                  FROM RequestLink 
                  WHERE RequestId = 3 OR RequestRelatedId = 3
                  UNION ALL 
    
                  SELECT a.*,
                       cast(
                             c.Path + '(' + 
                             cast(a.RequestId as nvarchar(max)) + ',' + 
                             cast(a.RequestRelatedId as nvarchar(max)) + ')' 
                           as nvarchar(max)
                           ) Path 
                FROM RequestLink a JOIN cte c ON 
                      a.RequestId = c.RequestRelatedId 
                      OR c.RequestId = a.RequestRelatedId 
                      OR c.RequestId = a.RequestId 
                      OR c.RequestRelatedId = a.RequestRelatedId 
                    where c.Path not like cast(
                             '%(' + 
                             cast(a.RequestId as nvarchar(max)) + ',' + 
                             cast(a.RequestRelatedId as nvarchar(max)) + 
                             ')%' 
                           as nvarchar(max)
                           )
    
      )
      SELECT DISTINCT id from (
         SELECT distinct RequestId as id FROM cte
         union all 
         SELECT distinct RequestRelatedId as id FROM cte
      ) a
    

    此查询首先找到id=3,然后递归地附加具有公共ID 的其他链接并为其创建路径。已经在路径中的路径行将被丢弃。最后我们 select 一个 distinct 数组 ids.[a sample on fiddle]
    您可以在 EF 上下文中将此查询用作调用 Database.SqlQuery(...) 的函数。

    另一种选择是将所有链接加载到内存并构建整个图,然后使用图算法找到路径。如果链接数量较少,可以使用此选项。

    另一种选择是将导航属性添加到 Request 类,正如 Fabio Luz 帖子所建议的那样,在这种方法中,您可以递归地导航两个导航属性并检索子项,直到达到n。其中n 是引用数。
    您必须注意这种方法中的循环依赖,可以通过保留遍历的Requests 列表来克服。 这种方法需要多次 DB 往返(需要连接来检索导航属性),所以如果路径的深度很大,这可能不是一个选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-28
      • 2011-11-16
      • 1970-01-01
      相关资源
      最近更新 更多