Asp.net mvc 使用Automapper展平和派生属性

Asp.net mvc 使用Automapper展平和派生属性,asp.net-mvc,entity-framework,design-patterns,domain-driven-design,automapper,Asp.net Mvc,Entity Framework,Design Patterns,Domain Driven Design,Automapper,我在一个ASP.NETMVC网站上工作,使用EF4和Automapper。我正在取得良好的进展,并且对设计感觉相当好(考虑到这是我第一次尝试真正的DDD架构)。但我开始看到一些我不太适应的事情 我的问题可能更多地与我缺乏Automapper和DDD的经验有关,所以你们中的一些人可能很容易回答这些问题,并为我指出正确的方向。我也很有可能完全错了,但我的方向是基于我在这里阅读的内容,一些来自搜索,一些来自问我自己的问题。但我也开始意识到我看到的一些“建议”是相互矛盾的 快速背景,我的域模型只是从EF

我在一个ASP.NETMVC网站上工作,使用EF4和Automapper。我正在取得良好的进展,并且对设计感觉相当好(考虑到这是我第一次尝试真正的DDD架构)。但我开始看到一些我不太适应的事情

我的问题可能更多地与我缺乏Automapper和DDD的经验有关,所以你们中的一些人可能很容易回答这些问题,并为我指出正确的方向。我也很有可能完全错了,但我的方向是基于我在这里阅读的内容,一些来自搜索,一些来自问我自己的问题。但我也开始意识到我看到的一些“建议”是相互矛盾的

快速背景,我的域模型只是从EF4 POCO模板(贫血)生成的POCO。这些在我的服务层中使用,它们也通过服务层公开给我的应用程序。我没有任何类型的DTO,只有我的贫血领域模型、视图模型和自动映射器来映射这两个。假设它们可以一对一地映射,这一切都很好

当我必须将域模型扁平化为一个视图模型时,或者当我需要一些业务逻辑来定义视图模型属性时,就会出现问题

两个例子:

展平

我有两个模型,User和UserProfile。这是一个一对一的映射,所以它在逻辑上是平坦的——它只是在两个独立的模型中。我的视图模型需要在一个对象中同时具有User和UserProfile属性。据我所见,Automapper没有简单的方法来实现这一点,因此我所做的是扩展我的用户POCO,向其添加所需的UserProfile属性:

public string UserName
{
    get { return UserProfile.UserName; }
}
我不是一个大风扇,因为它似乎违反干,并可能成为一个有点痛苦,我越要这样做

封装业务逻辑

我还有一个例子,用户属性不是存储的,而是派生的,在返回值之前需要进行一些逻辑处理。例如,个人资料图像URL将取决于他们是在Facebook还是Twitter上注册的。所以我有这样的想法:

public string ProfileImageUrl
{
    get
    {
        if (User.TwitterId != null)
        {
            // return Twitter profile image URL
        }
        if (User.FacebookId != null)
        {
            // return Facebook profile image URL
        }
    }
}
我很确定这不是Automapper负责的事情,但我不确定如果我想让它保持贫血,是否应该通过域模型扩展来完成。所以我不确定这是属于哪里的

我并不完全认为我的域模型是贫乏的(我知道这是它自己的争论),但我确实预期这个域模型将被具有不同视图模型和验证的多个应用程序使用

先谢谢你

更新:作为对@jfar的回应,我对扁平化示例的主要问题是,这似乎应该是Automapper应该能够做到的。如果没有,那么我可以接受这个选择。我只是想找个人来仔细检查我的设计,以确保没有更好的方法来实现这一点

封装业务逻辑示例的问题是,我违反了我的域模型。在某些情况下,这甚至可能需要访问存储库以获取用于业务逻辑的特定数据,对我来说,这似乎是一个糟糕的设计


我开始怀疑的另一件事是,我是否不应该有DTO层,但我也不想走这条路(根据我上面链接的问题,不使用DTO w/DDD似乎更容易被接受)。

定义一个模型,重新组合这两个模型:

public class UserWithProfile
{
    public User User { get; set; }
    public UserProfile Profile { get; set; }
}
然后让您的服务层返回此模型。如果用户和用户配置文件之间存在1对1映射,则另一种可能性是定义以下模型:

public class UserWithProfile: User
{
    public UserProfile Profile { get; set; }
}
然后定义仅包含给定视图所需内容的视图模型:

public class SomeUserViewModel
{
    ... only properties needed by the given view
}
接下来定义模型和视图模型之间的映射:

Mapper.CreateMap<UserWithProfile, SomeUserViewModel>()
      ...
Mapper.CreateMap()
...
最后,在您的控制器中,使用服务层获取模型,将其映射到视图模型,并将此视图模型传递到视图进行渲染,这是非常标准的东西

  • 展平:您可以使用Automapper映射到现有对象的功能将映射链接在一起。
  • 封装业务逻辑:我真的看不出你的问题。但是,如果您想让Automapper负责,请查看前后映射或自定义解析器
    为什么你如此专注于保持你的领域模型贫血?在我看来,您的域对象只不过是DTO

    ProfileImageUrl
    属于您的域类,该域类具有返回正确URL所需的信息。我想这是
    UserProfile
    。如果单个类没有所需的信息,这就是服务的用途。您可以在服务上创建一个返回URL的方法,或者像Darin Dimitrov建议的那样创建一个复合对象


    Firestrand和Darin Dimitrov的答案都是实现扁平化的好方法。

    我搜索并阅读了更多关于扁平化的内容,搜索的越多,看起来Automapper应该自动处理这个问题。在玩了一会儿之后,我找到了一种方法来实现这一点,尽管我仍然找不到任何这样做的例子,即使有Automapper的文档

    关键是用源对象图的完全限定名命名目标对象上的属性

    源对象:

    class User
    {
        int Id { get; set; }
        FacebookUser FacebookUser { get; set; }
    }
    
    class FacebookUser
    {
        string UserName { get; set; }
    }
    
    目标对象:

    class UserViewModel
    {
        int Id { get; set; }
        string FacebookUserUserName { get; set; }
    }
    
    因此:


    也许这对我来说是显而易见的,也许对大多数人来说也是如此。现在我想起来了,这是有意义的——这是唯一可行的方法。直到现在我才明白。

    你能总结一下你的问题吗?我看不出你做的有什么不对劲。是的,我看不太清楚。我已经补充了我原来的问题。非常感谢你的回答。但问题是,SomeUserViewModel是否有另一个名为Profile的UserProfile类型的属性?如果是,这是不可能的
    UserViewModel.Id -> User.Id
    UserViewModel.FacebookUserUserName -> User.FacebookUser.UserName