从POCO对象引用DbContext

3

我将在我的下一个项目中使用Code First。我非常喜欢这个想法,到目前为止它也运行得很好。我唯一不满意的是,我找不到如何操纵这个工具的任何文档,而且谷歌搜索通常会引用过时的CTPs。

对于这个问题,我将建立一个有向图模型。图遍历算法并不打算达到最优。

我有一个简单的POCO结构,类似于这样:

class Graph : DbContext
{
    public DbSet<Node> Nodes { get; set; }
    public DbSet<Edge> Edges { get; set; }

    public Graph(string connectionString) : base(connectionString) { }
}

class Edge
{
    public int Id { get; set;}
    public double Weight { get; set; }

    public Node StartNode { get; set; }
    public Node EndNode { get; set; }
}

class Node
{
    public int Id { get; set; }
    public string Label { get; set; }
}

简单而整洁。

但是现在假设我想要为每个节点添加对图形对象的某种引用,以便节点可以在图形的上下文中了解自身的情况,例如它有多少条边。

我希望我的节点在创建时拥有:

class Node
{
    public int Id { get; set; }
    public string Label { get; set; }

    //I want this property populated by magic. 
    //Just leaving it here crashes the program
    public Graph Graph { get; set; }

    //So that this property would do meaningful things.
    public int EdgesFromThisNode
    {
        get { return Graph.Edges.Count(e => e.StartNode.Id == Id); }
    }
}

我觉得我在这里违反了惯例,为了解决我遇到的特定问题。例如,这个属性可以作为方法移动到Graph类中。我不想这么做的原因是我想绑定到那个属性,而绑定是有害的。
你们其中一个巫师能引导我找到必要的注释/EntityTypeConfiguration魔法的正确组合来解决这个问题吗?
有没有其他的惯例需要我注意?例如,如果我能够将所有的边或者更好的一些边(从节点出发的那些)绑定到节点上,那就更加优美了。
提前感谢,如果您对任何Code First爱好者应该阅读...首先,请分享您的链接!
1个回答

4
您可以介绍关联的另一面,也就是以该节点为起点并以该节点为终点的边的集合。这样您就不需要在Node类中使用数据库上下文了。
class Edge
{
    public int Id { get; set;}
    public double Weight { get; set; }

    [InverseProperty("OutgoingEdges")]
    [Required]
    public Node StartNode { get; set; }
    [InverseProperty("IncomingEdges")]
    [Required]
    public Node EndNode { get; set; }
}

class Node
{
    public int Id { get; set; }
    public string Label { get; set; }

    public ICollection<Edge> OutgoingEdges { get; set; }
    public ICollection<Edge> IncomingEdges { get; set; }

    public int EdgesFromThisNode
    {
        get { return OutgoingEdges != null ? OutgoingEdges.Count() : 0; }
    }
}

如果您不想在Edge类中使用属性,则可以在Fluent配置中进行设置:

modelBuilder.Entity<Edge>()
            .HasRequired(e => e.StartNode)
            .WithMany(n => n.OutgoingEdges);

modelBuilder.Entity<Edge>()
            .HasRequired(e => e.EndNode)
            .WithMany(n => n.IncomingEdges);

当你加载一个 Node 时,你必须确保要加载的边的集合也被加载:

using (var graph = new Graph())
{
    Node node = graph.Nodes.Include(n => n.OutgoingEdges)
                     .FirstOrDefault(n => n.Id == 1);
    // node.EdgesFromThisNode would give now correct result
}

另外,您可以将导航属性标记为virtual以从延迟加载中受益。
注意:只有在您真正对节点的出边和入边感兴趣时,此解决方案才有用。 (我主要是指您问题的这部分:“如果我能够将所有边缘或更好地说,一些边缘(从节点发出的边缘)绑定到节点……”)如果您只想拥有边缘的数量,则会从数据库中加载过多内容(所有Edge对象)。 编辑 关于EF 4.1,特别是Code-First的一些资源:
Code-First Walkthrough:http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-code-first-walkthrough.aspx 12-Part tutorial about EF 4.1:http://blogs.msdn.com/b/adonet/archive/2011/01/27/using-dbcontext-in-ef-feature-ctp5-part-1-introduction-and-model.aspx Morteza Manavi's blog about Associations and Inheritance in Code-First:http://weblogs.asp.net/manavi/default.aspx MSDN pages about EF 4.1:http://msdn.microsoft.com/en-us/library/gg696172%28v=vs.103%29.aspx 一些教程视频:http://msdn.microsoft.com/en-us/data/cc300162 编辑2 如果您只想将边的数量绑定到视图,而不加载所有边缘对象,则可以考虑使用ViewModel,该ViewModel包装节点本身并具有附加属性:
public class NodeViewModel
{
    public Node Node { get; set; }
    public int NumberOfOutgoingEdges { get; set; }
}

我会在Node类中保留这两个导航集合(并删除EdgesFromThisNode属性),但对于这种特定的绑定情况,我不会加载集合,而是使用新的ViewModel类型进行投影:

using (var graph = new Graph())
{
    NodeViewModel nodeViewModel = graph.Nodes
        .Where(n => n.Id == 1)
        .Select(n => new NodeViewModel()
            {
                Node = n,
                NumberOfOutgoingEdges = n.OutgoingEdges.Count()
            })
        .FirstOrDefault();
    // nodeViewModel.Node doesn't have the OutgoingEdges loaded now
}

然后,您将NodeViewModel绑定到视图而不是直接绑定Node。这种解决方案避免了将数据库上下文以某种方式注入到您的模型类中(在我看来,这非常违反POCO理念)。


谢谢您提供如此全面的答案,我会对我的实际问题进行审查,并了解这个解决方案如何适用。您知道是否可能将一个图形对象引用附加到每个节点上吗?这样做是否不妥? - Gleno
那个由12部分组成的教程真的很棒! - Gleno
@Gleno:对我来说,这个想法有些不妥。 "POCO" 的概念是在其中没有任何持久性相关的代码或引用。我在我的答案中添加了一个 Edit2 部分,作为您绑定场景的替代解决方案,避免在您的模型类中使用上下文。虽然我不知道这是否符合您的所有要求。 - Slauma

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