自动映射:希望通过接口映射重用代码

时间:2012-02-01 15:00:16

标签: c# asp.net-mvc linq automapper

我正在构建CMS之类的应用程序。

例如,我的BlogPost页面包含多个小部件区域。每个小部件都有一系列“相关”博客文章。

typical widget

我的所有观点都是纯粹的表现形式,我构建网址,将日期时间和内容转换为服务层中的字符串。我发现这种方法更容易维护,因为视图具有zer0逻辑。所有逻辑都整合到AutoMapper的解析器,转换器和自定义转换逻辑中。

让我们更接近手头的问题。 要创建网址,我需要2个参数:BlogId和BlogSlug,我的网址看起来像b / {id} / {slug} .html 我很高兴。

在我的CSM中,我使用了所谓的“源模型”,这是一种不是视图模型的模型,而是它的中间表示。为什么我不得不诉诸这种邪恶的解决方案? 好吧,让我们看一下典型的数据检索代码在我的项目中的样子:

.Select(x => 
{
    Id = x.Id,
    BlogId = x.Blog.Id,
    BlogSlug = x.Blog.Slug,

    // Here is the trap, LINQ provider will throw an exception, since he doesn't know how to translate function into expression
    BlogUrl = Url.Action("RenderPost", "BlogController", new { Id = x.Blog.Id, slug = x.Blog.Slug }) 
}

所以这不是一个选择。 幸运的是,我们可以做到这一点

.Select(x => new
{
    Id = x.Id,
    BlogId = x.Blog.Id,
    BlogSlug = x.Blog.Slug
}
.ToList()
.Select(x => new
{
    // This works
    BlogUrl = Url.Action("RenderPost", "BlogController", new { Id = x.BlogId, slug = x.BlogSlug })
}

将这些东西复制粘贴到每个呈现不同“有趣的博客”部分的动作方法中(它们具有不同的视觉表现形式以及不能使用相同的视图模型)?不是一个好方法,所以我提出了一个解决方案。 我创建了“源模型”,因此代码将是

.Select(x => new BlogPostSourceViewModel
{
    Id = x.Id,
    BlogId = x.Blog.Id,
    BlogSlug = x.Blog.Slug
}
.ToList()
.Select(x => x.ToBlogPostViewModel())  // Extension method { return Mapper.Map<>() }
.ToList();

这肯定看起来更好,但我有许多不同的模型,如BlogPostSourceViewModel,BlogAuthorSourceViewModel,BlogCommentSourceViewModel。他们都需要这个链接构建逻辑。 好的,我将所需的源数据(BlogId,BlogSlug)提取到界面

BlogPostSourceViewModel : IBlogPostUrl
BlogAuthorSourceViewModel: IBlogPostUrl
BlogCommentSourceViewModel : IBlogPostUrl

然后我定义映射

Mapper.CreateMap<BlogPostSourceViewModel, BlogPostViewModel>
    .ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Mapper.CreateMap<BlogAuthorSourceViewModel, BlogAuthorViewModel>
    .ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Mapper.CreateMap<BlogCommentSourceViewModel, BlogCommentViewModel>
    .ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())

解析器:

BlogPostUrlResolver : ValueResolver<IBlogPostUrl, String>
// Here goes the url building logic

正如您所看到的,我需要更多的模型需要博客网址,我必须添加更多相同的映射。现在可以,但随着项目的增长,这将是痛苦的。 理想情况下,我希望这样:

Mapper.CreateMap<IBlogPostUrl, SomeOtherInterfaceWithBlogUrlAsString>
    .ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())

但Automapper不理解它。而且我不知道如果有其他方法可以做到这一点。

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

如果我正确理解了这个问题......

为了在所有类中使用AutoMapper映射功能,您可以提供将使用泛型的基类:

public abstract class Base<Entity, ViewModel>   
    where Entity : EntityObject         
    where ViewModel : BaseViewModel     
    {
        // you will call this method from your operations class using base
        public SomeViewModel GetData()
        {
            public Entity entityObject = db.Entity.SingleOrDefault();
            public ViewModel yourViewModelName = base.Map(entityObject);

            return yourViewModelName;
        }
        ....
        // this will be defined only once for each mapping direction
        // ie. there will be multiple of these.
        public static Entity Map(ViewModel typeViewModel)
        {
            try
            {
                Mapper.CreateMap<ViewModel, Entity>();
                Entity t = Mapper.Map<ViewModel, Entity>(typeViewModel);

                return t;
            }
            catch (Exception exc)
            {
                throw exc;
            }
        }
    }

// this will be your class for database operations on specific Entity
// that inherits generic base, with its AutoMapping setup.
public class DataBaseOperationsClass : Base<SomeEntity, SomeViewModel>
{
    public SomeViewModel Get()
    { 
        return base.GetData();
    }
}


希望这有帮助!

相关问题