11

ユーザーが新しいパーツレコードを作成できるようにするWebサイトがあります。特定のフィールドの一意性を検証するための最良の方法を見つけようとしています。その部品番号が別の部品にすでに存在する場合、誰かが部品番号1234の部品を追加しようとしないようにしたいと思います。

Webアプリケーションは、オブジェクトをデータベースにマッピングするために、流暢なnHibernateを備えたAsp.netMVCを使用しています。ValidateNonEmpty、ValidateRangeなどのビューモデルでCastle検証を使用しています。ValidateSelfメソッドを使用してリポジトリにクエリを実行し、そのパーツ番号がすでに存在するかどうかを確認する必要がありますか?ViewModelでリポジトリを使用することについて何かが正しく感じられません。

そのロジックをコントローラーアクションに配置する方がよいでしょうか?私のViewModelはその時点で(ModelBind中に)すでに検証されていると思うので、それは正しくないようです。

または多分それは上記のどれでもありません。これについて助けてくれてありがとう。

更新 わかりました。これが役立つかどうかはわかりませんが、プロジェクトの一般的な作成アクションの保存アクションは次のようになります。

public ActionResult Create(PartViewModel viewModel)
{
 //I think I'd like to know if its Valid by this point, not on _repository.Save
 if(ModelState.IsValid)
 {
    try
    {
        var part = _partCreateViewModelMap.MapToEntity(viewModel);

        _repository.Save(part);
        return Redirect("~/Part/Details/" + part.Id);
    }
    catch (Exception e)
    {
        // skip on down...
    }
 }

 // return view to edit 
 return View(viewModel);
}
4

6 に答える 6

1

私は何度もこの質問を受けました。友達はバリデーターコードからデータアクセスができるか心配していました。答えは簡単です。これを行う必要がある場合は、行う必要があります。通常、抽象化の各レベルでこのようなチェックを行う必要があります。そして、すべてのチェックの後、一意制約違反による例外をキャッチする準備ができているはずです。

于 2009-09-17T20:02:45.557 に答える
1

データベース内に一意の制約を定義する場合、一意の値が既に存在するかどうかを確認する責任をデータベースに委譲してみませんか? NHibernate を使用すると、NHibernate.Exceptions.ISQLExceptionConverterインターフェイスを使用して、制約違反に関連する既知のエラーをキャプチャして変換できます。また、インプリメンターNHibernate.Exceptions.IViolatedConstraintNameExtracter(「参考文献」を参照NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter) を使用して、データベース例外の厄介な詳細を取得し、それをユーザー フレンドリなメッセージに変換し、選択した検証例外として再パッケージ化して、関連するコントローラーでキャッチすることもできます。

私のプロジェクトの1つからの迅速で非常に具体的な迅速で汚い例外コンバーターの例:


Imports NHibernate
Imports NHibernate.Exceptions
Imports System.Data.SqlClient
Imports System.Data.Common

Namespace NHibernate

    Public Class ConstraintViolationExceptionConverter
        Implements ISQLExceptionConverter

        Public Function Convert(ByVal adoExceptionContextInfo As Global.NHibernate.Exceptions.AdoExceptionContextInfo) As System.Exception Implements Global.NHibernate.Exceptions.ISQLExceptionConverter.Convert

            Dim dbEx As DbException = ADOExceptionHelper.ExtractDbException(adoExceptionContextInfo.SqlException)

            If TypeOf dbEx Is SqlException Then
                Dim sqlError As SqlException = DirectCast(dbEx, SqlException)

                Select Case sqlError.Number
                    Case 547
                        Return New ConstraintViolationException(adoExceptionContextInfo.Message, adoExceptionContextInfo.SqlException)

                End Select

            End If

            Return SQLStateConverter.HandledNonSpecificException(adoExceptionContextInfo.SqlException, adoExceptionContextInfo.Message, adoExceptionContextInfo.Sql)

        End Function


    End Class

End Namespace

web.config/nhibernate-configuration/session-factoryプロパティ要素を介して構成されます:


