我正在构建CMS之类的应用程序。
例如,我的BlogPost页面包含多个小部件区域。每个小部件都有一系列“相关”博客文章。
我的所有观点都是纯粹的表现形式,我构建网址,将日期时间和内容转换为服务层中的字符串。我发现这种方法更容易维护,因为视图具有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不理解它。而且我不知道如果有其他方法可以做到这一点。
有什么想法吗?
答案 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();
}
}
希望这有帮助!