【问题标题】:Referencing DbContext from POCO object从 POCO 对象引用 DbContext
【发布时间】:2011-04-23 17:04:52
【问题描述】:

我在下一个项目中使用 Code First。我真的很喜欢这个主意,而且到目前为止效果很好。我唯一不满意的是,我找不到任何关于如何使用这种野兽的文档,而且谷歌搜索通常指的是现在已经过时的 CTP。

对于这个问题,我将建模一个有向图。 图遍历的算法并不是最优的。

我有一个简单的 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; }
}

简单整洁。

但现在假设我想向每个节点添加对 Graph 对象的某种引用,以便节点可以在图的上下文中弄清楚自己的情况,例如它有多少条边。

我希望我的节点在创建时具有

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 类。我不想这样做的原因是因为我想绑定到那个属性,而绑定是邪恶的。

你们中的哪一位能指导我正确组合 Annotation/EntityTypeConfiguration 魔法来快速实现这一点吗?

我应该注意不同的约定吗?例如,如果我能以某种方式绑定所有 Edge 或更好,一些 Edge(从节点发出的那些)到节点,那就更优雅了。

提前致谢,如果您对任何 Code First 爱好者应阅读的内容有任何建议...首先,请分享您的链接!

【问题讨论】:

    标签: entity-framework c#-4.0 ef-code-first


    【解决方案1】:

    你可以引入关联的另一端,即从节点开始到节点结束的边的集合。您将不需要 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 以受益于延迟加载。

    注意:此解决方案仅在您真正对节点的传出和传入边缘感兴趣时才有用。 (我主要指的是你问题的这一部分:“如果我能以某种方式绑定所有边缘或更好,一些边缘(从节点发出的边缘)到节点......”)如果你只想有 number 个您从数据库中加载过多的边(所有 Edge 对象)。

    编辑

    一些关于 EF 4.1 的资源,尤其是 Code-First:

    代码优先演练:http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-code-first-walkthrough.aspx

    关于 EF 4.1 的 12 部分教程:http://blogs.msdn.com/b/adonet/archive/2011/01/27/using-dbcontext-in-ef-feature-ctp5-part-1-introduction-and-model.aspx

    Morteza Manavi 关于 Code-First 中的关联和继承的博客:http://weblogs.asp.net/manavi/default.aspx

    关于 EF 4.1 的 MSDN 页面:http://msdn.microsoft.com/en-us/library/gg696172%28v=vs.103%29.aspx

    一些教程视频:http://msdn.microsoft.com/en-us/data/cc300162

    编辑 2

    如果您只想将 number 个边绑定到视图而不加载所有边对象,我会考虑使用包装节点本身并具有附加属性的 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 的想法)。

    【讨论】:

    • 感谢您提供非常全面的答案,我将回顾我的实际问题以及该解决方案如何适应它。您知道是否可以将 Graph 对象引用附加到每个节点?味道不好吗?
    • 这个 12 部分的教程真的很棒!
    • @Gleno:对我来说,它的味道很差,是的。 “POCO”的想法是其中没有与持久性相关的代码或引用。我在我的答案中附加了一个 Edit2 部分,作为您的绑定方案的替代解决方案,它避免在您的模型类中包含上下文。我不知道这是否能满足您的所有要求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 2011-07-04
    • 1970-01-01
    相关资源
    最近更新 更多