自定义 MappedEntityDomainManager 抛出 StackOverflowException

Custom MappedEntityDomainManager throws StackOverflowException

我创建了一个自定义的 MappedEntityDomainManager 来将我的数据对象映射到 DTO。在过去的几天里,我试图在 TableController 中找出它抛出 WhosebugException 的原因。这是迄今为止我唯一用于测试 AutoMapper 的 MappedEntityDomainManager。

数据对象和 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.WhosebugException' occurred in mscorlib.dll

{Cannot evaluate expression because the current thread is in a stack overflow state.}

如果我将 Query() 方法更改为 select 我的新 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();
            }

编辑

回答 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.
        }
    }

PostPhotoUrlDto 没有 EntityTypeConfiguration。

    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 Whosebug because of the circular dependency
    }

我真的不需要访问导航列表 属性 所以这解决了我的问题。我还看到数据库架构关系已正确创建。

值得注意的是,我必须将 public IQueryable<PostDto> GetAllPost() 方法更改为 IEnumerable<PostDto> GetAllPost() 才能正确接收包含导航属性的 JSON。 请参阅下面生成的最终方法。

        // GET tables/Post
        public async Task<IEnumerable<PostDto>> GetAllPost()
        {
            return await Query().ToListAsync();
        }

Whosebugexception 是由递归循环引起的,即两个导航属性相互引用。我在多对多关系中遇到了同样的问题。我通过删除其中一个实体上的导航 属性(列表)来修复它,而是使用以下方法进行多对多映射:

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