Entity Frameworkを使用していますが、ブラウザーに親データと子データを取得する際に問題があります。私のクラスは次のとおりです。
public class Question
{
public int QuestionId { get; set; }
public string Title { get; set; }
public virtual ICollection<Answer> Answers { get; set; }
}
public class Answer
{
public int AnswerId { get; set; }
public string Text { get; set; }
public int QuestionId { get; set; }
public virtual Question Question { get; set; }
}
次のコードを使用して、質問と回答のデータを返します。
public IList<Question> GetQuestions(int subTopicId, int questionStatusId)
{
var questions = _questionsRepository.GetAll()
.Where(a => a.SubTopicId == subTopicId &&
(questionStatusId == 99 ||
a.QuestionStatusId == questionStatusId))
.Include(a => a.Answers)
.ToList();
return questions;
}
C#側ではこれは機能しているように見えますが、回答オブジェクトには質問への参照があります。 WebAPIを使用してブラウザにデータを取得すると、次のメッセージが表示されます。
'ObjectContent`1'タイプは、コンテンツタイプ 'application/jsonの応答本文のシリアル化に失敗しました。 charset = utf-8 '。
タイプ「Models.Core.Question」のプロパティ「question」の自己参照ループが検出されました。
これは、質問に回答があり、回答に質問への参照があるためですか?私が見たすべての場所は、子供の親への参照を持つことを示唆しているので、私は何をすべきかわかりません。誰かが私にこれに関するいくつかのアドバイスを与えることができます。
これは、質問に回答があり、回答に質問への参照があるためですか?
はい。シリアル化できません。
編集:Tallmarisの答えとOttOのコメントを参照してください。より簡単で、グローバルに設定できます。
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
旧回答:
EFオブジェクトQuestion
を独自の中間体またはDataTransferObjectに投影します。その後、このDtoを正常にシリアル化できます。
public class QuestionDto
{
public QuestionDto()
{
this.Answers = new List<Answer>();
}
public int QuestionId { get; set; }
...
...
public string Title { get; set; }
public List<Answer> Answers { get; set; }
}
何かのようなもの:
public IList<QuestionDto> GetQuestions(int subTopicId, int questionStatusId)
{
var questions = _questionsRepository.GetAll()
.Where(a => a.SubTopicId == subTopicId &&
(questionStatusId == 99 ||
a.QuestionStatusId == questionStatusId))
.Include(a => a.Answers)
.ToList();
var dto = questions.Select(x => new QuestionDto { Title = x.Title ... } );
return dto;
}
OWINを使用している場合は、これ以上GlobalSettingsを使用しないでください。 IAppBuilder UseWebApi関数(または使用しているサービスプラットフォーム)に渡されるHttpConfigurationオブジェクトのこの同じ設定を変更する必要があります。
このように見えるでしょう。
public void Configuration(IAppBuilder app)
{
//auth config, service registration, etc
var config = new HttpConfiguration();
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//other config settings, dependency injection/resolver settings, etc
app.UseWebApi(config);
}
ASP.NET Coreでは、修正は次のとおりです。
services
.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
DNX/MVC 6/ASP.NET vNext blah blahを使用している場合、HttpConfiguration
でさえ欠落しています。 Startup.cs
ファイルで次のコードを使用してフォーマッターを構成する必要があります。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().Configure<MvcOptions>(option =>
{
//Clear all existing output formatters
option.OutputFormatters.Clear();
var jsonOutputFormatter = new JsonOutputFormatter();
//Set ReferenceLoopHandling
jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
//Insert above jsonOutputFormatter as the first formatter, you can insert other formatters.
option.OutputFormatters.Insert(0, jsonOutputFormatter);
});
}
ASP.NET Core Web-API(.NET Core 2.0):
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<MvcJsonOptions>(config =>
{
config.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
}
これを使用して:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
うまくいかなかった。代わりに、テストするためにモデルクラスの新しい単純化されたバージョンを作成しましたが、正常に戻りました。この記事では、EFでうまく機能したが、シリアライズ可能ではなかった私のモデルで抱えていた問題のいくつかについて説明します。
http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4
.Net Framework 4.5を使用する新しいAsp.Net Webアプリケーションの場合:
Web API:Go_App_Start-> WebApiConfig.cs:
次のようになります。
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// ReferenceLoopHandling.Ignore will solve the Self referencing loop detected error
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//Will serve json as default instead of XML
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
ReferenceLoopHandling.Ignoreは機能しませんでした。私がやり遂げることができる唯一の方法は、コードを介して不要な親へのリンクを削除し、私がしたものを保持することでした。
parent.Child.Parent = null;
ASP.NET Core 2.2では、上記の回答の構成はどれも機能しませんでした。
仮想ナビゲーションプロパティにJsonIgnore
属性を追加しました。
public class Question
{
public int QuestionId { get; set; }
public string Title { get; set; }
[JsonIgnore]
public virtual ICollection<Answer> Answers { get; set; }
}
遅延読み込みのため、このエラーが発生しています。したがって、私の提案は、プロパティから仮想キーを削除することです。 APIを使用している場合、遅延読み込みはAPIの健全性に適していません。
構成ファイルに余分な行を追加する必要はありません。
public class Question
{
public int QuestionId { get; set; }
public string Title { get; set; }
public ICollection<Answer> Answers { get; set; }
}
public class Answer
{
public int AnswerId { get; set; }
public string Text { get; set; }
public int QuestionId { get; set; }
public Question Question { get; set; }
}
エンティティdb = new Entities()
db.Configuration.ProxyCreationEnabled = false;
db.Configuration.LazyLoadingEnabled = false;
このエラーは、既存のデータベースのedmx(概念モデルを定義するXMLファイル)を生成したときに発生し、親テーブルと子テーブルの両方へのナビゲーションプロパティがあったことがわかりました。子にナビゲートしたいだけなので、親オブジェクトへのすべてのナビゲーションリンクを削除し、問題は解決しました。
この問題を簡単に回避するために、新しい子コレクションを動的に作成できます。
public IList<Question> GetQuestions(int subTopicId, int questionStatusId)
{
var questions = _questionsRepository.GetAll()
.Where(a => a.SubTopicId == subTopicId &&
(questionStatusId == 99 ||
a.QuestionStatusId == questionStatusId))
.Include(a => a.Answers).Select(b=> new {
b.QuestionId,
b.Title
Answers = b.Answers.Select(c=> new {
c.AnswerId,
c.Text,
c.QuestionId }))
.ToList();
return questions;
}