从数据库获取结果时防止循环引用
Prevent circular references when getting result from database
如果我有以下简单模型:
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
的列表,它一直重复,直到我们得到堆栈溢出。
映射器非常简单:
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;
}
}
有没有办法阻止这些引用被解析?
您愿意改变您的数据模型吗?我认为最好的解决方案是删除循环引用。
如果公司包含一个用户列表,User 是否还需要包含他的 CompanyId
和 Company
对象?我将从您的 CompanyUser
对象中删除 public Company Company { get; set; }
并从您的 User
对象中删除 Companies
。
有多种解决方案:
1) 您可以使用自动映射器代替自己的映射器。它有 MaxDepth 属性 可以防止这个问题:
Mapper.CreateMap<Source, Destination>().MaxDepth(1);
2) 您可以从实体中删除依赖项并在一个方向上使用影子属性。
您的问题是您正在映射到 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
映射中映射关系,根据 ID 连接关系。
如果我有以下简单模型:
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
的列表,它一直重复,直到我们得到堆栈溢出。
映射器非常简单:
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;
}
}
有没有办法阻止这些引用被解析?
您愿意改变您的数据模型吗?我认为最好的解决方案是删除循环引用。
如果公司包含一个用户列表,User 是否还需要包含他的 CompanyId
和 Company
对象?我将从您的 CompanyUser
对象中删除 public Company Company { get; set; }
并从您的 User
对象中删除 Companies
。
有多种解决方案:
1) 您可以使用自动映射器代替自己的映射器。它有 MaxDepth 属性 可以防止这个问题:
Mapper.CreateMap<Source, Destination>().MaxDepth(1);
2) 您可以从实体中删除依赖项并在一个方向上使用影子属性。
您的问题是您正在映射到 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
映射中映射关系,根据 ID 连接关系。