我正在使用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对象的引用,这样节点就可以在图形的上下文中找出自己的内容,例如它有多少边。
我希望我的Node在创建时具有
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-class。我不想这样做的原因是因为我想绑定到那个属性,绑定是邪恶的。
你们其中一个向导能否引导我选择正确的Annotation / EntityTypeConfiguration魔法组合来拉动这个快速的魔法吗?
我应该注意一个不同的约定吗?例如,如果我能以某种方式绑定所有边缘或更好的边缘,一些边缘(从节点发出的边缘)到节点,那就更优雅了。
提前致谢,如果您对Code First爱好者应该阅读的内容有任何建议......请先分享您的链接!
答案 0 :(得分: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
类中的属性:
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关于代码优先关联和继承的博客: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
如果你只想将边缘的数字绑定到视图而不加载所有边缘对象,我会考虑使用一个ViewModel来封装Node本身,并且还有一个额外的属性:
public class NodeViewModel
{
public Node Node { get; set; }
public int NumberOfOutgoingEdges { get; set; }
}
我会将两个导航集合保留在Node类中(并删除EdgesFromThisNode
属性),但我不会为这个特定的绑定方案加载集合,而是使用投影到新的ViewModel类型:< / p>
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的想法)。