2017-03-14 75 views
2

比方说,我有我通过EF加载PatientCycle域实体:自定义实体 - 视图模型映射器循环依赖

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导航属性。这个工作,但需要更多的实体,映射器等。

有没有更好的方法?

+0

而不是注入实例 - 注入工厂('Func '或自定义工厂)。 – Evk

+0

@Evk感谢您的回复。我没有完全掌握。你可以发布一个kopy.io吗? – im1dermike

+0

您是否需要与其绑定的患者以外的“周期”?如果没有,那么为什么不用CycleMapper.GetViewModel(patient)来更改CycleMapper中的GetViewModel定义。根据我的理解,您的周期只需要一个患者实例来填充它的Patient导航属性。 – Alaminut

回答

0

所以你有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); 

像往常一样,不再关心循环依赖关系。