Частичная проверка модели в сценариях создания / обновления - PullRequest
2 голосов
/ 13 февраля 2012

У меня есть модель для пользователя, сгенерированная EF из существующей базы данных:

public class User
{
 public int Id { get;set; }
 public string Name { get;set; }
 public string Password { get;set; }
 public DateTime Created { get;set; }
 public DateTime LastModified { get;set; }
}

Затем я создавал модель пользователя и применял аннотации данных для добавления проверки.Я использую AutoMapper для перевода между User и UserModel.

Я сейчас пытаюсь создать представления со следующими бизнес-правилами:

  • при создании пользователя, поля Created и LastModifiedне должны появляться в пользовательском интерфейсе, но оба должны быть установлены до сохранения модели в репозитории;
  • при редактировании пользователя поля Created и LastModified не должны появляться в пользовательском интерфейсе, а поле Createdдолжно быть оставлено без изменений, а поле LastModified должно быть обновлено до сохранения модели в хранилище;
  • при редактировании пользователя, если поле пароля не трогается, то поле Пароль в модели не изменяется;если поле пароля содержит значение, которое следует использовать для обновления модели.

Итак, как я могу добиться этого при минимальном дублировании кода?Например, я должен иметь EditUserModel и CreateUserModel, наследуя от базовой UserModel, которая имеет поля, общие для обоих (Id, Name, Password)?Должна ли какая-либо модель иметь ссылку на Created / LastModified?И в частности, как я могу справиться с требованием смены пароля?

Ответы [ 2 ]

2 голосов
/ 13 февраля 2012

Вот как я обычно справляюсь с этой ситуацией. Для модели представления я использую только один, EditUserModel, поскольку поддержка 3 классов не окупается, только 1-2 свойства различны, и, на мой взгляд, модель представления не так важна, я просто быть прагматичным. В вашем случае EditUserModel должен выглядеть примерно так

public class EditUserModel
{
   public int Id {get;set} //used when modifying user
   public string Name {get;set;}
   public string Password {get;set;}
   public string ConfirmPassword {get;set;} //this is optionally
}

Эта модель передается контроллеру, и я лично использую подход DDD (как образ мыслей) и, скажем, модель предметной области, которая выглядит следующим образом (она основана на коде, который я на самом деле использую)

public class Password
{
   public Password(string value,string salt)
   {
      //you can apply basic validation rules if you want\\

      Hash=(salt+value).Sha256(); //a variant of hashing and an extension method
      Salt=salt;
   }
   public string Hash {get;private set;}
   public string Salt {get;private set;} 
}

    public class Member
{
   public Member(string name, Password pwd)
   {
       Name=name;
       Password=pwd;
       Created= DateTime.Now;
    }

    public Member(int id,DateTime created,string name,Password p)
    {
        Id = id;
        Created = created;
        _name = name;
        _password = p;
    }
   public int Id { get; set; } 
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            LastModified = DateTime.Now;
            _name = value;
        }
    }

    private Password _password;
    public Password Password
    {
        get { return _password; }
        set
        {
            _password = value;
            LastModified = DateTime.Now;
        }
    }

    public DateTime Created {get;private set;}
   public DateTime LastModified {get;private set;}
}

Первый раз при создании пользователя

var user= new Member(model.Name,new Password(model.Password,"a salt"));
repository.Save(user);

При обновлении

var user= repository.GetUser(int id);
user.Name=model.Name;
if (!string.IsNullOrEmpty(model.Password)) user.Password= new Password(model.Password,"a salt");
repository.Save(user);

В репозитории (обратите внимание, что у меня нет большого опыта работы с EF, поэтому этот код, безусловно, можно оптимизировать)

public void Save(Member user)
{
using (var dc = new DbContext())
    {

 if (user.Id==0)
  {
     //do insert, all this can be handled via automapper
    var u= new User();
    u.Name=user.Name;
    u.Password=user.Password.Hash;
    u.Created=user.Created;
    u.LastModified=user.LastModifed;  
    dc.Users.Add(u);
    dc.SaveChanges();
    user.Id=u.Id;

  }
else  
 {
    //do edit
    var u= dc.Users.First(d=>d.Id==user.Id);
    //map values \\

    dc.Users.SaveChanges();// EF should detect if something was changed and save only changes
  }
}
}

  public Member GetUser(int id)
   {
     //get User from DbContext \\

      var m= new Member(id,user.Created,user.Name,new Password(user.Password,"my salt")); 
      return m;


    }

Это поспешный код, и все может быть улучшено, но позвольте мне рассказать, почему такой «сложный» подход. Прежде всего, мы имеем более четкое различие между уровнями ответственности, модель представления обрабатывает все необходимое для отображения и отправки данных в контроллер, а модель предметной области не смешивается с моделью постоянства (модель EF). Вы можете довольно легко изменить технику хэширования паролей, и создается / последний измененный материал обрабатывается там, где действительно происходят изменения. Когда вы начнете добавлять больше функциональности, этот дизайн значительно облегчит вам работу.

Основная цель - ясность и удобство обслуживания, и в зависимости от использования вы можете выбрать другой подход, например, обновление пользователя с помощью команд (вы отправляете в хранилище команду для изменения имени или пароля)

Хорошо, я закончил:)

1 голос
/ 13 февраля 2012

Для первых двух вопросов поля Created и LastModified должны быть определены в модели только для чтения или не отображаться вообще, и вы можете использовать триггеры INSERT и UPDATE в своей базе данных для установки их значений.

Другая возможность состоит в том, чтобы переопределить метод SaveChanges в контексте данных и вызвать соответствующий метод для вашей сущности, чтобы установить значения этих свойств непосредственно перед вставкой или обновлением.

при редактировании пользователя, если поле пароля не трогается, то Поле пароля в модели не изменяется; если поле пароля содержит значение, которое следует использовать для обновления модели.

Вы можете попробовать обработать это требование на уровне отображения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...