Defined model and classes
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<RootLink>()
.HasOne(link => link.Node)
.WithMany(node => node.RootLinks)
.HasForeignKey(link => link.NodeId);
modelBuilder.Entity<RootLink>()
.HasOne(link => link.RootNode)
.WithMany()
.HasForeignKey(rootLink => rootLink.RootNodeId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<NodeLink>()
.HasOne(link => link.Node)
.WithMany(node => node.NodeLinks)
.HasForeignKey(link => link.NodeId);
modelBuilder.Entity<NodeLink>()
.HasOne(l => l.LinkedNode)
.WithMany()
.HasForeignKey(l => l.LinkedNodeId)
.OnDelete(DeleteBehavior.Restrict);
}
...
public class Node
{
public long Id { get; set; }
public ICollection<RootLink> RootLinks { get; set; }
public ICollection<NodeLink> NodeLinks { get; set; }
public int Value { get; set; }
}
public class NodeLink
{
public long Id { get; set; }
public long NodeId { get; set; }
public Node Node { get; set; }
public long LinkedNodeId { get; set; }
public Node LinkedNode { get; set; }
}
public class RootLink
{
public long Id { get; set; }
public long NodeId { get; set; }
public Node Node { get; set; }
public long RootNodeId { get; set; }
public Node RootNode { get; set; }
}
DB populated as followed:
var node1 = new Node();
var node2 = new Node();
var node3 = new Node();
node1.NodeLinks = new List<NodeLink>()
{
new NodeLink
{
Node = node1,
LinkedNode = node2
},
new NodeLink
{
Node = node3,
LinkedNode = node3
}
};
node1.RootLinks = new List<RootLink>
{
new RootLink {RootNode = node1},
new RootLink {RootNode = node3}
};
ctx.Nodes.AddRange(node1, node2, node3);
Thr question is how can I query nodes with it nodeLinks and rootLinks in one query using EF core?
In plain sql it will looks like this:
SELECT [node].[Id], [node].[Value], [rootLink].[Id], [rootLink].[NodeId], [rootLink].[RootNodeId]
FROM [Nodes] AS [node]
LEFT JOIN [RootLinks] AS [rootLink] ON [node].[Id] = [rootLink].[NodeId]
LEFT JOIN [NodeLinks] AS [nodeLink] ON [node].[Id] = [rootLink].[NodeId]
WHERE [node].[Id] in (NODE_ID_LIST)
ORDER BY [node].[Id]
Using ef i ended up with following variants of query:
public static IEnumerable<Node> FindVariant1(TestDbContext ctx, params long[] nodeIdList)
{
return ctx.Nodes
.Include(node => node.NodeLinks)
.Include(node => node.RootLinks)
.Where(node => nodeIdList.Contains(node.Id)).ToList();
}
public static IEnumerable<Node> FindVariant2(TestDbContext ctx, params long[] nodeIdList)
{
return ctx.Nodes
.GroupJoin(ctx.RootLinks, node => node.Id, rootLink => rootLink.NodeId,
(node, rootLinks) => new {node, rootLinks})
.SelectMany(info => info.rootLinks.DefaultIfEmpty(), (info, rootLink) => new {info.node, rootLink})
.GroupJoin(ctx.NodeLinks, node => node.node.Id, nodeLink => nodeLink.NodeId,
(info, nodeLinks) => new {info.node, info.rootLink, nodeLinks})
.SelectMany(info => info.nodeLinks.DefaultIfEmpty(),
(info, nodeLink) => new {info.node, info.rootLink, nodeLink})
.Where(node => nodeIdList.Contains(node.node.Id)).ToList()
.Select(r => r.node);
}
Both generates several queries.