I am developing application having business objects created from EF 4.0. The application is layered with Data Layer having repository of classes. Above that is a business layer where business processes are mapped as per the requirements. Business layer is calling data layer.
Business objects have complex associations. One scenario:
Merchant has Multiple Addresses
Merchant belongs to one Category
Merchant has an Account
Account has a Wallet
User will be creating a new merchant along with above mentioned business objects (graph). There are various business rules when creating and updating a merchant.
I would like to split my validations into two sub sections. 1) Scalar Property Validation to be handled by Validation Blocks 5.0 and 2) Business Process Rules Validation to be handled in business layer components. However, i am facing difficulty on having a centralized way to report broken rules (Scalar Properties validation and Business Process Rule Validation). I don't want to inject exceptions everywhere where business rule in is violated as per the business process mapped.
One e.g is following:
When creating a new merchant, Category needs to be validated. Because if a certain type category is associated with this new merchant then business rule says that such merchant cannot exists twice in the system.
Now, when a UI pass the new merchant graph to business layer component, i first validate BO Scalar properties validation (validated bu Validation Blocks) then this BO is passed into business process rule validation methods to check various rules. There are possible violation points which i want to report and shall not allow the merchant and graph objects to be persisted.
Please share any value able design approach to manage centralized validation rule logging and reporting to UI layer.
EDIT: I don't want to incorporate validations on EF SaveChanges, ChangeAssociation etc event handlers.
While you said you don't want to throw exceptions, I found throwing exceptions very effective. Especially when you having multiple sub systems validating the system, throwing one single type of exception as a facade will be very effective.
What you can do is creating a custom exception (i.e. named ValidationException) that will be thrown either when your Validation Application Block (VAB) reports error or the business rules report errors. In the presentation layer you will have to catch this ValidationException and report this exact type of exception in a user friendly way to the end user.
Using a single exception type for both validation sub systems, allows you to report errors in a consistent way, and also ensures that validation errors won't get unnoticed when you (or an other developer) forgets to handle validation errors. Handling errors should be very explicit IMO. When you let methods return a list of errors it is easy to forget to handle them. When creating a custom exception, it is easy to add a property to that exception type that contains a list of validation errors. Such a list of errors is easily extracted from VAB. I don't know what validation system you use for your business rules validation, but it can't be to hard to extract a list of error codes from it.
The most simple way to handle this in UI is of course by using a try-catch:
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc
try
{
businessCommand.Execute();
}
catch (ValidationException ex)
{
UIValidationHelper.ReportValidationErrors(ex.Errors);
}
Of course having these try-catch statements all over the place is ugly, but at least this code is easy to follow. Dependent on how you structured your business layer and the UI technology you use, there are prettier solutions you could use. For instance, you can wrap the actual operation that can fail with an action as follows:
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc
UIValidationHelper.ExecuteOrDisplayErrors(() =>
{
businessCommand.Execute();
});
The UIValidationHelper would look like this:
public static class UIValidationHelper
{
public static void ExecuteOrDisplayErrors (Action action)
{
try
{
action();
}
catch (ValidationException ex)
{
// Show the errors in your UI technology
ShowErrorMessage(ex.Errors);
}
}
}
An other option, that I've used myself in the past, is by extending the business layer with events. For this to work you need a construct such as commands, as I use in my examples. When using events, the UI could look like this:
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc
businessCommand.ValidationErrorOccurred +=
UIValidationHelper.DisplayValidationErrors;
businessCommand.Execute();
This example hooks a static method to the ValidationErrorOccurred event of a command instance. The trick here is to let the Execute method of that command catch ValidationExceptions and route them to the ValidationErrorOccurred when it is registered. When no method is registered, this exception should bubble up the call stack, because an unhandled validation exception should of course not get unnoticed.
While it is possible to of course do this directly from your business layer, it would make your business layer dependent on a particular validation technology. Besides this, this method allows clients to choose to handle validation errors in any way they want or decide not to handle them at all (for instance when you don’t expect any validation errors to occur in a particular use case).
When using ASP.NET Web Forms, the DisplayValidationErrors method of the UIValidationHelper could look like this:
public static class UIValidationHelper
{
public static void DisplayValidationErrors(
object sender, ValidationErrorEventArgs e)
{
Page page = GetValidPageFromCurrentHttpContext();
var summary = GetValidationSummaryFromPage()
foreach (var result in e.Error.Results)
{
summary.Controls.Add(new CustomValidator
{
ErrorMessage = result.Message,
IsValid = false
});
}
}
private static Page GetValidPageFromCurrentHttpContext()
{
return (Page)HttpContext.Current.CurrentHandler;
}
private ValidationSummary GetValidationSummaryFromPage(Page page)
{
return page.Controls.OfType<ValidationSummary>().First();
}
}
This static helper method injects the messages of the reported errors into a ValidationSummary control on the page. It expects the page to contain a ValidationSummary control at root level of the page. A more flexible solution can easily be created.
The last thing that I like to show you is how the the BusinessCommand would look like when adopting this solution. The base class of these commands could look something like this:
public abstract class BusinessCommand
{
public event EventHandler<ValidationErrorEventArgs>
ValidationErrorOccurred;
public void Execute()
{
try
{
this.ExecuteInternal();
}
catch (ValidationException ex)
{
if (this.ValidationErrorOccurred != null)
{
var e = new ValidationErrorEventArgs(ex);
this.ValidationErrorOccurred(this, e);
}
else
{
// Not rethrowing here would be a bad thing!
throw;
}
}
}
protected abstract void ExecuteInternal();
}
The ValidationErrorEventArgs looks like this:
public class ValidationErrorEventArgs : EventArgs
{
public ValidationErrorEventArgs(ValidationException error)
{
this.Error = error;
}
public ValidationException Error { get; private set; }
}
I hope this all makes sense and sorry for my long answer :-)
Good luck.
Object member validation can be done within encapsulated business objects. Whether you do this in the setter properties or as separate method calls is up to you. Should the objects be set with values that are invalid, should the application allow data into the system that is of the wrong type, range checks etc.
As for the Rules part, I would look at the visitor pattern for each object graph that your trying to achieve some rules checking on. I would report this back probably as a new nested object based on what was found. My personal preference to this reporting side, is to use a visitor pattern that produces an XML Document or some other custom nested class depending on your efficiency needs. The actual rules within the visitor pattern can be declared outside of the visitor pattern, preferably in a declarative approach. For example CheckDuplicateRecord. This would allow for reuse.
Keep all this in the same layer as the Business Layer, but further sub divide the business layer into a Rules validation layer and the Business Objects.
My personal approach with using EF is to use POCO objects as these allow for scalability. I would then do some validation on the UI, then do some validation when transported to the Business Object layer, then do the same again in the EF DAL layer.
Related
When I write codes, I try to be careful about SOLID and clean code principles. When I look at my functions, I think that I fall into side effect error.
For example, lets assume that I have a logic in a web service. When I trigger a method, it must get all data from another service and insert them to database. My representative methods are like below.
//when I call the method, process starts
public void TriggerProcess()
{
GetInformationsFromService();
}
public void GetInformationsFromService()
{
var informations = exampleService.GetInformations();
InsertInformations(informations);
}
public void InsertInformations(informations)
{
insertThemToDb(informations);
}
When I write codes like above, I fall into side effect error. If someone wants to use only GetInformationsFromService() methods in the service, it shoud not insert data.
However, If I call methods like below..
public void TriggerProcess()
{
var informations = GetInformationsFromService();
InsertInformations(informations);
}
There will be always a lot of methods like chain methods which have one purpose that is to call methods in proper order and there is always a middle
layer between trigger methods and methods with one responsibility. if business gets bigger, it seems strange I think.
public void RepresentativeMethod()
{
method1();
method2();
method3();
//...
}
How can I avoid side effect? Which pattern can I use to make good implementation?
Updating/Inserting data in the database from the data from another service and just viewing of data is two different use cases/process. Don't try to reuse your GetInformationsFromService() because it has different purpose. Actually, you must rename it something like SyncInformation() and you will have another method called GetInformation() just to view data.
Here's what you can do, eliminate the TriggerProcess() because SyncInformation() is already a process, just call it directly:
This use cases/process should also be included in the domain layer:
Synchronize Information Use Case:
public void SyncInformation() {
var informations = exampleService.GetInformations();
informationRepository.insertInformation(informations);
}
Get Information Use Case:
public List<Information> GetInformation() {
return exampleService.getInformation();
}
Fetching and saving of data should be in your data layer:
ExampleService:
public List<Information> getInformation() {
// logic to fetch from another service, eg: API
}
InformationRepository:
public void insertInformation(informations)
{
// insert to database logic
}
Here, we are following the separation of concerns because we're splitting it into two layers, domain and data. Domain layer handles all the application/business logic like for example the steps on how to synchronize of information. It knows WHEN it should save data but it doesn't know HOW. Data layer knows HOW to read and save data, but it doesn't know WHEN it should happen.
I've just recently started learning MVC patterns, originally in android but currently with spring MVC framework. I'm wondering if it is more appropriate to have testing/exception handling in the model or a controller. What I mean is, say I had some field in the model which should be restricted to some values, or a range of values; should I be testing the inputs and throwing exception in the model and having the controller catch them, or should the controller check inputs on it's own before forwarding inputs to the model?
My concern with testing in the controller is that I may need to check values in several spots whereas if I were to test in the model it's only done in one place. My concern with checking inputs in the model is that, for some reason, that seems really odd to me; then again I am new to this pattern so I don't really know yet.
What is the norm? What is recommended?
Thanks everyone
It is appropriate to have testing and/or exception handling in the model and the controller, which ever is most appropriate to the handling of the exception.
For example, if you want want to parse a number from a string and use a default value when the string does not contain a number and you are parsing in the model, then you should handle the numberformatexception in the model.
I think of this as an "expected" exception.
private String blammyValue;
public int getBlammyAsInt()
{
int returnValue;
try
{
returnValue = Integer.parseInt(blammyValue);
}
catch (NumberFormatException exception)
{
returnValue = -1; // some default value
}
return returnValue;
}
If the exception is something that is out-of-the-ordinary,
like a database exception,
and for which there is no reasonable default behavior,
then catching it in the controller makes sense.
Right now, I have a domain entity named StyleBundle. This StyleBundle takes a list of Styles:
public class StyleBundle
{
public StyleBundle(List<Style> styles)
{
this.Styles = styles;
}
public IEnumerable<Style> Styles { get; private set;}
}
So, in my original design, a StyleBundle should never be created with an empty Style list. This was a rule that the domain experts basically said was good.
I wrote this using a guard clause in the constructor:
if (styles.Count() == 0)
throw new Exception("You must have at least one Style in a StyleBundle.");
which made sure I could not create StyleBundle in an invalid state. I thought an exception made sense here b/c a StyleBundle being created without at least one Style was exceptional in the system.
Of course, change came down the road during the rest of the project, and now it should be possible for a user to create a StyleBundle without Styles, but they should not be allowed to PERSIST a StyleBundle without Styles.
So now I'm looking at my guard clause and realizing that I can't have the exception thrown from the constructor anymore.
Moving forward, I have a Service/Application layer that my code-behinds interact with when they're working with StyleBundles. In my Service Layer, I have a StyleBundleService class, and that class exposes basic functionality to the UI... among them is "CreateStyleBundle".
It seems as if I'll have to have my Service Layer check to see if the StyleBundle does or does not have any Styles before it's persisted to the database, but something about this decision feels "wrong" to me.
Anyone run into a similar thing? Basically, the different between the state of an object being valid when "new'ed up" vs. the state of the same object when it comes to persistence?
Thanks!
Mike
I would add an IsValid method to your entity. This would check if the entity is currently in a valid state (in your case, check if there are styles).
This method can be called from your Repository to check if an entity may be persisted. You can add more rules to the IsValid method for specific entities and you can implement something like a collection of Validation errors is you want to throw a meaningful exception.
Expanding what Wouter said, plus handy BeforeSaving and BeforeDeleting methods:
public interface IDomainObject<T>
{
bool IsValid();
}
public interface IEntity<T> : IDomainObject<T>
{
}
public interface IAggregateRoot<T> : IEntity<T>
{
void BeforeSaving();
void BeforeDeleting();
}
public interface IAggregateRoot { //or simply IEntity depending on the model
bool IsValid();
}
public class StyleBundle : IAggregateRoot<T> {
return styles.Count() > 0
}
public class StyleBundleRepository : Repository<StyleBundle> {
}
public abstract class Repository<T> : IRepository<T> where T : class, IAggregateRoot<T> {
public T Save(T t)
{
t.BeforeSaving(); //for all AggregateRoots, maybe logging what the aggregate was like before the changes
if(!t.IsValid())
throw Exeception("Entity invalid");
EntityStore.Current.SaveChanges();
// "AfterSaving" here, i.e.: log how the entity looks after the update
}
}
Edit: I dont personally use the IsValid idea, I go with a full class of EntityValidationErrors where I can report back to the client what was wrong before attempting to save, things that shouldnt be null, shouldnt be empty (like your Styles etc)
There are multiple strategies:
Some developers prefer to create 2 methods in the entity itself, one called IsValid() which validates the entity in terms of business rules (general validation) and another one called IsValidForPersistence() which validates the entity for persistence.
Regarding IsValid() I prefer instead not to allow invalid state in the first place by validating all inputs, and to support invariants I use factory or builder.
you may check the link http://www.codethinked.com/thoughts-on-domain-validation-part-1
for some thoughts.
I know, this question is three years old, but seeing the current answer is something I like to respond to. We are talking about the domain data. Hence, there can't be a valid StyleBundle with 0 objects. I imagine, you have a frontend editor somewhere, were you create a "new" StyleBundle and have to add at least one style, before hitting the "save" button.
At this point in the frontend, you won't have a domain object. You may have a data transfer object, that will be send with a "CreateNewStyleBundle" command.
In my opinion, the domain object must be agnostic to persitance and should always be in a valid state. If you have to call a "IsValid" method, you circumvent the whole idea of having domain objects in the first place.
That's just my humble opinion.
MVC best practices state that the model should handle input/data validation. Let's say that we have a model that creates new user accounts, with the following fields and constraints:
Username - not null, not already in DB
Password - not null, alphanumeric only
E-mail - not null, not already in DB, valid e-mail format
We have an AccountModel with a CreateNewUser() function:
component
{
public void function CreateNewUser(string username, string password, string email)
{
// create account
}
}
Then we have a controller that processes a form post and tells the model to create the account:
component
{
public void function NewUser()
{
var username = event.getValue("username");
var password = event.getValue("password");
var email = event.getValue("email");
var accountModel = new AccountModel();
accountModel.CreateNewUser(username, password, email);
event.addResult("UserCreated");
}
Now I want to add validation. If the user fails to provide input for all three fields, the application should show the user three validation error messages. This is easy enough to do in the controller:
// assumes that ValidateInput() is a function on the controller that returns an array
var validationErrors = ValidateInput(username, password, email);
// if there were any validation errors, return a ValidationError result
if (len(validationErrors)
{
event.setValue("validationerrors", validationErrors);
event.addResult("ValidationError");
}
else
{
event.addResult("UserCreated");
}
And the view will pull the validationerrors variable and display the error messages.
However, this logic is supposed to reside in the model. How do I do this? I can think of two ways:
Method 1: Move ValidateInput() from the controller to the model. Now the controller has to call ValidateInput() first before CreateNewUser(). If the programmer forgets to do this, CreateNewUser() will throw a validation exception. The downside to this is that now every data operation that requires validation will need an if/else block.
Method 2: Forego having to call ValidateInput() and just call CreateNewUser() directly. If there were any validation errors, an exception will be thrown and it will contain the error message array. This method would work in C#, but it looks like ColdFusion does not support returning of data with the exception, only an exception message and type. Also, every data operation will require a try/catch block.
Method 3: ??
How would you handle validation in this case? Is method 1 what most people do when it comes to validation in the model? Or do people typically use method 2? Or is there a method 3 that I'm not thinking of?
I don't think you should couple the validation of the user's data entry to the creation of the user's account: they are too different things.
If you couple the two together, it means you're basically doing form validation every time you create an account, which doesn't seem right to me.
I see form validation as a UI concern, more than a concern of the objects that might be ultimately created from that data. Obviously your createNewUser() method has its own business rules (which will probably closely mirror that of the validation for a create-new-user form, but they are still separate concerns).
It is possibly a bit unorthodox, but I will put a validateUserData() (or something) method in my User CFC which the form-validation model then calls to help with the validation. This means the business rules for a user are in the same place, and can be called separately. Thereafter, createNewUser() works on a garbage-in/garbage-out principle.
Sidenote: createNewUser() is a bit of a tautological name, innit? What other sort of user would you be creating other than a new one? Also, if it's in your Account.cfc; is it a new user or a new account that's being created? If an account and a user are not synonymous and an account might exist without a user (or vice-versa), perhaps you ought to have a User.cfc too. Then again, this code you've given us could simply be for the purposes of illustration, and you've got all this covered.
I am attempting to create the the business and data layers for my big ASP.NET MVC application. As this is the first time for me attempting a project of this scale I am reading some books and trying to take good care at separating things out properly. Usually my applications mix the business logic and data access layers, and multiple business entities are intertwined in the single class (which has confused me a few times when I was trying to figure out where to add things).
Most of what I have been reading is to separate out the business and data layers. This seems all fine and dandy, but I am having trouble visualizing exactly how to do this in some scenarios. For example, let's say I am creating a system that allows admins to add a new product to the system:
public class Product
{
public int Id { get; private set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Then I separate out the data access by creating a repository
public class ProductRepository
{
public bool Add(Product product);
}
Let's say I want to require a product's name to have at least 4 characters. I can't see how to do this cleanly.
One idea I had was to expand the Name's set property and only set it if it's 4 characters long. However, there is no way for a method that is creating the product to know the name didn't get set except that Product.Name != whatever they passed in.
Another idea I had is to put it in the Add() method in the repository, but then I have my business logic right there with the data logic, which also means if the Add call fails I don't know if it failed for the business logic or because the DAL failed (and it also means I can't test it using mock frameworks).
The only thing I can think of is to put my DAL stuff in a 3rd layer that gets called from the Add() method in the repository, but I don't see this in any of the domain modelling examples in my book or on the web (that I've seen at least). It also adds to the complexity of the domain models when I am not sure it is needed.
Another example is wanting to make sure that a Name is only used by one product. Would this go in the Product class, ProductRepository Add() method, or where?
As a side note, I plan to use NHibernate as my ORM however, to accomplish what I want it (theoretically) shouldn't matter what ORM I am using since TDD should be able to isolate it all.
Thanks in advance!
I usually approach this by using a layered architecture. How to do this? You basically have the following (ideally) VS projects:
Presentation layer (where the UI stuff resides)
Business layer (where the actual business logic resides)
Data access layer (where you communicate with your underlying DBMS)
For decoupling all of them I use so-called interface layers s.t. in the end I have
Presentation layer (where the UI
stuff resides)
IBusiness layer (containing the interfaces for the
business layer)
Business layer (where
the actual business logic resides)
IDataAccess layer (containing the
interfaces for the DAO layer)
Data access layer (where you communicate
with your underlying DBMS)
This is extremely handy and creates a nicely decoupled architecture. Basically your presentation layer just accesses the interfaces and not the implementations itself. For creating the according instances you should use a Factory or preferably some dependency injection library (Unity is good for .Net apps or alternatively Spring.Net).
How does this impact on your business logic / testability of your app?
It is probably too long to write everything in detail, but if you're concerned about having a well testable design you should absolutely consider dependency injection libraries.
Using NHibernate,...whatever ORM
Having a DAO layer completely separated through the interfaces from the other layers you can use whatever technology behind for accessing your underlying DB. You could directly issue SQL queries or use NHibernate, as you wish. The nice thing is that it is totally independent from the rest of your app. You could event start today by writing SQLs manually and tomorrow exchange your DAO dll with one that uses NHibernate without a single change in your BL or presentation layer.
Moreover testing your BL logic is simple. You may have a class like:
public class ProductsBl : IProductsBL
{
//this gets injected by some framework
public IProductsDao ProductsDao { get; set; }
public void SaveProduct(Product product)
{
//do validation against the product object and react appropriately
...
//persist it down if valid
ProductsDao.PersistProduct(product);
}
...
}
Now you can easily test the validation logic in your SaveProduct(...) method by mocking out the ProductDao in your test case.
Put things like the product name restriction in the domain object, Product, unless you want to allow products with fewer than 4 characters in some scenarios (in this case, you'd apply the 4-character rule at the level of the controller and/or client-side). Remember, your domain objects may be reused by other controllers, actions, internal methods, or even other applications if you share the library. Your validation should be appropriate to the abstraction you are modeling, regardless of application or use case.
Since you are using ASP .NET MVC, you should take advantage of the rich and highly extensible validation APIs included in the framework (search with keywords IDataErrorInfo MVC Validation Application Block DataAnnotations for more). There are lots of ways for the calling method to know that your domain object rejected an argument -- for example, throwing the ArgumentOutOfRangeException.
For the example of ensuring that product names are unique, you would absolutely not put that in Product class, because this requires knowledge of all other Products. This logically belongs at the persistence layer and optionally, the repository. Depending on your use case may warrant a separate service method that verifies that the name does not already exist, but you shouldn't assume that it will still be unique when you later try to persist it (it has to be checked again, because if you validate uniqueness and then keep it around a while longer before persisting, someone else could still persist a record with the same name).
This is the way I do it:
I keep the validation code in the entity class, which inherits some general Item Interface.
Interface Item {
bool Validate();
}
Then, in the repository's CRUD functions i call the appropriate Validate function.
This way all the logic paths are validating my values, but i need to look only in one place to see what that validation really is.
Plus, sometimes you use the entities outside the repository scope, for example in a View. So if the validation is separated, each action path can test for validation without asking the repository.
For restrictions I utilize the partial classes on the DAL and implement the data annotation validators. Quite often, that involves creating custom validators but that works great as it's completely flexible. I've been able to create very complex dependent validations that even hit the database as part of their validity checks.
http://www.asp.net/(S(ywiyuluxr3qb2dfva1z5lgeg))/learn/mvc/tutorial-39-cs.aspx
In keeping with the SRP (single responsibility principle), you might be better served if the validation is separate from the product's domain logic. Since it's required for data integrity, it should probably be closer to the repository - you just want to be sure that validation is always run without having to give it thought.
In this case you might have a generic interface (e.g. IValidationProvider<T>) that is wired to a concrete implementation through an IoC container or whatever your preference may be.
public abstract Repository<T> {
IValidationProvider<T> _validationProvider;
public ValidationResult Validate( T entity ) {
return _validationProvider.Validate( entity );
}
}
This way you can test your validation separately.
Your repository might look like this:
public ProductRepository : Repository<Product> {
// ...
public RepositoryActionResult Add( Product p ) {
var result = RepositoryResult.Success;
if( Validate( p ) == ValidationResult.Success ) {
// Do add..
return RepositoryActionResult.Success;
}
return RepositoryActionResult.Failure;
}
}
You could go a step further, if you intend on exposing this functionality via an external API, and add a service layer to mediate between the domain objects and the data access. In this case, you move the validation to the service layer and delegate data access to the repository. You may have, IProductService.Add( p ). But this can become a pain to maintain due to all of the thin layers.
My $0.02.
Another way to accomplish this with loose coupling would be to create validator classes for your entity types, and register them in your IoC, like so:
public interface ValidatorFor<EntityType>
{
IEnumerable<IDataErrorInfo> errors { get; }
bool IsValid(EntityType entity);
}
public class ProductValidator : ValidatorFor<Product>
{
List<IDataErrorInfo> _errors;
public IEnumerable<IDataErrorInfo> errors
{
get
{
foreach(IDataErrorInfo error in _errors)
yield return error;
}
}
void AddError(IDataErrorInfo error)
{
_errors.Add(error);
}
public ProductValidator()
{
_errors = new List<IDataErrorInfo>();
}
public bool IsValid(Product entity)
{
// validate that the name is at least 4 characters;
// if so, return true;
// if not, add the error with AddError() and return false
}
}
Now when it comes time to validate, ask your IoC for a ValidatorFor<Product> and call IsValid().
What happens when you need to change the validation logic, though? Well, you can create a new implementation of ValidatorFor<Product>, and register that in your IoC instead of the old one. If you are adding another criterion, however, you can use a decorator:
public class ProductNameMaxLengthValidatorDecorator : ValidatorFor<Person>
{
List<IDataErrorInfo> _errors;
public IEnumerable<IDataErrorInfo> errors
{
get
{
foreach(IDataErrorInfo error in _errors)
yield return error;
}
}
void AddError(IDataErrorInfo error)
{
if(!_errors.Contains(error)) _errors.Add(error);
}
ValidatorFor<Person> _inner;
public ProductNameMaxLengthValidatorDecorator(ValidatorFor<Person> validator)
{
_errors = new List<IDataErrorInfo>();
_inner = validator;
}
bool ExceedsMaxLength()
{
// validate that the name doesn't exceed the max length;
// if it does, return false
}
public bool IsValid(Product entity)
{
var inner_is_valid = _inner.IsValid();
var inner_errors = _inner.errors;
if(inner_errors.Count() > 0)
{
foreach(var error in inner_errors) AddError(error);
}
bool this_is_valid = ExceedsMaxLength();
if(!this_is_valid)
{
// add the appropriate error using AddError()
}
return inner_is_valid && this_is_valid;
}
}
Update your IoC configuration and you now have a minimum and maximum length validation without opening up any classes for modification. You can chain an arbitrary number of decorators in this way.
Alternatively, you can create many ValidatorFor<Product> implementations for the various properties, and then ask the IoC for all such implementations and run them in a loop.
Alright, here is my third answer, because there are so very many ways to skin this cat:
public class Product
{
... // normal Product stuff
IList<Action<string, Predicate<StaffInfoViewModel>>> _validations;
IList<string> _errors; // make sure to initialize
IEnumerable<string> Errors { get; }
public void AddValidation(Predicate<Product> test, string message)
{
_validations.Add(
(message,test) => { if(!test(this)) _errors.Add(message); };
}
public bool IsValid()
{
foreach(var validation in _validations)
{
validation();
}
return _errors.Count() == 0;
}
}
With this implementation, you are able to add an arbitrary number of validators to the object without hardcoding the logic into the domain entity. You really need to be using IoC or at least a basic factory for this to make sense, though.
Usage is like:
var product = new Product();
product.AddValidation(p => p.Name.Length >= 4 && p.Name.Length <=20, "Name must be between 4 and 20 characters.");
product.AddValidation(p => !p.Name.Contains("widget"), "Name must not include the word 'widget'.");
product.AddValidation(p => p.Price < 0, "Price must be nonnegative.");
product.AddValidation(p => p.Price > 1, "This is a dollar store, for crying out loud!");
U can use a other validation system. you can add a method to IService in service layer such as:
IEnumerable<IIssue> Validate(T entity)
{
if(entity.Id == null)
yield return new Issue("error message");
}