I'm using Code First for my next project. I really like the idea, and so far it has worked great. My only beef with it, is that I can't find any documentation on how to wield this beast, and googling often refers to now outdated CTPs.
For this question, i'll model a directed graph. The algorithm for graph traversal is not intended to be optimal.
I have a simple poco structure, something like this
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; }
}
Simple and neat.
But now suppose I want to add to each node some kind of reference to the Graph object, so that the node can figure things out about itself in the context of a graph, for example how many edges it has.
I want my Node, when created, to have
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); }
}
}
I think that I'm stepping out of convention here to solve a particular problem that I'm having. For example this property could be moved as a method to the Graph-class. The reason that I don't want to do that, is because I want to bind to that property, and binding is evil.
Can one of you wizards guide me to the right combination of Annotation/EntityTypeConfiguration magic necessary to pull this fast one?
Is there a different convention that I should be aware of? For example, If I somehow could bind all the Edges or better yet, some of the Edges (The ones eminating from the node) to the Node, then that's even more elegant.
Thanks in advance, and if you have an advice on what any Code First enthusiast should read ... first, please share your links!
You could introduce the other side of the association, that means the collections of Edges which start at the node and which end at the node. You wouldn't need then the database context in the Node class:
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; }
}
}
Or in Fluent configuration if you don't want the attributes in the Edge class:
modelBuilder.Entity<Edge>()
.HasRequired(e => e.StartNode)
.WithMany(n => n.OutgoingEdges);
modelBuilder.Entity<Edge>()
.HasRequired(e => e.EndNode)
.WithMany(n => n.IncomingEdges);
When you load a Node you have to make sure that also the collection of edges you want is loaded:
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
}
Alternatively you can mark your navigation properties as virtual to benefit from lazy loading.
Note: This solution is only useful if you are really interested in the outgoing and incoming edges of a node. (I was refering mainly to this part of your question: "If I somehow could bind all the Edges or better yet, some of the Edges (The ones eminating from the node) to the Node...") If you only want to have the number of edges you are loading too much from the database (all the Edge objects).
Edit
A few resources about EF 4.1, especially 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
A few tutorial videos: http://msdn.microsoft.com/en-us/data/cc300162
Edit 2
If you want to only bind the number of edges to a view without loading all edge objects I would consider to work with a ViewModel which wraps the Node itself and has an additional property:
public class NodeViewModel
{
public Node Node { get; set; }
public int NumberOfOutgoingEdges { get; set; }
}
I would leave the two navigation collections in the Node class (and remove the EdgesFromThisNode property) but I wouldn't load the collections for this specific binding scenario and use instead a projection into the new ViewModel type:
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
}
Then you bind the NodeViewModel to your View and not directly the Node. This solution avoids to inject somehow the database context into your model class (which is very against the idea of a POCO in my opinion).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With