EF Entities vs. Service Models vs. View Models (MV

2019-01-16 19:33发布

I'm trying to understand and figure good practices for designing your app/domain models (POCOs/DTOs).

Let's say I have the following database table, Account:

UserID int
Email varchar(50)
PasswordHash varchar(250)
PasswordSalt varchar(250)

Of course, EF4 would build the entity like so:

public class Account
{
    public int UserID { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string PasswordSalt { get; set; }
}

Now, let's say I have a view model for registering a new user, which may look something like so:

public class RegistrationViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

Lastly, I have a service which needs to register the user:

public class RegistrationService
{
    public void RegisterUser(??? registration)
    {
        // Do stuff to register user
    }
}

I'm trying to figure out what to pass into the RegisterUser method. The view model is, of course, located under my web app (presentation layer), so I do not want this getting passed to my service.

So, I'm thinking one of four possibilities:

1) Set up a service model that is similar, if not identical, to the RegistrationViewModel, and use this:

public class RegistrationServiceModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(RegistrationServiceModel registration)
    {
        // Do stuff to register user
    }
}

2) Set up an interface of the model, and inherit this in my view model, and set up my method to accept the interface:

public interface IRegistrationModel
{
    string Email;
    string Password;
}

public class RegistrationServiceModel : IRegistrationModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(IRegistrationModel registration)
    {
        // Do stuff to register user
    }
}

3) Pass in the Account entity, doing the RegistrationViewModel-to-Account mapping in my controller:

public class RegistrationService
{
    public void RegisterUser(Account account)
    {
        // Do stuff to register user
    }
}

4) Move my view model out of the presentation into a domain/service layer, and pass that into the service method:

public class RegistrationService
{
    public void RegisterUser(RegistrationViewModel account)
    {
        // Do stuff to register user
    }
}

None of these three scenarios seem ideal, as I see problems in each of them. So I'm wondering if there's another method I can't think of.

What are good practices for this?

Thanks in advance.

3条回答
贼婆χ
2楼-- · 2019-01-16 19:54

You never pass a view model to the service. A service doesn't even know about the existence of a view model that you might have defined in your presentation tier. A service works with domain models.
Use Auto mapper to map between view model and domain model and vice versa.

Personally, I've never heard of service models in DDD (view models for services).

查看更多
爷、活的狠高调
3楼-- · 2019-01-16 19:57

In this case it makes perfect sense to use a DTO (Data Transfer Object). You can create an AccountDto class at the service layer and use it to pass the registration data down to the service. It might be similar to the ViewModel in some cases, but generally you can show much more in your View than is required to create a user. To further illustrate the point, your ViewModel will probably at least look something like this:

public class RegistrationViewModel
{
    [Required]
    public string Email { get; set; }

    [Required]
    public string Password { get; set; } 

    [Required]
    [Compare("Password")]
    public string RepeatPassword { get; set; } 
}

While your DTO will only require the Email and Password properties.

public class AccountDto
{
    public string Email { get; set; }
    public string Password { get; set; }
}

So as you see, the ViewModel only contains data needed for the View. The email validation and password comparison logic happens in your Web layer. You use the DTO to get get only email and password to the Service. And then at the service layer you hash the password, populate your Entity object and persist the values to the database.

查看更多
萌系小妹纸
4楼-- · 2019-01-16 20:06

Use 3rd option, for sure. As šljaker said, Service should be unaware of presentation part of application (which your ViewModel is a part of).

Sure, as well, don't overcomplicate things around by including tons of transition models like RegistrationServiceModel or - even worse - IRegistrationModel (last one will lead to "interface explosion" one day).

So:

  1. Have a Domain entity (POCO entity that is persisted with Entity Framework or NHibernate or NoRM or whatever).
  2. Have a ViewModel that represents your domain model in given context. Don't hesitate to make a ViewModel per Controller Action if necessary. The side-effect benefit of strict ViewModels (those which are 1:1 with your View) is complete absence of over-posting and under-posting problems. It depends on your concrete situation/taste though.
  3. Use DataAnnotation attributes with your ViewModels to provide basic validation (remember to validate business rules too but it should sit behind the wire - inside Services/Repositories layer).
  4. Don't let App Service ever know about ViewModels. Create a domain entity instance and feed it to the Service instead (to validate/persist).
  5. Use AutoMapper as an option to quicky map from your domain entities to ViewModels.
  6. Map from incoming ViewModel or FormCollection to your entity in either Controller action or custom IModelBinder.
  7. (Optionally) I'd recommend to follow the Thunderdome Principle. It's a really really convenient usage of ViewModels.
查看更多
登录 后发表回答