使用MVC和fluent Nhibernate,在将字段绑定到域对象并保存之前,如何验证ViewModel上的惟一字段?

时间:2022-03-14 02:17:37

I have a website where I allow users to create new Part records. I'm trying to figure out the best way to validate specific fields for uniqueness. I want to make sure that somebody doesn't try to add a Part with PartNumber 1234 if that PartNumber already exists on a different Part.

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

The Web Application is using Asp.net MVC with fluent nHibernate for mapping my objects to the database. I'm using Castle validation on my view models for things like ValidateNonEmpty, ValidateRange, etc. Should I use the ValidateSelf method to query the repository to see if that part number already exists? Something doesn't feel right about using my Repository on the ViewModel.

Web应用程序使用Asp.net MVC和fluent nHibernate将对象映射到数据库。我在我的视图模型中使用了Castle验证,比如ValidateNonEmpty、ValidateRange等等。我是否应该使用ValidateSelf方法查询存储库,看看是否已经存在该部分编号?在ViewModel上使用我的存储库感觉不太对。

Would it be better for me to place that logic on the controller action? That doesn't seem right because I expect my ViewModel to already be Validated at the point (during ModelBind).

对我来说,把那个逻辑放在控制器动作上会更好吗?这似乎不太对,因为我希望我的ViewModel已经在此时(在ModelBind期间)得到验证。

Or maybe its none of the above. Thanks for any help on this one.

或者可能以上都不是。谢谢你在这方面的帮助。

UPDATE Ok, not sure if this will help, but here is what my Save action looks like for a typical Create Action in my project:

更新Ok,不确定这是否有用,但以下是我的保存操作在我的项目中的典型创建操作的样子:

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

6 个解决方案

#1


1  

I have been asked this question many times. My friends were worried about whether they can perform data access from the validator code. The answer is simple. If you need to do this, you should do it. Usually we need to do such checks at each level of abstraction. And after all checks you should be ready to catch an exception, caused by unique constraint violation.

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

#2


1  

If you define a unique constraint within the database, then why not delegate the responsibility to check for whether a unique value already exists to the database? Using NHibernate, you can use the NHibernate.Exceptions.ISQLExceptionConverter interface to capture and transform known errors relating to constraint violations. You can also use NHibernate.Exceptions.IViolatedConstraintNameExtracter implementers (see NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter) to get at the grubby details of your database exception, and transform it into a user-friendly message, repackage as a validation exception of your chosing and catch it in the relevant controller.

如果您在数据库中定义了唯一约束,那么为什么不委托责任检查数据库是否已经存在一个惟一值呢?使用NHibernate,您可以使用NHibernate. exception。ISQLExceptionConverter接口用于捕获和转换与约束违反相关的已知错误。您也可以使用nhibernate . exception。IViolatedConstraintNameExtracter实现程序(参见NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter)获取数据库异常的脏细节,并将其转换为用户友好的消息,重新打包作为选择的验证异常,并将其捕获到相关的控制器中。

Example of a quick, very specific quick and dirty exception converter from one of my projects:

我的一个项目中的一个快速、非常具体的快速和脏异常转换器示例:


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

Configured through the web.config/nhibernate-configuration/session-factory property element:

通过网络配置。配置/ nhibernate-configuration /会话工厂属性元素:


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

Edit: Should probably mention that the converter interface has changed in recent versions of NHibernate, the interface from this example is from NHibernate.dll v2.1.0.4000

编辑:应该提到转换器接口在最近版本的NHibernate中发生了变化,这个例子中的接口来自NHibernate。dll v2.1.0.4000

#3


1  

I typically put a Service layer between my controllers and repositories.
The service layer would then handle the validation and calls to the repository.

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

Then, if there's a validation error in the service layer, I throw a custom exception, catch it in the controller, and inject the errors in to the model state.

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

#4


0  

I have no answer for your question but you can check sharparchitecture.net site. It contains some best practives for asp.net mvc and nhibernate. Also I can recommend you to check xval project and tutorials about Validating with Data Annotation Validators

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

#5


0  

I have found the solution that works for me is to

我找到了适合我的解决方案。

1.) Ask if the entity is valid to execute your validation work.
2.) After this is complete you should have something on your object to show it's valid or not (in my case I use a CSLA like concept of "broken rules").
3.) If you have something like this you can verify the object is valid before NHibernate tries to persist it as shown below.

1)。询问实体是否对执行验证工作有效。2)。完成之后,您应该在对象上显示它是否有效(在我的例子中,我使用了类似CSLA的“破坏规则”概念)。3)。如果您有这样的东西,您可以在NHibernate尝试持久化对象之前验证对象是否有效,如下所示。

The only issue with this approach is that you do need to implement an interface on each entity requiring validation. If you can live with this it will stop NHibernate from persisting the changes of an object that's not valid according to your rules.

这种方法的惟一问题是,您确实需要在每个需要验证的实体上实现一个接口。如果您能够接受这种方法,它将阻止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();
            }
        }
    }
}

#6


0  

I would say this matters on your architecture. With MVC apps that I have done in the past we abstract away the domain stuff away from the web stuff and naturally we use dependency injection to avoid hard dependencies.

