代码之家  ›  专栏  ›  技术社区  ›  veggerby

ASP.NET MVC和NHibernate关联

  •  1
  • veggerby  · 技术社区  · 16 年前

    假设我有一门课,例如:

    public class Person
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual int SpouseId { get; set; }
        public virtual Person Spouse { get; set; }
    }
    

    我已经包含spouseID,因为这是使用(try)updateModel(基于 ScottGu's sample MVC 2.0 code here )在我的nHibernate映射中,我将spouseID设置为insert=false和update=false,并且我的映射工作正常。

    我要在ASP.NET MVC(2)中有一个“创建人”窗体,然后使用spouseID选择配偶:

    <%
    using (Html.BeginForm()) 
    {
    %>
        <%= Html.LabelFor(m => m.Name) %>
        <%= Html.TextBoxFor(m => m.Name) %>
        <%= Html.ValidationMessageFor(m => m.Name) %>
    
        <%= Html.LabelFor(m => m.SpouseId) %>
        <%= Html.EditorFor(m => m.SpouseId, "PersonPicker") %>
        <%= Html.ValidationMessageFor(m => m.SpouseId) %>
    <%
    }
    %>
    

    在这里,我有一个“人员选取器”的编辑模板,它只显示一个基于视图数据中人员列表的下拉列表。

    然后,当我执行TryUpdateModel()时,它工作正常,我会像这样填充我的值 预期 也就是说,SpouseID被设置了,但配偶没有。当然,问题是,NHibernate使用的是人.配偶。

    我肯定更愿意完全跳过SpouseID,因为它扰乱了我的模型,而且它只是因为这个问题才存在。

    我当然可以 person.Spouse = myService.Find<Person>(person.SpouseId); 但它有一种非常,非常难闻的气味。

    最好的方法是什么?最好完全删除SpouseID属性。

    (我可以补充一下,我使用的是VS2008和.NET 3.5)

    2 回复  |  直到 15 年前
        1
  •  0
  •   veggerby    16 年前

    这是我迄今为止所做的工作,但我不认为它是完美的,因此我怀疑它是否是最好/正确的方法。

    我不知道ModelBinder的方式,创建了一个类似以下的ModelBinder:

    public class MyModelBinder : DefaultModelBinder
    {
        public MyModelBinder(IService service)
        {
            this.Service = service; 
        }
    
        private IService Service
        {
            get;
            set;
        }
    
        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            var modelType = bindingContext.ModelType;
    
            if (/* determine if modelType is a type for association */)
            {
                var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                var id = (int?)value.ConvertTo(typeof(int));
                var result = id.HasValue ? this.Service.Find(modelType, id.Value) : null;
    
                if (result != null)
                {
                    return result;
                }
            }
    
            return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        }
    }
    

    除了判断类型是否“包含”的逻辑之外,我觉得这很好。

    繁琐的部分来自于在global.asax中注册绑定器(使用IOC的结构图):

    ModelBinders.Binders[typeof(Person)] = ObjectFactory.GetInstance<MyModelBinder>();
    

    我要为所有类型做这个。它是有效的,比我以前的解决方案更好,但是它能更好吗?

        2
  •  0
  •   veggerby    15 年前

    好吧,我真正,真正的结果是使用 AutoMapper 以及一个视图模型:

    public class PersonViewModel 
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int SpouseId { get; set; }    
    }
    

    以及填充视图模型:

    // set once for app Mapper.CreateMap<Person, PersonViewModel>();
    var personViewModel = Mapper.Map<Person, PersonViewModel>(person);
    

    要重新填充域:

    // set once for app 
    // Mapper.CreateMap<PersonViewModel, Person>()
    //    .ForMember(x => x.Id, o => o.Ignore())
    //    .ForMember(x => x.Spouse, o => o.MapFrom(x => PersonService.Find(x.SpouseId)));
    var viewModel = new PersonViewModel();
    if (TryUpdateModel(viewModel)) 
    {
        var person = PersonService.Find(viewModel.Id);
        Mapper.Map(personViewModel, person);
    }
    

    我唯一不喜欢的是配偶的“反向映射”,因为这需要一个个人服务(例如通过DI/IOC),但是除了这个之外它还可以工作。

    对于这个简单的示例,automapper可能是杀伤力过大的,如果我们不为“保存回域模型”使用automapper,那么在映射配置中就不会有personservice依赖性(但是在action方法中,这可能会“违反”dry)。