Я создал настраиваемый MappedEntityDomainManager для сопоставления объектов данных с DTO. За последнюю пару дней я пытаюсь выяснить в TableController, почему он выбрасывает исключение StackOverflowException. Это единственный MappedEntityDomainManager, который я до сих пор тестировал AutoMapper.Пользовательский MappedEntityDomainManager throws StackOverflowException
Объект данных и модель DTO.
public class PostDto : EntityData
{
public PostDto()
{
User = new UserDto();
PhotoUrls = new HashSet<PostPhotoUrlDto>();
}
public DateTimeOffset DatePosted { get; set; }
public string StatusText { get; set; }
public int TypeOfPost { get; set; }
public UserDto User { get; set; }
public ICollection<PostPhotoUrlDto> PhotoUrls { get; set; }
}
public class Post : EntityData
{
public string UserId { get; set; }
public DateTimeOffset DatePosted { get; set; }
public string StatusText { get; set; }
public PostType TypeOfPost { get; set; }
public virtual User User { get; set; }
public virtual ICollection<PostPhotoUrl> PhotoUrls { get; set; }
}
DomainManager.
public class PostDtoToPostMappedEntityDomainManager : MappedEntityDomainManager<PostDto, Post>
{
public PostDtoToPostMappedEntityDomainManager(DbContext context, HttpRequestMessage request, ApiServices services)
: base(context, request, services)
{
}
public PostDtoToPostMappedEntityDomainManager(DbContext context, HttpRequestMessage request, ApiServices services, bool enableSoftDelete)
: base(context, request, services, enableSoftDelete)
{
}
public override IQueryable<PostDto> Query()
{
return base.Query();
}
public override SingleResult<PostDto> Lookup(string id)
{
return this.LookupEntity(model => model.Id == id);
}
public override Task<PostDto> UpdateAsync(string id, Delta<PostDto> patch)
{
return base.UpdateEntityAsync(patch, id);
}
public override Task<bool> DeleteAsync(string id)
{
return base.DeleteItemAsync(id);
}
}
Конфигурации картографирования.
cfg.CreateMap<Post, PostDto>()
.ForMember(postDto => postDto.TypeOfPost, map => map.MapFrom(post => (int) post.TypeOfPost))
.ForMember(postDto => postDto.User, map => map.MapFrom(post => post.User))
.ForMember(postDto => postDto.PhotoUrls, map => map.MapFrom(post => post.PhotoUrls));
cfg.CreateMap<PostDto, Post>()
.ForMember(post => post.TypeOfPost, map => map.MapFrom(postDto => postDto.TypeOfPost))
.ForMember(post => post.User, map => map.MapFrom(postDto => postDto.User))
.ForMember(post => post.PhotoUrls, map => map.MapFrom(postDto => postDto.PhotoUrls));
В PostController.
// GET tables/Post
public IQueryable<PostDto> GetAllPost()
{
return Query();
}
Исключение выбрасывается в методе PostDtoToPostMappedEntityDomainManager
public override IQueryable<PostDto> Query()
.
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
{Cannot evaluate expression because the current thread is in a stack overflow state.}
Если изменить метод запроса(), чтобы выбрать мой новый DTO вручную он работает отлично, я просто не могу понять, что нужно AutoMapper в этой точке.
public override IQueryable<PostDto> Query()
{
MobileServiceContext ctx = this.Context as MobileServiceContext;
return ctx
.Posts
.Include(post => post.User)
.Include(post => post.PhotoUrls).AsNoTracking()
.ToArray()
.Select(x => new PostDto
{
Id = x.Id,
DatePosted = x.DatePosted,
StatusText = x.StatusText,
TypeOfPost = (int)x.TypeOfPost,
User = new UserDto
{
Id = x.User.Id,
FirstName = x.User.FirstName
},
PhotoUrls = new List<PostPhotoUrlDto>()
{
new PostPhotoUrlDto() { Url = "AURL" }
}
}).AsQueryable();
}
EDIT
После whitelatino
ответа я начал комментировать навигационные свойства и пытаемся вещи, оканчивающихся с решением. Проблема была более глубокой в отношениях с круговой зависимостью в District => Clubs => District, см. Ниже настройки.
public class PostEntityTypeConfiguration : EntityTypeConfiguration<Post>
{
public PostEntityTypeConfiguration()
{
// Properties
HasRequired(post => post.User);
HasMany(post => post.PhotoUrls).WithRequired().HasForeignKey(ph => ph.PostId);
}
}
public class UserEntityTypeConfiguration : EntityTypeConfiguration<User>
{
public UserEntityTypeConfiguration()
{
// Properties
HasOptional(user => user.Club);
HasMany(user => user.Posts).WithRequired().HasForeignKey(post => post.UserId).WillCascadeOnDelete(false);
}
}
public class ClubEntityTypeConfiguration : EntityTypeConfiguration<Club>
{
public ClubEntityTypeConfiguration()
{
// Properties
HasMany(club => club.Members).WithOptional(user => user.Club).HasForeignKey(user => user.ClubId).WillCascadeOnDelete(false);
HasRequired(club => club.ClubDistrict); // Left this as is
}
}
public class DistrictEntityTypeConfiguration : EntityTypeConfiguration<District>
{
public DistrictEntityTypeConfiguration()
{
// Properties
// HasMany(district => district.Clubs).WithRequired().HasForeignKey(club => club.DistrictId).WillCascadeOnDelete(false); // I had to comment this and remove the navigation list property "Clubs" from the District data object.
}
}
Отсутствие EntityTypeConfiguration для PostPhotoUrlDto.
public class PostPhotoUrl : EntityData
{
public string PostId { get; set; }
public string Url { get; set; }
public virtual Post Post { get; set; }
}
public class Club : EntityData
{
// Properties
public virtual District ClubDistrict { get; set; }
public virtual ICollection<User> Members { get; set; }
}
public class User : EntityData
{
// Properties
public virtual Club Club { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public User()
{
Posts = new HashSet<Post>();
}
}
public class District : EntityData
{
// Properties
// public virtual ICollection<Club> Clubs { get; set; } // Had to comment this, it is causing a stackoverflow because of the circular dependency
}
Мне действительно не нужен доступ к свойству списка навигации, так что это решение моей проблемы. Также я вижу, что отношение схемы базы данных создается соответствующим образом.
Замечательно, что мне пришлось изменить общедоступный метод IQueryable<PostDto> GetAllPost()
на IEnumerable<PostDto> GetAllPost()
, чтобы правильно получить JSON с включенными навигационными свойствами. См. Ниже полученный окончательный метод.
// GET tables/Post
public async Task<IEnumerable<PostDto>> GetAllPost()
{
return await Query().ToListAsync();
}
Это была проблема с более глубокими навигацией свойств отношений. Я обновил свой вопрос со всеми подробностями для других, которые могут столкнуться с одной и той же проблемой в немного более сложной схеме, чем базовая. –