Используя MVC и свободный Nhibernate, как мне проверить уникальные поля в моей ViewModel, прежде чем связать их с моим Доменным объектом и сохранить их? - PullRequest
11 голосов
/ 17 сентября 2009

У меня есть веб-сайт, где я разрешаю пользователям создавать новые записи деталей. Я пытаюсь найти лучший способ проверки конкретных полей на уникальность. Я хочу убедиться, что кто-то не пытается добавить деталь с PartNumber 1234, если этот номер уже существует в другой детали.

Веб-приложение использует Asp.net MVC с свободно используемым nHibernate для отображения моих объектов в базе данных. Я использую проверку замков в моих моделях представления для таких вещей, как ValidateNonEmpty, ValidateRange и т. Д. Должен ли я использовать метод ValidateSelf для запроса к хранилищу, чтобы узнать, существует ли этот номер детали? Что-то не так с использованием моего репозитория на ViewModel.

Было бы лучше для меня поместить эту логику в действие контроллера? Это кажется неправильным, потому что я ожидаю, что моя ViewModel уже будет проверена в точке (во время ModelBind).

Или, может быть, это ничего из вышеперечисленного. Спасибо за любую помощь в этом.

UPDATE Хорошо, не уверен, поможет ли это, но вот как выглядит мое действие Сохранить для типичного действия «Создать» в моем проекте:

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 голос
/ 19 сентября 2009

Обычно я помещаю слой Service между моими контроллерами и репозиториями.
Сервисный уровень будет затем обрабатывать проверку и вызовы в хранилище.

Затем, если в слое сервиса есть ошибка проверки, я выбрасываю пользовательское исключение, ловлю его в контроллере и вставляю ошибки в состояние модели.

1 голос
/ 18 сентября 2009

Если вы определяете уникальное ограничение в базе данных, то почему бы не делегировать ответственность за проверку того, существует ли уникальное значение в базе данных? Используя 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

1 голос
/ 18 сентября 2009

Мне задавали этот вопрос много раз. Мои друзья беспокоились о том, смогут ли они осуществлять доступ к данным из кода валидатора. Ответ прост. Если вам нужно сделать это, вы должны это сделать. Обычно нам нужно делать такие проверки на каждом уровне абстракции. И после всех проверок вы должны быть готовы перехватить исключение, вызванное нарушением уникального ограничения.

0 голосов
/ 23 сентября 2009

Я бы сказал, что это имеет значение для вашей архитектуры. С приложениями MVC, которые я делал в прошлом, мы абстрагируем контент домена от веб-контента и, естественно, используем внедрение зависимостей, чтобы избежать жестких зависимостей.

Когда дело доходит до проверки модели, когда вы находитесь в процессе ее привязки, да, вы можете легко использовать службу, репозиторий или все, что у вас будет дальше в вашей архитектуре в методе ValidateSelf. Я думаю, что возникает вопрос о том, как насчет этой зависимости.

Если я правильно помню, вы можете создать свой собственный пользовательский механизм связывания, который будет использовать вашу инфраструктуру внедрения зависимостей для подключения любых служб, необходимых вашей модели для проверки при ее создании, вызовите механизм связывания по умолчанию MVC для заполнения объекта, а затем вызовите Основа проверки замка, чтобы сделать проверку. Это не полностью продуманное решение, но, надеюсь, оно вызывает некоторые идеи.

0 голосов
/ 18 сентября 2009

Я нашел решение, которое работает для меня, чтобы

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();
            }
        }
    }
}
0 голосов
/ 17 сентября 2009

У меня нет ответа на ваш вопрос, но вы можете проверить сайт sharparchitecture.net. Он содержит некоторые лучшие практики для asp.net mvc и nhibernate. Также я могу порекомендовать вам проверить проект xval и руководства по валидации с помощью валидаторов аннотаций данных

...