2015-05-02 3 views
1

Я создал настраиваемый 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(); 
     } 

Исключение выбрасывается в методе PostDtoToPostMappedEntityDomainManagerpublic 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(); 
     } 

ответ

0

Ошибка stackoverflow возникает из-за рекурсивного цикла, то есть двух свойств навигации, ссылающихся друг на друга. У меня была такая же проблема во многих отношениях. Я установил ее, удалив свойство навигации (список) на одном из объектов, и вместо этого сделал многие ко многим картографирования с:

modelBuilder.Entity<Event>().HasMany(m => m.HostList).WithMany();

+0

Это была проблема с более глубокими навигацией свойств отношений. Я обновил свой вопрос со всеми подробностями для других, которые могут столкнуться с одной и той же проблемой в немного более сложной схеме, чем базовая. –

Смежные вопросы