Предотвращение циклических ссылок при получении результата из базы данных - PullRequest
1 голос
/ 05 апреля 2019

Если у меня есть следующая простая модель:

    public class Company
    {
        public Guid CompanyId { get; set; }

        public ICollection<CompanyUser> Users { get; set; }
    }

    public class CompanyUser
    {
        public Guid CompanyId { get; set; }

        public Guid UserId { get; set; }

        public Company Company { get; set; }

        public User User { get; set; }
    }

    public class User
    {
        public Guid UserId { get; set; }

        public ICollection<CompanyUser> Companies { get; set; }
    }

Чтобы получить список компаний + их пользователей + объект пользователя, я запускаю следующий запрос:

return await _dataContext.Companies
    .Include(m => m.Users)
    .ThenInclude(m => m.User)
    .OrderBy(m => m.Name)
    .ToListAsync();

Результаты работают, но я использую картограф для сопоставления результатов с моделью представления, проходя рекурсивно через модель.

В результате получается, что объект Company имеет ссылку на список CompanyUser, в каждом из этих CompanyUser объектов у нас есть Company, который снова имеет список CompanyUser, который просто сохраняет повторяя, пока мы не получим переполнение стека.

enter image description here

Карта очень проста:

var results = companies.ToViewModel<Company, CompanyViewModel>();
public static IList<TModel> ToViewModel<TEntity, TModel>(this IEnumerable<TEntity> entities)
    where TEntity : class
    where TModel : class, IViewModel<TEntity>, new()
{
    return entities?.Select(entity => entity.ToViewModel<TEntity, TModel>()).ToList();
}

public static TModel ToViewModel<TEntity, TModel>(this TEntity entity)
    where TEntity : class
    where TModel : class, IViewModel<TEntity>, new()
{
    if (entity == null)
    {
        return null;
    }

    var model = new TModel();
    model.ToViewModel(entity);
    return model;
}
public interface IViewModel<in TEntity>
    where TEntity : class
{
    void ToViewModel(TEntity entity);
}

public class CompanyViewModel : IViewModel<Company>
{
    public Guid CompanyId { get; set; }

    public IList<CompanyUserViewModel> Users { get; set; }

    public void ToViewModel(Company entity)
    {
        CompanyId = entity.CompanyId;
        Users = entity.Users.ToViewModel<CompanyUser, CompanyUserViewModel>();
    }
}

public class CompanyUserViewModel : IViewModel<CompanyUser>
{
    public Guid CompanyId { get; set; }

    public Guid UserId { get; set; }

    public CompanyViewModel Company { get; set; }

    public UserViewModel User { get; set; }

    public void ToViewModel(CompanyUser entity)
    {
        CompanyId = entity.CompanyId;
        UserId = entity.UserId;
        Company = entity.Company.ToViewModel<Company, CompanyViewModel>();
        User = entity.User.ToViewModel<User, UserViewModel>();
    }
}

public class UserViewModel : IViewModel<User>
{
    public Guid UserId { get; set; }

    public void ToViewModel(User entity)
    {
        UserId = entity.Id;
    }
}

Есть ли способ предотвратить разрешение этих ссылок?

Ответы [ 3 ]

1 голос
/ 05 апреля 2019

Есть несколько решений:

1) Вы можете использовать autopper вместо собственного mapper. У него есть свойство MaxDepth, которое предотвращает эту проблему:

Mapper.CreateMap<Source, Destination>().MaxDepth(1);

2) Вы можете удалить зависимости от ваших сущностей и использовать свойства тени в одном направлении.

1 голос
/ 05 апреля 2019

Ваша проблема в том, что вы отображаете на CompanyViewModel, который затем сопоставляется с CompanyUserViewModel, но затем снова отображается на CompanyViewModel, что создает бесконечный цикл.

Если вы планируете всегда начинать с Company (до CompanyView), то удалите рекурсию обратно из CompanyUserViewModel.

public void ToViewModel(CompanyUser entity)
{
    CompanyId = entity.CompanyId;
    UserId = entity.UserId;
    // Company = entity.Company.ToViewModel<Company, CompanyViewModel>();
    User = entity.User.ToViewModel<User, UserViewModel>();
}

В качестве альтернативы не отображайте отношения в вашем отображении ToViewModel, подключите отношения после того, как они основаны на идентификаторах.

1 голос
/ 05 апреля 2019

Готовы ли вы изменить свою модель данных? Я думаю, что лучшим решением было бы удалить круговую ссылку.

Если в компании есть список пользователей, нужен ли пользователю и объект CompanyId, и объект Company, в котором он содержится? Я бы удалил public Company Company { get; set; } из вашего CompanyUser объекта и Companies из вашего User объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...