如何为Entity Framework无键联合视图配置导航属性?
无键视图导航属性的替代实现方案
首先先指出你代码里的一个小错误:ExampleKeylessUnionView 中的 Table2 导航属性类型写错了,应该是 Table2 而非 Table1,先修正这个问题再看方案。
EF 确实不支持为无键实体配置导航属性,下面是几个可行的替代方案:
1. 手动批量关联关联数据(推荐)
在业务层通过扩展方法或工具类,批量查询视图数据对应的关联实体并赋值,避免N+1查询问题:
// 给视图查询添加扩展方法,批量加载关联数据 public static async Task<IEnumerable<ExampleKeylessUnionView>> LoadRelatedDataAsync( this IQueryable<ExampleKeylessUnionView> viewQuery, YourDbContext context) { var viewList = await viewQuery.ToListAsync(); // 提取需要查询的关联ID var table1Ids = viewList.Where(v => v.Table1Id != 0).Select(v => v.Table1Id).Distinct(); var table2Ids = viewList.Where(v => v.Table2Id != 0).Select(v => v.Table2Id).Distinct(); // 批量查询并转成字典方便快速查找 var table1Dict = await context.Table1 .Where(t => table1Ids.Contains(t.Table1Id)) .ToDictionaryAsync(t => t.Table1Id); var table2Dict = await context.Table2 .Where(t => table2Ids.Contains(t.Table2Id)) .ToDictionaryAsync(t => t.Table2Id); // 给视图实体赋值关联数据 foreach (var view in viewList) { table1Dict.TryGetValue(view.Table1Id, out view.Table1); table2Dict.TryGetValue(view.Table2Id, out view.Table2); } return viewList; } // 使用方式 var viewsWithData = await context.ExampleKeylessUnionView .LoadRelatedDataAsync(context);
2. 给视图添加伪主键,转为有键实体
如果可以修改数据库视图的定义,给视图添加一个唯一标识作为主键(比如复合主键或生成唯一列),这样就能把视图配置成有键实体,正常使用导航属性:
修改视图SQL(以SQL Server为例)
CREATE VIEW ExampleUnionView AS SELECT Table1Id, Table2Id, -- 用Table1Id和Table2Id组合成复合主键(注意处理0的情况) CONCAT(ISNULL(Table1Id, 0), '_', ISNULL(Table2Id, 0)) AS ViewId, -- 其他字段 ... FROM ( SELECT Table1Id, 0 AS Table2Id, ... FROM Table1 UNION ALL SELECT 0 AS Table1Id, Table2Id, ... FROM Table2 ) AS UnionData
修改实体类配置
// 去掉[Keyless]特性,添加主键 public class ExampleUnionView { [Key] public string ViewId { get; set; } public int Table1Id { get; set; } public int Table2Id { get; set; } [ForeignKey(nameof(Table1Id))] public virtual Table1 Table1 { get; set; } [ForeignKey(nameof(Table2Id))] public virtual Table2 Table2 { get; set; } } // Table1和Table2的导航属性也能正常配置 public class Table1 { [Key] public int Table1Id { get; set; } [InverseProperty(nameof(ExampleUnionView.Table1))] public virtual ExampleUnionView View { get; set; } }
3. 使用投影查询直接返回关联数据
不需要给视图实体加导航属性,查询时直接通过Join关联表,投影到DTO或匿名类型中:
// 定义DTO存储视图+关联数据 public class ViewWithRelatedDataDto { public int Table1Id { get; set; } public int Table2Id { get; set; } public Table1 Table1 { get; set; } public Table2 Table2 { get; set; } } // 查询时直接关联 var query = from view in context.ExampleKeylessUnionView join t1 in context.Table1 on view.Table1Id equals t1.Table1Id into t1Group from t1 in t1Group.DefaultIfEmpty() // 左连接处理Table1Id=0的情况 join t2 in context.Table2 on view.Table2Id equals t2.Table2Id into t2Group from t2 in t2Group.DefaultIfEmpty() select new ViewWithRelatedDataDto { Table1Id = view.Table1Id, Table2Id = view.Table2Id, Table1 = t1, Table2 = t2 }; var result = await query.ToListAsync();
4. 反向导航属性的处理
对于Table1和Table2中的View导航属性,同样可以通过批量查询赋值实现:
// 批量给Table1列表赋值对应的视图数据 var table1List = await context.Table1.ToListAsync(); var viewMap = await context.ExampleKeylessUnionView .Where(v => table1List.Select(t => t.Table1Id).Contains(v.Table1Id)) .ToDictionaryAsync(v => v.Table1Id); foreach (var t1 in table1List) { viewMap.TryGetValue(t1.Table1Id, out t1.View); }
内容的提问来源于stack exchange,提问作者alrightgame
相关产品推荐
相关产品推荐

