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

发布于 2024-08-04 21:09:31 字数 1040 浏览 8 评论 0原文

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

Web 应用程序使用 Asp.net MVC 和流畅的 nHibernate 将我的对象映射到数据库。我在我的视图模型上使用 Castle 验证,例如 ValidateNonEmpty、ValidateRange 等。我应该使用 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);
}

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.

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.

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).

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:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不奢求什么 2024-08-11 21:09:31

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

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.

忘年祭陌 2024-08-11 21:09:31

如果您在数据库中定义了唯一约束,那么为什么不委托检查数据库是否已存在唯一值的责任呢?通过 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-configuration/session-factory 属性元素进行配置:


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

编辑: 应该提到的是,最近版本的 NHibernate 中的转换器接口已发生变化,此示例中的接口来自 NHibernate.dll v2.1.0.4000

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.

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:


<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

你曾走过我的故事 2024-08-11 21:09:31

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

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

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.

耳钉梦 2024-08-11 21:09:31

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

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

濫情▎り 2024-08-11 21:09:31

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

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

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.

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.

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();
            }
        }
    }
}
姐不稀罕 2024-08-11 21:09:31

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

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

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

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.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文