【问题标题】:Find entry in one table to search in another [duplicate]在一个表中查找条目以在另一个表中搜索 [重复]
【发布时间】:2019-11-16 01:03:14
【问题描述】:

我正在尝试使用 Entity Framework Core 和 ASP.NET Core 制作 Web API

我有一个用户资料表(称为featUsers)和一个场景表(称为场景)。首先,我想根据一个字段找到一个用户,然后返回与该用户关联的场景列表。

返回所有场景都可以正常工作:

// GET: api/Scenarios
[HttpGet]
public async Task<ActionResult<IEnumerable<Scenario>>> Getscenarios()
{
    return await _context.scenarios.ToListAsync();
}

修改它以测试搜索和返回具有特定外键的场景,也可以正常工作:

// GET: api/Scenarios
[HttpGet]
public async Task<ActionResult<IEnumerable<Scenario>>> Getscenarios()
{
    return await _context.scenarios.Where(scenario => scenario.FeatUserId == 1).ToListAsync();
}

但是当我进一步修改它以首先找到用户配置文件,然后使用该配置文件来查找场景时,事情就失败了。这是函数的样子:

// GET: api/Scenarios
[HttpGet]
public async Task<ActionResult<IEnumerable<Scenario>>> Getscenarios()
{
    //Find the profile, with specific Identity ID
    var featProfile = _context.featUsers.FirstOrDefault(u => u.IdentityId == "44fc0698-9f99-46dd-bfac-db1781fd8b01");

    //Debug
    Console.WriteLine(featProfile.FeatUserId);

    //Return all the scenarios which are related to that profile above
    return await _context.scenarios.Where(scenario => scenario.FeatUserId == featProfile.FeatUserId).ToListAsync();
}

当我使用 Postman 查询此端点时(Console.WriteLine 正确地将“1”写入终端,正如预期的那样,但是)这是我在 Postman 中得到的响应:

Newtonsoft.Json.JsonSerializationException: Self referencing loop detected with type 'DAF_FEAT.Models.Scenario'. Path '[0].featUser.scenarios'.
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(JsonWriter writer, Object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, 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 Newtonsoft.Json.JsonSerializer.Serialize(JsonWriter jsonWriter, Object value)
   at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
   at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
   at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events)
   at IdentityServer4.Hosting.MutualTlsTokenEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Cache-Control: no-cache
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate
Host: localhost:5001
User-Agent: PostmanRuntime/7.15.0
Postman-Token: c3ba03bb-8f2f-433a-998a-e13cd361f888

为了完整起见,模型如下所示:

    public class FeatUser
    {
        public int FeatUserId { get; set; }
        public string Role { get; set; }
        public string OrganisationName { get; set; }
        public string PhoneNumber { get; set; }

        public string IdentityId { get; set; }

        public List<Scenario> Scenarios { get; set; }
    }

    public class Scenario
    {
        public int ScenarioId { get; set; }
        public string Name { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateUpdated { get; set; }
        public bool Active { get; set; }

        public int FeatUserId { get; set; }
        public FeatUser FeatUser { get; set; }
    }

也许我做错了,应该通过做更多这样的事情来利用导航属性:

return (await _context.featUsers.Include(u => u.Scenarios).FirstOrDefaultAsync(u => u.IdentityId == userId)).Scenarios.ToList();

【问题讨论】:

  • FeatUser 包含多个Scenario 对象和Scenario 链接到包含多个Scenario 对象的FeatUser。等等等等。您找到了一个很好的理由,为什么您永远不应该将 EF 模型直接返回给您的客户。
  • 啊,当然:services.AddMvc() .AddNewtonsoftJson(options =&gt; options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore); 会解决这个问题。没有意识到我觉得很傻。
  • 不要因为没有意识到而感到愚蠢,这并不那么明显。但也许不谷歌搜索会觉得有点傻:)

标签: c# asp.net-core .net-core entity-framework-core


【解决方案1】:

正如 DavidG 在他的评论中建议的那样,您不应该在您的 api 中返回 EF 模型。您应该为您的模型创建 DTO(数据传输对象)并返回它们。你的 DTO 不应该有自引用循环(FeatUser 包含 Scenario,然后 Scenario 包含 FeatUser)。因此,在您的示例中,您的 DTO 将如下所示:

public class FeatUserDto
{
    public int FeatUserId { get; set; }
    public string Role { get; set; }
    public string OrganisationName { get; set; }
    public string PhoneNumber { get; set; }

    public string IdentityId { get; set; }

    public List<ScenarioDto> Scenarios { get; set; }
}

public class ScenarioDto
{
    public int ScenarioId { get; set; }
    public string Name { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateUpdated { get; set; }
    public bool Active { get; set; }
}

请注意 ScenarioDto 不包含返回 FeatUserDto 的链接以避免自引用循环。

您现在需要将模型映射到 DTO。 Automapper 可以提供帮助。

【讨论】:

  • 您看到了我链接的副本,为什么要添加答案?
  • 我在添加答案后看到了重复的链接。
猜你喜欢
  • 2020-01-08
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 2017-04-28
  • 2016-08-28
  • 1970-01-01
  • 2014-12-21
相关资源
最近更新 更多