<property name="sql_exception_converter">csl.NHibernate.ConstraintViolationExceptionConverter, csl</property>

編集: NHibernate の最近のバージョンでコンバーター インターフェイスが変更されたことにおそらく言及する必要があります。この例のインターフェイスは NHibernate.dll v2.1.0.4000 からのものです。

于 2009-09-18T16:31:54.170 に答える
1

私は通常、コントローラーとリポジトリーの間にサービスレイヤーを配置します。
次に、サービス層が検証とリポジトリへの呼び出しを処理します。

次に、サービス レイヤーで検証エラーが発生した場合は、カスタム例外をスローし、それをコントローラーでキャッチして、モデルの状態にエラーを挿入します。

于 2009-09-19T15:42:08.203 に答える
0

ご質問に対する回答はありませんが、sharparchitecture.net サイトを確認してください。asp.net mvc と nhibernate のベスト プラクティスが含まれています。また、xval プロジェクトと Data Annotation Validators による検証に関するチュートリアルを確認することをお勧めします。

于 2009-09-17T19:13:27.567 に答える
0

私に適した解決策は次のとおりです。

1.) エンティティが検証作業を実行するのに有効かどうかを尋ねます。
2.) これが完了したら、オブジェクトに有効かどうかを示す何かが必要です (私の場合、「壊れたルール」の CSLA のような概念を使用しています)。
3.) このようなものがある場合、以下に示すように、NHibernate が永続化を試みる前にオブジェクトが有効であることを確認できます。

このアプローチの唯一の問題は、検証が必要な各エンティティにインターフェイスを実装する必要があることです。これを受け入れることができれば、NHibernate が規則に従って無効なオブジェクトの変更を永続化するのを止めることができます。

using System;
using NHibernate;
using NHibernate.Event;
using Validation.Entities.Interfaces;
using Persistence.SessionBuilder;

namespace Persistence.Validation
{
    public class ValidationEventListener : IPreInsertEventListener, IPreUpdateEventListener
    {

        public bool OnPreInsert(NHibernate.Event.PreInsertEvent @event)
        {
            var entityToInsert = @event.Entity as IBusinessBase;

            if (entityToInsert != null)
            {
                if (entityToInsert.BrokenRules != null)
                {
                    RollbackTransactionBecauseTheEntityHasBrokenRules();
                }
            }

            return false;
        }

        public bool OnPreUpdate(NHibernate.Event.PreUpdateEvent @event)
        {
            var entityToUpdate = @event.Entity as IBusinessBase;

            if (entityToUpdate != null)
            {
                if (entityToUpdate.BrokenRules != null)
                {
                    RollbackTransactionBecauseTheEntityHasBrokenRules();
                }
            }

            return false;
        }

        private void RollbackTransactionBecauseTheEntityHasBrokenRules()
        {
            try
            {
                ISession session = SessionBuilderFactory.GetBuilder().CurrentSession;

                if (session != null)
                {
                    session.Transaction.Rollback();
                }
            }
            catch (Exception ex)
            {
                //this will force a rollback if we don't have a session bound to the current context 
                throw new NotImplementedException();
            }
        }
    }
}
于 2009-09-17T20:10:51.113 に答える
0

これはあなたのアーキテクチャにとって重要だと思います。私が過去に行った MVC アプリでは、ドメインの要素を Web の要素から抽象化し、依存性注入を使用してハード依存を回避しています。

モデルをバインドしているときにモデルを検証することになると、ValidateSelf メソッドで、サービス、リポジトリ、またはアーキテクチャの次のものを簡単に使用できます。その依存関係はどうなのかという疑問が生じると思います。

私の記憶が正しければ、依存性注入フレームワークを使用してモデルの作成時に検証に必要なサービスをプラグインする独自のカスタム バインダーを作成し、MVC のデフォルト バインダーを呼び出してオブジェクトを入力し、Castle Validation のフレームワークを呼び出すことができます。検証を行います。これは完全に考え抜かれた解決策ではありませんが、いくつかのアイデアが生まれることを願っています。

于 2009-09-23T00:53:51.657 に答える