【问题标题】:How should I report a duplicated business entity/object on service layer?我应该如何在服务层报告重复的业务实体/对象?
【发布时间】:2012-11-14 22:18:08
【问题描述】:

我正在尝试使用最佳实践进行编码,但我对此有疑问。我正在 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;           
      }
   }

}

【问题讨论】:

    标签: c# asp.net service repository design-patterns


    【解决方案1】:

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

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

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

    ...更新 1...

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

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

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

    模型只是一个 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);
        }
    }
    

    【讨论】:

    • 嗨 Jason,我只在服务层调用我的存储库,为了对模型执行验证以查看我的用户是否已经存在,我需要调用模型中的存储库接口。这个对吗?。你介意按照你建议的做法编写一段代码来知道我做得对吗?如何在模型中设置响应消息?谢谢。
    • 验证表单和逻辑现在更有意义,我会给你答案,但我仍然不确定如何实现它,因为现在我不熟悉 IoC 和引擎。验证( ) 我页面上的函数调用仍然在它们上。
    • 我将更新我的答案以包括没有 IoC 的情况。
    猜你喜欢
    • 2011-06-01
    • 2014-08-08
    • 2011-01-22
    • 2013-07-07
    • 2017-11-04
    • 2011-03-09
    • 1970-01-01
    • 2013-11-27
    • 2016-08-10
    相关资源
    最近更新 更多