EF Code Firstを使用して、お互いのコレクションを持つクラスを作成しました。エンティティ:
public class Field
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<AppUser> Teachers { get; set; }
public Field()
{
Teachers = new List<AppUser>();
}
}
public class AppUser
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public virtual List<Field> Fields { get; set; }
public AppUser()
{
Fields = new List<FieldDTO>();
}
}
DTO:
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<AppUserDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<AppUserDTO>();
}
}
public class AppUserDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
マッピング:
Mapper.CreateMap<Field, FieldDTO>();
Mapper.CreateMap<FieldDTO, Field>();
Mapper.CreateMap<AppUserDTO, AppUser>();
Mapper.CreateMap<AppUser, AppUserDTO>();
そして、私はこのコードを呼び出すときにStackOverflowExceptionを取得しています(コンテキストは私のdbContextです):
protected override IQueryable<FieldDTO> GetQueryable()
{
IQueryable<Field> query = Context.Fields;
return query.ProjectTo<FieldDTO>();//exception thrown here
}
これは、Lists内で無限に相互に呼び出してループするために起こると思います。しかし、私はこれがなぜ起こるのか分かりません。私のマッピングは間違っていますか?
自己参照エンティティと自己参照DTOがあります。一般的に、自己参照DTOは悪い考えです。特にプロジェクションを行う場合-EFは、アイテムの階層を結合および結合し、結合する方法を知りません。
2つの選択肢があります。
まず、階層を念頭に置いてDTOを明示的にモデリングすることにより、特定の階層の深さを強制できます。
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<TeacherDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<TeacherDTO>();
}
}
public class TeacherDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
}
public class AppUserDTO : TeacherDTO
{
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
これは最も明白で明示的であるため、好ましい方法です。
あまり明確ではなく、あまり明確ではない方法は、AutoMapperが階層関係をトラバースする最大深度を持つように構成することです。
CreateMap<AppUser, AppUserDTO>().MaxDepth(3);
最も簡単に理解できるので、私は#1に行くことを好みますが、#2も同様に機能します。
他のオプションはPreserveReferences()メソッドを使用しています。
CreateMap<AppUser, AppUserDTO>().PreserveReferences();
私はこの一般的な方法を使用します:
public static TTarget Convert<TSource, TTarget>(TSource sourceItem)
{
if (null == sourceItem)
{
return default(TTarget);
}
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var serializedObject = JsonConvert.SerializeObject(sourceItem, deserializeSettings);
return JsonConvert.DeserializeObject<TTarget>(serializedObject);
}