Dani Avni
Dani Avni

Reputation: 435

should a validation function access the repository directly?

I have the following in my application: Action Orm entity (From telerik open access) Repository(Of Action) AppService(Holds an instance of the repository)

when I need to save an instance, I send the instance to the AppService. the AppService then calls a validator to validate the instance to save. the validator is based on http://codeinsanity.com/archive/2008/12/02/a-framework-for-validation-and-business-rules.aspx (full code on https://github.com/riteshrao/ncommon)

so basically my save function in the AppService looks like this

Public Sub AddAction(ByVal Item As Data.Model.Action)
        Contract.Requires(Of ArgumentNullException)(Item IsNot Nothing, "Item is nothing.")

        Dim validateResult As Rules.ValidationResult = _ActionValidator.Validate(Item)
        If Not validateResult.IsValid Then
            Throw New Validation.ValidationException(validateResult)
        End If

        Try
            _ActionRepository.Add(Item)
            _unitOfWork.SaveChanges()
        Catch ex As Exception
            _unitOfWork.ClearChanges()
            Throw New DataServiceException(ex.Message, ex)
        End Try

    End Sub

for checking properties of the Action item the sample code works great. my question begins when i need to make sure that the same action is not added twice to the DB for the same customer (ie. id is difference, name is the same and customer is the same)

as I see it I have a few options: option 1: check for a duplicate action using something like

function(validatedItem) item.Customer.Actions.Any(function(item)  item.id<>validatedItem.id andalso item.name=validatedItem.name))

basically I go from the action being saved back to the customer and then back to all his actions and check if any action exists with a different id and same name

the down sides are:

a. for this to work, when accessing the customer property of the item, the entire customer object is read from DB which is redundant in this case b. the Any function is being evaluated on the client as item.Customer.Actions returns IList(Of Action)

Option 2: let the validation class have access to the action repository. then i could simply do something like

'assume I already have validatedItem
repository.Any(function(item) item.id<>validatedItem.id and item.customerid=validatedItem.customerid and item.name=validatedItem.name)

this will result in an Exists query being sent to the DB but the downside(?) is that the validation framework should not access the repository directly (as far as I have seen in the very few examples i could find that do use validation and ORM)

Option 3: let the validation class have access to the AppService and use the AppService to check for existence of a duplicate. problems: a. I create a circular reference (AppService->Validation Class->AppService) b. I need to create a lot of useless functions in the AppService for loading items based on criteria that is only relevant for the validation

Any ideas what is the best course here?

Upvotes: 0

Views: 267

Answers (1)

Eben Roux
Eben Roux

Reputation: 13256

The simplest is not to check duplicates in the database from your domain.

When a collection of entities is part of you aggregate then it is a non-issue since you would not permit the duplicate to be added to the collection. Since the aggregate is stored as a whole you would never run into the problem.

For scenarios where you do not want a duplicate, say, e-mail address and no collection of the entities is represented by an aggregate (such as the Users in a system) you can just let the database enforce the uniqueness. Simply pick up the exception and report back. In many instances your validation would not be able to enforce the uniqueness simply because it doesn't have/implement the required locks that a database system would have.

So I'd simply leave that up to the database.

Upvotes: 2

Related Questions