Asp.net mvc 使用MVC和fluent Nhibernate,在将ViewModel上的唯一字段绑定到域对象并保存它们之前,如何验证它们?

Asp.net mvc 使用MVC和fluent Nhibernate,在将ViewModel上的唯一字段绑定到域对象并保存它们之前,如何验证它们?,asp.net-mvc,model-view-controller,fluent-nhibernate,repository-pattern,castle,Asp.net Mvc,Model View Controller,Fluent Nhibernate,Repository Pattern,Castle,我有一个允许用户创建新零件记录的网站。我试图找出验证特定字段唯一性的最佳方法。我想确保,如果零件号为1234的零件已存在于其他零件上,则不会有人尝试添加该零件 Web应用程序使用Asp.net MVC和fluent nHibernate将我的对象映射到数据库。我在视图模型上使用Castle验证,用于ValidateNonEmpty、ValidateRange等。我是否应该使用该方法查询存储库以查看该零件号是否已经存在?在ViewModel上使用my Repository感觉有些不对劲 我把逻辑放

我有一个允许用户创建新零件记录的网站。我试图找出验证特定字段唯一性的最佳方法。我想确保,如果零件号为1234的零件已存在于其他零件上,则不会有人尝试添加该零件

Web应用程序使用Asp.net MVC和fluent nHibernate将我的对象映射到数据库。我在视图模型上使用Castle验证,用于ValidateNonEmpty、ValidateRange等。我是否应该使用该方法查询存储库以查看该零件号是否已经存在?在ViewModel上使用my Repository感觉有些不对劲

我把逻辑放在控制器动作上会更好吗?这似乎不正确,因为我希望我的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);
}

我对你的问题没有答案,但你可以查看sharparchitecture.net网站。它包含了asp.net mvc和nhibernate的一些最佳实践。另外,我可以建议您查看xval项目和有关使用数据注释验证器进行验证的教程

我已经多次被问到这个问题。我的朋友们担心他们能否从验证程序代码执行数据访问。答案很简单。如果你需要这样做,你应该这样做。通常我们需要在每个抽象层次上进行这样的检查。在所有检查之后,您应该准备好捕获由唯一约束冲突引起的异常。

我发现适合我的解决方案是

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();
            }
        }
    }
}

如果在数据库中定义了唯一约束,那么为什么不将检查唯一值是否已存在于数据库的责任委派给数据库呢?使用NHibernate,您可以使用
NHibernate.Exceptions.ISQLExceptionConverter
接口捕获和转换与约束冲突相关的已知错误。您还可以使用
NHibernate.Exceptions.IViolatedConstraintNameExtracter
实现者(请参见
NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter
)获取数据库异常的肮脏细节,并将其转换为用户友好的消息,重新打包作为您选择的验证例外,并在相关控制器中捕获它

来自我的一个项目的快速、非常特定的快速脏异常转换器示例:


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配置/session工厂
属性元素进行配置:


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

csl.NHibernate.ConstraintViolationExceptionConverter,csl

编辑:可能应该提到,转换器接口在NHibernate的最新版本中发生了更改,此示例中的接口来自NHibernate.dll v2.1.0.4000

我通常在控制器和存储库之间放置一个服务层。
然后,服务层将处理验证和对存储库的调用


然后,如果服务层中存在验证错误,我将抛出一个自定义异常,在控制器中捕获它,并将错误注入到模型状态。

我认为这对您的体系结构很重要。在我过去做过的MVC应用中,我们将域内容从web内容中抽象出来,自然地,我们使用依赖注入来避免硬依赖

当您在绑定模型时验证模型时,是的,您可以通过ValidateSelf方法轻松地使用服务、存储库或架构中的任何东西。我想问题是,这种依赖性如何


如果我没记错的话,您可以创建自己的定制绑定器,该绑定器将使用依赖项注入框架插入您的模型需要验证的任何服务,在创建时,调用MVC的默认绑定器来填充对象,然后调用Castle validation的框架来进行验证。这不是一个经过深思熟虑的解决方案,但希望能激发一些想法。

记住,在您更新数据库之前,另一个用户在检查第一个试图添加零件的用户后,冷添加零件号为1234的零件。@Ian,这是一个很好的观点。只有少数用户负责添加部件。在这一点上,我更担心的是重复的部分,而不是同时重复的部分。发生这种情况的可能性要小得多,但这是一个很好的观察结果。@Hodzansresredin,谢谢-我确实使用了xVal框架。这只是介于服务器端验证和客户端验证框架(jquery)之间。不过,xVal并没有任何远程验证功能,所以我可以看看Adrian Grigore的文章。@Toran Billups,谢谢你的回答。我认为这并不能解决问题,因为我的问题更多的是何时/何地验证“BrokenRules”,而不是如何在预插入或预更新时处理它。我想在我尝试提交之前,我想知道它是否是有效的提交。还是t