0

我正在尝试使用最佳实践进行编码,但我对此有疑问。我正在 WebForms 上对此进行测试。

我有一个 UserService 层,其中我有一个将用户传递给 RepositoryLayer 的方法:

public AddUserResponse AddUserResponse(AddUserRequest addUserRequest)         
{
  AddUserResponse response = new AddUserResponse();
  User objUser = new User();

  objUser.Names = addUserRequest.Names;
  objUser.LastName = addUserRequest.LastName;
  objUser.Email  = addUserRequest.Email;
  objUser.Alias  = addUserRequest.Alias;
  objUser.Profile.IdProfile = addUserRequest.Profile.IdProfile;
  objUser.Password = addUserRequest.Password;
  objUser.Active = addUserRequest.Active;

  short OperationState=_userRepository.Add(objUser);        
  if (OperationState==0)  
  {
    response.State=true;
    response.Message="User inserted";
  }
  else if (OperationState==2)
  {
    response.State=false;
    response.Message="Alias or Email already exist. Cannot insert User";
  }
  else 
  {
    response.State=false;
    response.Message="Error in User insertion";
  }       

  return response;           
}

然后我有一个 UserRepository 层,其中我有一个添加来自我的服务层的用户的功能:

public short Add(User objUser)
    { ...  return OperationState  }

如图所示,此函数依靠存储过程调用来插入用户记录。如果用户电子邮件或别名不存在,则插入并返回 0,如果存在则返回 2,如果操作失败则返回 1。

我在一次调用中执行检查和插入以节省数据库往返。

我是否以正确的方式对我的服务和存储库类执行检查?或者如果不是,我应该如何抽象逻辑以让系统确定何时是重复用户?我应该使用模型或服务来放置验证逻辑并在发生这种情况时引发自定义异常吗?

非常感谢您的洞察力。

更新

出于普遍的兴趣,我现在发布如何在我的应用程序上实现这一点,一旦我得到 Jason 的 IoC 解决方案,我也会对此进行更新。

型号类:

using ABC.DEF.Infrastructure.Domain;

namespace ABC.DEF.Model
{   
    public class AliasOrEmailAreUnique
    {
        private readonly IRepository<User, int> repository;

        public AliasOrEmailAreUnique(IRepository<User,int> repository) 
        {
            this.repository = repository;
        }

        //If the user is added has Id 0 so search among all the existing users for one that could have the alias or email registered already    
        //else if the user is being edit then search among all the user except the user with such Id(itself)
        public bool IsBroken(User model) 
        {
            if (model.IdUser == 0)
            {
                return (
                repository.List().Where(x => x.Alias == model.Alias).Any()
                || repository.List().Where(x => x.Email == model.Email).Any()
                );
            }
            else 
            {
                return (
                repository.List().Where(x => x.Alias == model.Alias && x.IdUser != model.IdUser).Any()                    
                || repository.List().Where(x => x.Email == model.Email && x.IdUser != model.IdUser).Any()
                );
            }


        }

        public ErrorMessage ErrorMessage
        { 
          get { return new ErrorMessage { Property = "AliasEmail", Message = "Alias or Email exists already" }; } 
        }
    }
}

服务等级:

using ABC.DEF.Repository;
using ABC.DEF.Model;
using ABC.DEF.Service.Messaging.User;

namespace ABC.DEF.Service
{
    public class UsuarioService
    {
        public AddUserResponse AddUserResponse(AddUserRequest addUserRequest)         
        {
            AddUserResponse response = new AddUserResponse();
            User objUser = new User();

            objUser.Names = addUserRequest.Names;
            objUser.LastName = addUserRequest.LastName;
            objUser.Email  = addUserRequest.Email;
            objUser.Alias  = addUserRequest.Alias;
            objUser.Profile.IdProfile = addUserRequest.Profile.IdProfile;
            objUser.Password = addUserRequest.Password;
            objUser.Active = addUserRequest.Active;


            //Determine if the Alias or Email are unique
            Model.AliasOrEmailAreUnique aliasOrEmailAreUnique = new Model.AliasOrEmailAreUnique(_userRepository);

            if (!aliasOrEmailAreUnique.IsBroken(objUser))
            {
            _usuarioRepository.Add(objUser);
            response.State = true;
            response.Message = "User added succesfully";
            }
            else
            {
            response.State = false;
            response.Message = aliasOrEmailAreUnique.ErrorMessage.Message;
         }

         return response;           
      }
   }

}
4

1 回答 1

2

我喜欢在工作单元开始时验证输入。对于 Web 应用程序,请求是工作单元。在触发控制器操作之前,我验证用户输入。行动本身就是“幸福的道路”。如果能做到这一点,我知道我的手术会成功。在请求(响应)的最后,我将任何更改提交回数据库。

我还想保持我的操作明确,因此添加实体的调用与编辑实体的调用与删除实体的调用不同。

在您的场景中,您有一个服务层而不是控制器操作,但过程仍然相同。在调用服务层之前验证模型。然后将模型传递给服务层以执行您想要的操作。

...更新 1...

回应您在下面的评论......

我一直只在服务层调用我的存储库

小心不要落入认为有一个用于拨打电话的线性模式的陷阱。通过应用程序。而是将其视为具有多层的洋葱或球体。

该模型只是一个 POCO/DTO。将有其他组件负责验证模型。通常我有一个看起来像这样的业务规则引擎......从我的头顶上写下来。

interface IRule<T>
{
     bool IsBroken(T model);
     ErrorMessage Message {get;}
}

interface IRulesEngine
{
     IEnumerable<ErrorMessage> Validate<T>(T model);
}

class ErrorMessage
{
      public string Property {get;set;}
      public string Message {get;set;}
}

class RulesEngine : IRulesEngine
{
     private readonly IContainer container;
     public RulesEngine(IContainer container)
     {
          this.container = container;
     }

     public IEnumerable<ErrorMessage> Validate<T>(T model)
     {
           return container
                     .GetInstances<IRule<T>>()
                     .Where(rule => rule.IsBroken(model))
                     .Select(rule =>  rule.Message);
     }
}

此实现假设一个 IoC 容器,但可以在没有 IoC 容器的情况下实现。a 规则可能看起来像这样

class NameIsUnique<MyClass> : IRule<MyClass>
{
     private readonly IRepository<TheEntity> repository;

     public NameIsUnique<MyClass>(IRepository<TheEntity> repository)
     {
         this.repository = repository;
     }

     public bool IsBroken(MyClass model)
     {
          return repository.Where(x => x.Name == model.Name).Any();
     }

     public ErrorMessage 
     { 
         get { return new ErrorMessage { Property = "Name", Message = "Name is not unique" }; } 
     }
}

最后,如何使用它。

var errors = engine.Validate(model);
LoadErrorsInToView(errors);
if(errors.Any()) return;

//call service to process the happy path...

...更新 2...

首先我们重构我们的接口

//this is just a marker interface. don't directly imeplement this.
interface IRule
{
}

interface IRule<T> : IRule
{
    bool IsBroken(T model);
    ErrorMessage Message {get;}
}

class RulesEngine : IRulesEngine
{
    public reasdonly ICollection<IRule> Rules = new List<IRule>();

    public IEnumerable<ErrorMessage> Validate<T>(T model)
    {
       return Rules
                 .Where(x => typeof(IRule<T>).IsAssignableFrom(x.GetType()))
                 .Cast<IRule<T>>()
                 .Where(rule => rule.IsBroken(model))
                 .Select(rule =>  rule.Message);
    }
}
于 2012-11-14T22:25:49.670 回答