NET核心&;EntityFramework核心:Linq中的左(外)连接
我正在尝试使用ASP.NET Core和EntityFramework Core在Linq中实现左连接 有两个表的简单情况:NET核心&;EntityFramework核心:Linq中的左(外)连接,linq,asp.net-core,entity-framework-core,Linq,Asp.net Core,Entity Framework Core,我正在尝试使用ASP.NET Core和EntityFramework Core在Linq中实现左连接 有两个表的简单情况: 个人(身份证、名、姓) 个人详细信息(id、个人id、详细文本) 我尝试查询的数据是Person.id、Person.firstname、Person.lastname和PersonDetails.DetailText。 有些人没有DetailText,因此想要的结果为空 在SQL中,它工作得很好 SELECT p.id, p.Firstname, p.Lastname
- 个人(身份证、名、姓)
- 个人详细信息(id、个人id、详细文本)
SELECT p.id, p.Firstname, p.Lastname, d.DetailText FROM Person p
LEFT JOIN PersonDetails d on d.id = p.Id
ORDER BY p.id ASC
结果如预期:
# | id | firstname | lastname | detailtext
1 | 1 | First1 | Last1 | details1
2 | 2 | First2 | Last2 | details2
3 | 3 | First3 | Last3 | NULL
在我的Web API控制器中,我查询:
[HttpGet]
public IActionResult Get()
{
var result = from person in _dbContext.Person
join detail in _dbContext.PersonDetails on person.Id equals detail.PersonId
select new
{
id = person.Id,
firstname = person.Firstname,
lastname = person.Lastname,
detailText = detail.DetailText
};
return Ok(result);
}
在swagger中的结果是失踪人员3(那些没有详细文本)
我在林克做错了什么
更新1: 谢谢你迄今为止的答案和链接 我使用
将下面的代码复制并粘贴到
和.DefaultIfEmpty()
中,在进一步阅读之后,我明白这应该是可行的
不幸的是,事实并非如此
首先,代码开始抛出异常,但仍然返回前两个结果(缺少null)。从输出窗口复制粘贴:
System.NullReferenceException: Object reference not set to an instance of an object.
at lambda_method(Closure , TransparentIdentifier`2 )
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
at Microsoft.AspNetCore.Mvc.Formatters.JsonOutputFormatter.WriteObject(TextWriter writer, Object value)
at Microsoft.AspNetCore.Mvc.Formatters.JsonOutputFormatter.d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__31.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__29.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__23.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.ApplicationInsights.AspNetCore.ExceptionTrackingMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.ApplicationInsights.AspNetCore.RequestTrackingMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.d__2.MoveNext()
Microsoft.AspNetCore.Server.Kestrel:Error: Connection id "0HKVGPV90QGE0": An unhandled exception was thrown by the application.
System.NullReferenceException: Object reference not set to an instance of an object.
at lambda_method(Closure , TransparentIdentifier`2 )
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
at Microsoft.AspNetCore.Mvc.Formatters.JsonOutputFormatter.WriteObject(TextWriter writer, Object value)
at Microsoft.AspNetCore.Mvc.Formatters.JsonOutputFormatter.d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__31.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__29.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__23.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.ApplicationInsights.AspNetCore.ExceptionTrackingMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.ApplicationInsights.AspNetCore.RequestTrackingMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.d__2.MoveNext()
谷歌给了我一个:
以及
现在我不确定这是否是某个仍然存在或应该已经修复的bug。我正在使用EntityFramework核心RC2
解决方案1:导航属性 正如Gert Arnold在评论中指出的:使用导航属性 这意味着(工作)查询看起来就像
var result = from person in _dbContext.Person
select new
{
id = person.Id,
firstname = person.Firstname,
lastname = person.Lastname,
detailText = person.PersonDetails.Select(d => d.DetailText).SingleOrDefault()
};
return Ok(result);
在我的PersonExampleDB中,我没有正确设置外键,因此属性PersonDetails
不在脚手架模型类中。但是使用这个方法是最简单的解决方案(而且可以工作,甚至工作得很快),而不是现在的连接(参见bug报告)
当连接方式工作一次时,仍然对更新感到满意。如果需要执行左连接,则必须使用
进入和DefaultIfEmpty()
,如下所示
var result = from person in _dbContext.Person
join detail in _dbContext.PersonDetails on person.Id equals detail.PersonId into Details
from m in Details.DefaultIfEmpty()
select new
{
id = person.Id,
firstname = person.Firstname,
lastname = person.Lastname,
detailText = m.DetailText
};
您可以了解更多信息:如果您不使用左连接,那么您使用的linq基本上是创建一个内部连接。对于linq中的左连接,请使用into关键字
[HttpGet]
public IActionResult Get()
{
var result = from person in _dbContext.Person
join detail in _dbContext.PersonDetails on person.Id equals detail.PersonId
into Details
from defaultVal in Details.DefaultIfEmpty()
select new
{
id = person.Id,
firstname = person.Firstname,
lastname = person.Lastname,
detailText = defaultVal.DetailText
};
return Ok(result);
}
我同意这篇文章的作者——它看起来仍然像一个bug!如果有空的联接表,则始终会收到“对象引用未设置为对象的实例”。唯一的方法是将联接表检查为null:
IEnumerable<Models.Service> clubServices =
from s in services
from c in clubs.Where(club => club.ClubId == s.ClubId).DefaultIfEmpty()
from t in clubs.Where(tenant => tenant.TenantId == c.TenantId).DefaultIfEmpty()
select new Models.Service
{
ServiceId = s.ServiceId.ToString(),
ClubId = c == null ? (int?)null : c.ClubId,
ClubName = c == null ? null : c.Name,
HasTimeTable = s.HasTimeTable,
MultipleCount = s.MultipleCount,
Name = s.Name,
Tags = s.Tags.Split(';', StringSplitOptions.RemoveEmptyEntries),
TenantId = t == null ? (int?)null : t.TenantId,
TenantName = t == null ? null : t.Name
};
IEnumerable clubServices=
来自服务行业的
从clubs.Where中的c开始(club=>club.ClubId==s.ClubId).DefaultIfEmpty()
来自clubs.Where中的t(tenant=>tenant.TenantId==c.TenantId).DefaultIfEmpty()
选择新型号。服务
{
ServiceId=s.ServiceId.ToString(),
ClubId=c==null?(int?)null:c.ClubId,
ClubName=c==null?null:c.名称,
HastinMetable=s.HastinMetable,
MultipleCount=s.MultipleCount,
Name=s.Name,
Tags=s.Tags.Split(“;”,StringSplitOptions.RemoveEmptyEntries),
TenantId=t==null?(int?)null:t.TenantId,
租户名称=t==null?null:t.名称
};
我无法选中“detailText=person.PersonDetails.Select(d=>d.detailText.SingleOrDefault()
”,因为我的联接表位于不同的数据库中。为什么要联接?使用导航属性。这是我第一次使用linq。我对一切有用的东西都开放:-)@GertArnold谢谢你的链接。我读过了。因为我使用了数据库优先的方法并构建了模型类,所以我具有导航属性。但我不知道这如何帮助我检索丢失的条目。介意再给我一个提示吗?那么你有一个财产人。个人信息?新的错误表明你可能在选择
之前的某个地方有一个ToList
(或可计算的
)。into在EF Core 3中不起作用。EF Core 3不允许客户端执行。您好。“网络链接没有任何结果。”ValterEkholm现在提交了一份官方文件。
IEnumerable<Models.Service> clubServices =
from s in services
from c in clubs.Where(club => club.ClubId == s.ClubId).DefaultIfEmpty()
from t in clubs.Where(tenant => tenant.TenantId == c.TenantId).DefaultIfEmpty()
select new Models.Service
{
ServiceId = s.ServiceId.ToString(),
ClubId = c == null ? (int?)null : c.ClubId,
ClubName = c == null ? null : c.Name,
HasTimeTable = s.HasTimeTable,
MultipleCount = s.MultipleCount,
Name = s.Name,
Tags = s.Tags.Split(';', StringSplitOptions.RemoveEmptyEntries),
TenantId = t == null ? (int?)null : t.TenantId,
TenantName = t == null ? null : t.Name
};