我想说的是,这对你的架构很重要。使用我过去做过的MVC应用程序,我们将域内容从web内容中抽象出来,自然我们使用依赖注入来避免硬依赖。

When it comes to validating the model when you are in the act of binding it, yes you could easily use the service, repository, or whatever you have next in your architecture in a ValidateSelf method. I think the question rises of what about that dependency.

当需要在绑定模型时验证模型时,您可以使用ValidateSelf方法轻松地使用服务、存储库或体系结构中接下来的任何东西。我认为关于这种依赖性的问题出现了。

If I remember correctly you can create your own custom binder that will use your dependency injection framework to plug-in any services your model needs for validation when you create it, call MVC's default binder to fill in the object, then call into Castle Validation's framework to do the validation. This isn't a fully thought solution, but hopefully it provokes some ideas.

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

#1


1  

I have been asked this question many times. My friends were worried about whether they can perform data access from the validator code. The answer is simple. If you need to do this, you should do it. Usually we need to do such checks at each level of abstraction. And after all checks you should be ready to catch an exception, caused by unique constraint violation.

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

#2


1  

If you define a unique constraint within the database, then why not delegate the responsibility to check for whether a unique value already exists to the database? Using NHibernate, you can use the NHibernate.Exceptions.ISQLExceptionConverter interface to capture and transform known errors relating to constraint violations. You can also use NHibernate.Exceptions.IViolatedConstraintNameExtracter implementers (see NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter) to get at the grubby details of your database exception, and transform it into a user-friendly message, repackage as a validation exception of your chosing and catch it in the relevant controller.

如果您在数据库中定义了唯一约束,那么为什么不委托责任检查数据库是否已经存在一个惟一值呢?使用NHibernate,您可以使用NHibernate. exception。ISQLExceptionConverter接口用于捕获和转换与约束违反相关的已知错误。您也可以使用nhibernate . exception。IViolatedConstraintNameExtracter实现程序(参见NHibernate.Exceptions.TemplatedViolatedConstraintNameExtracter)获取数据库异常的脏细节,并将其转换为用户友好的消息,重新打包作为选择的验证异常,并将其捕获到相关的控制器中。

Example of a quick, very specific quick and dirty exception converter from one of my projects:

我的一个项目中的一个快速、非常具体的快速和脏异常转换器示例:


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

Configured through the web.config/nhibernate-configuration/session-factory property element:

通过网络配置。配置/ nhibernate-configuration /会话工厂属性元素:


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

Edit: Should probably mention that the converter interface has changed in recent versions of NHibernate, the interface from this example is from NHibernate.dll v2.1.0.4000

编辑:应该提到转换器接口在最近版本的NHibernate中发生了变化,这个例子中的接口来自NHibernate。dll v2.1.0.4000

#3


1  

I typically put a Service layer between my controllers and repositories.
The service layer would then handle the validation and calls to the repository.

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

Then, if there's a validation error in the service layer, I throw a custom exception, catch it in the controller, and inject the errors in to the model state.

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

#4


0  

I have no answer for your question but you can check sharparchitecture.net site. It contains some best practives for asp.net mvc and nhibernate. Also I can recommend you to check xval project and tutorials about Validating with Data Annotation Validators

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

#5


0  

I have found the solution that works for me is to

我找到了适合我的解决方案。

1.) Ask if the entity is valid to execute your validation work.
2.) After this is complete you should have something on your object to show it's valid or not (in my case I use a CSLA like concept of "broken rules").
3.) If you have something like this you can verify the object is valid before NHibernate tries to persist it as shown below.

1)。询问实体是否对执行验证工作有效。2)。完成之后,您应该在对象上显示它是否有效(在我的例子中,我使用了类似CSLA的“破坏规则”概念)。3)。如果您有这样的东西,您可以在NHibernate尝试持久化对象之前验证对象是否有效,如下所示。

The only issue with this approach is that you do need to implement an interface on each entity requiring validation. If you can live with this it will stop NHibernate from persisting the changes of an object that's not valid according to your rules.

这种方法的惟一问题是,您确实需要在每个需要验证的实体上实现一个接口。如果您能够接受这种方法,它将阻止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();
            }
        }
    }
}

#6


0  

I would say this matters on your architecture. With MVC apps that I have done in the past we abstract away the domain stuff away from the web stuff and naturally we use dependency injection to avoid hard dependencies.

我想说的是,这对你的架构很重要。使用我过去做过的MVC应用程序,我们将域内容从web内容中抽象出来,自然我们使用依赖注入来避免硬依赖。

When it comes to validating the model when you are in the act of binding it, yes you could easily use the service, repository, or whatever you have next in your architecture in a ValidateSelf method. I think the question rises of what about that dependency.

当需要在绑定模型时验证模型时,您可以使用ValidateSelf方法轻松地使用服务、存储库或体系结构中接下来的任何东西。我认为关于这种依赖性的问题出现了。

If I remember correctly you can create your own custom binder that will use your dependency injection framework to plug-in any services your model needs for validation when you create it, call MVC's default binder to fill in the object, then call into Castle Validation's framework to do the validation. This isn't a fully thought solution, but hopefully it provokes some ideas.

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