如何在使用AutoMapper/AutoMapViewResult时将下拉列表的数据导入viewmodel

dav*_*ser 10 asp.net-mvc viewmodel automapper drop-down-menu

在阅读了ASP.NET MVC 2 in Action并观看Jimmy Bogard的MvcConf演讲(强烈推荐!)之后,我开始实现他们的一些想法.

他们做的很酷的事情之一,不仅是使用AutoMapper将您的实体映射到某个视图模型,而且还使用AutoMapViewResult自动执行此操作:

public class EventsController : BaseController
{
    public ActionResult Show(Event id) // EntityModelBinder gets Event from repository
    {
        return AutoMapView<EventsShowModel>(id); // AutoMapView<T>(model) is a helper method on the BaseController, that calls AutoMapViewResult<T>(...)
    }
}

// not exactly what you'll find in the book, but it also works :-)
public class AutoMapViewResult<TDestination> : ViewResult
{
    public AutoMapViewResult(string viewName, string masterName, object model)
    {
        ViewName = viewName;
        MasterName = masterName;

        ViewData.Model = Mapper.Map(model, model.GetType(), typeof(TDestination));
    }
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好,但现在有一个Edit动作EventsEditModel:

public class EventsEditModel
{
    // ... some properties ...
    public int LocationId { get; set; }
    public IList<SelectListItem> Locations { get; set; }
}

public class EventsController : BaseController
{
    public ActionResult Edit(Event id)
    {
        return AutoMapView<EventsEditModel>(id); 
    }
}
Run Code Online (Sandbox Code Playgroud)

现在(最后)问题:

你觉得,是要得到某种数据源的位置,最好的办法,如仓库到EventsEditModelLocations财产?

请记住,我想使用AutoMapViewResult和许多不同的实体 - 视图模型组合.

更新:

我选择了Necros的想法并创建了一个自定义属性.您可以查看代码并将其下载到我的博客ASP.NET MVC:使用属性将选择列表的数据加载到编辑模型中.

Ben*_*ter 6

我的解决方案是引入模型丰富器的概念,简单的类在将模型传递给View()之前"丰富"模型:

public class SiteSettingsModelEnricher : IModelEnricher<SiteSettingsModel>
{
    private readonly IThemeProvider themeProvider;
    public SiteSettingsModelEnricher(IThemeProvider themeProvider) {
        this.themeProvider = themeProvider;
    }

    public SiteSettingsModel Enrich(SiteSettingsModel model) {
        var themes = from t in themeProvider.GetThemes()
                     select new SelectListItem { Text = t, Value = t };
        model.Themes = themes;

        return model;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的AutoMapperViewResult ExecuteResult方法看起来像:

    public override void ExecuteResult(ControllerContext context) {

        var model = Mapper.Map(this.Model, typeof(TSource), typeof(TDestination)) as TDestination;

        // enrichers
        var enricher = DependencyResolver.Current.GetService<IModelEnricher<TDestination>>();
        if (enricher != null) {
            model = enricher.Enrich(model);
        }

        this.ViewData.Model = model;
        base.ExecuteResult(context);
    }
Run Code Online (Sandbox Code Playgroud)

由于我还使用了Jimmy演示文稿中的FormActionResult,我还在返回Failure结果之前使用了richr.这意味着像选择列表这样的东西被重新绑定并保持超级干燥.

[更新]

我在这里发布了一个基于上述内容的改进解决方案.


Nec*_*ros 5

当我需要时,我还没有达到这一点(因为我看到了谈话),但我有一个可能的解决方案.我认为它可以创建一个属性,指定需要加载此属性.我将从一个抽象类开始:

public abstract class LoadDataAttribute : Attribute
{
    public Type Type { get; set; }

    protected LoadDataAttribute(Type type)
    {
        Type = type;
    }

    public abstract object LoadData();
}
Run Code Online (Sandbox Code Playgroud)

然后为要加载的每种类型创建特定版本(在您的案例中为位置)

public class LoadLocationsAttribute : LoadDataAttribute
{
    public LoadLocationsAttribute() : base(typeof(IList<SelectListItem>))

    public override object LoadData()
    {
        // get locations and return IList<SelectListItem>
    }
}
Run Code Online (Sandbox Code Playgroud)

在你ExecuteResultAutoMappViewResult,你会发现所有的属性LoadDataAttribute,调用LoadData(),投它在属性类型规定,并将其分配给属性.

我的情况你只想用这种方式加载选择列表,你可以只返回IList<SelectListItem>而不是object,并节省自己一些麻烦铸造.

您的视图模型显然会使用该属性.

public class EventsEditModel
{
    // ... some properties ...
    public int LocationId { get; set; }

    [LoadLocations]
    public IList<SelectListItem> Locations { get; set; }
}
Run Code Online (Sandbox Code Playgroud)