假设我有Patient
和Cycle
域实体,我通过EF加载:
public class Patient
{
public int PatientId { get; set; }
public string Name { get; set; }
public List<Cycle> Cycles { get; set; }
public Patient()
{
Cycles = new List<Cycle>();
}
}
public class Cycle
{
public int CycleId { get; set; }
public int PatientId { get; set; }
public bool IsActive { get; set; }
public Patient Patient { get; set; }
}
如您所见,Patient
具有用于Cycles
集合的导航属性,Cycles
的导航属性引用回到Patient
。
以前,我已经使用过AutoMapper,但希望完全控制我的域实体 - 视图模型映射,因此我正在创建自定义映射器。这是Patient
的一个:
public class PatientMapper : IPatientMapper
{
private readonly ICycleMapper cycleMapper;
public PatientMapper(ICycleMapper cycleMapper)
{
this.cycleMapper= cycleMapper;
}
public PatientViewModel GetViewModel(Patient patient)
{
if (patient == null)
{
return null;
}
var viewModel = new PatientViewModel();
viewModel.PatientId = patient.PatientId;
viewModel.Name = patient.Name;
viewModel.Cycles = patient.Cycles.Select(x => cycleMapper.GetViewModel(x)).ToList();
return viewModel;
}
}
如您所见,我需要注入CycleMapper
。在CycleMapper
中,我需要注入PatientMapper
的实例来映射Patient
导航属性。这会引起环状DI问题。
过去,我通过创建每个实体的"基本"版本来解决此问题。例如,BasicCycle
将没有Patient
导航属性。这有效,但需要更多的实体,映射器等。
有更好的方法吗?
,因此您在这里有2个问题:构造函数中的递归依赖注入和递归映射。解决递归di-有几种选择。首先是将对象工厂而不是对象本身传递到构造函数。AutoFac本地支持:
private readonly Func<IPatientMapper> patientMapper;
public CycleMapper(Func<IPatientMapper> patientMapper)
{
this.patientMapper = patientMapper;
}
这样,您将依赖的构建延迟到需要,从而解决问题。
替代方法 - 使用属性注入而不是构造仪注入:
public IPatientMapper PatientMapper { get; set; }
和这样的注册(所有映射器):
builder.RegisterType<CycleMapper>().As<ICycleMapper>().SingleInstance().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
现在解决第一个问题时 - 如果您尝试用周期映射患者(因为自行车回来引用患者),则会存在堆栈溢出异常。解决此问题的最简单方法是跟踪已经映射的内容(再次,对于所有映射者,而不仅仅是一个映射者):
public interface IPatientMapper {
PatientViewModel GetViewModel(Patient patient, IDictionary<object, object> map = null);
}
public interface ICycleMapper {
CycleViewModel GetViewModel(Cycle cycle, IDictionary<object, object> map = null);
}
public class CycleMapper : ICycleMapper
{
public IPatientMapper PatientMapper { get; set; }
public CycleViewModel GetViewModel(Cycle cycle, IDictionary<object, object> map = null)
{
if (cycle == null) {
return null;
}
// If called without map - create new one
if (map == null)
map = new Dictionary<object, object>();
// if we already mapped this cycle before - don't do this again
// and instead return already mapped entity
if (map.ContainsKey(cycle))
return (CycleViewModel)map[cycle];
var viewModel = new CycleViewModel();
viewModel.PatientId = cycle.PatientId;
viewModel.CycleId = cycle.CycleId;
viewModel.IsActive = cycle.IsActive;
// add this entity to map before calling any other mappers
map.Add(cycle, viewModel);
// pass map to other mapper
viewModel.Patient = PatientMapper.GetViewModel(cycle.Patient, map);
return viewModel;
}
}
,当您需要映射某些内容时,您只需致电
var model = mapper.GetViewModel(patient);
像往常一样,不再关心循环依赖。