0

私はベストプラクティスでコーディングしようとしていますが、ここで疑問があります。これを WebForms でテストしています。

ユーザーを RepositoryLayer に渡すメソッドがある UserService Layer があります。

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 が返されます。

データベースの往復を節約するために、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 コンテナーを前提としていますが、コンテナーなしで実装できます。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 に答える