I'm exploring using FluentValidation as it seems to be an elegant API for validation of my ViewModels upon model binding. I'm looking for opinions on how to properly centralize validation using this library as well as from my business (service) layer and raise it up to the view without having 2 different approaches to adding modelstate errors.
I'm open to using an entirely different API but essentially looking to solve this branching validation strategy.
[Side Note: One thing I tried was to move my business method into my FluentValidation's custom RsvpViewModelValidator class and using the .Must method but it seemed wrong to hide that call in there because if I needed to actually use my Customer object they I would have to re-query it again since its out of scope]
Sample Code:
[HttpPost]
public ActionResult AcceptInvitation(RsvpViewModel model)
{
//FluentValidation has happened on my RsvpViewModel already to check that
//RsvpCode is not null or whitespace
if(ModelState.IsValid)
{
//now I want to see if that code matches a customer in my database.
//returns null if not, Customer object if existing
customer = _customerService.GetByRsvpCode(model.RsvpCode);
if(customer == null)
{
//is there a better approach to this? I don't like that I'm
//splitting up the validation but struggling up to come up with a
//better way.
ModelState.AddModelError("RsvpCode",
string.Format("No customer was found for rsvp code {0}",
model.RsvpCode);
return View(model);
}
return this.RedirectToAction(c => c.CustomerDetail());
}
//FluentValidation failed so should just display message about RsvpCode
//being required
return View(model);
}
[HttpGet]
public ActionResult CustomerDetail()
{
//do work. implementation not important for this question.
}
To give some closure to the question (and make it acceptable) as well as summarize the comments:
Business/process logic and validation logic are two entities. Unless the validation ties in to the database (e.g. check for unique entries) there's no reason to group validation into one location. Some are responsible in the model making sure there's nothing invalid about the information, and some handle how the validated values are used within the system. Think of it in terms of property getters/setters vs the logic used in the methods with those properties.
That being said, separating out the processes (checks, error handling, etc.--anything not relating to UI) can be done in a service layer which also tends to keep the application DRY. Then the action(s) is/are only responsible for calling and presenting and not performing the actual unit of work. (also, if various actions in your application use similar logic, the checks are all in one location instead of throw together between actions. (did I remember to check that there's an entry in the customer table?))
Also, by breaking it down in to layers, you're keeping concerns modular and testable. (Accepting an RSVP isn't dependent on an action in the UI, but now it's a method in the service, which could be called by this UI or maybe a mobile application as well).
As far as bubbling errors up, I usually have a base exception that transverses each layer then I can extend it depending on purpose. You could just as easily use Enums, Booleans, out parameters, or simply a Boolean (the Rsvp either was or wasn't accepted). It just depends on how finite a response the user needs to correct the problem, or maybe change the work-flow so the error isn't a problem or something that the user need correct.
You can have the whole validation logic in fluent validation:
public class RsvpViewValidator : AbstractValidator<RsvpViewModel>
{
private readonly ICustomerService _customerService = new CustomerService();
public RsvpViewValidator()
{
RuleFor(x => x.RsvpCode)
.NotEmpty()
.Must(BeAssociatedWithCustomer)
.WithMessage("No customer was found for rsvp code {0}", x => x.RsvpCode)
}
private bool BeAssociatedWithCustomer(string rsvpCode)
{
var customer = _customerService.GetByRsvpCode(rsvpCode);
return (customer == null) ? false : true;
}
}
Related
i'm creating a zend framework 2 application and i'm sort of trying to implement what is explained here:
http://ralphschindler.com/2009/08/13/dynamic-assertions-for-zend_acl-in-zf
The demonstration that the code works is really nice, but it doesn't really apply to how a framework (utilizing mvc) works. Or maybe i'm just on the wrong track...
i've created a RouteListener like this :
class RouteListener implements ListenerAggregateInterface
{
public function attach(EventManagerInterface $events)
{
$this->listeners[] = $result = $events->attach(
MvcEvent::EVENT_DISPATCH, array($this, "checkAcl"), 100
);
}
}
the method checkAcl then checks if you're allowed to do what you want to do.
The resource and action are determined like this:
$resource = $routeMatch->getParam("controller");
$action = $routeMatch->getParam("action");
And the role is determined by the identity stored in the session (which implements Zend\Permissions\Acl\Role\RoleInterface)
Following the example: how do i determine if a user is allowed to edit a certain blog-post?
By the time acl is doing it's checking, the controller hasn't loaded the blogpost yet, so i'm not sure how to approach this. Unless i duplicate the retrieval of the blogpost in the assertion, but that i'm hoping there is a better way.
I'm also using doctrine for my persistence layer and in the end i've solved this problem using doctrine's Lifecycle Events. This allows you to trigger the acl-check whenever you want: when a entity (p.e. a blog-post) is loaded, or saved, etc.
I'm new to WCF data services. I have a quite simple data model. Some of its properties have the same type, like this:
public IQueryable<IntegerSum> HouseholdGoodsSums
{
get
{
return GetData<IntegerSum>(DefaultProgramID, "rHouseholdGoodsPrice", IntegerSumConverter);
}
}
public IQueryable<IntegerSum> StructureSums
{
get
{
return GetData<IntegerSum>(DefaultProgramID, "rStructurePrice", IntegerSumConverter);
}
}
The IntegerSum is a very very simple class:
[DataServiceKey("Amount")]
public class IntegerSum
{
public int Amount { get; set; }
}
When I navigate to my service in a web browser, I see the following error message:
The server encountered an error processing the request. The exception message is 'Property 'HouseholdGoodsSums' and 'StructureSums' are IQueryable of types 'IntegrationServices.PropertyIntegrationServices.IntegerSum' and 'IntegrationServices.PropertyIntegrationServices.IntegerSum' and type 'IntegrationServices.PropertyIntegrationServices.IntegerSum' is an ancestor for type 'IntegrationServices.PropertyIntegrationServices.IntegerSum'. Please make sure that there is only one IQueryable property for each type hierarchy.'.
When I get rid of one of these two properties, the service starts working.
I searched for this error message in google, but haven't found a solution.
Is it really not allowed to have two properties with the same type in a data model? If so, why?
Comrade,
To address the error first, you're running into a limitation in the Reflection provider. Specifically, the Reflection provider doesn't support MEST.
That said, there are better approaches to achieve what you're trying to achieve. You should probably not make IntegerSum an entity type (an entity type is a uniquely identifiable entity, which doesn't really fit your scenario). While you can't expose that directly, you can expose it as a service operation. That seems much closer to what you're trying to achieve.
A couple of ways to distinguish between whether or not something should be an entity:
If it has a key already, such as a PK in a database, it should probably be an entity type
If you need to create/update/delete the object independently, it must be an entity type
HTH,
Mark
Ok, each and every time I get into this situation, I struggle back and forth until I find a way to solve it (and that is usually not the way I would have liked to solve it).
What I'm talking about is disconnected entities in EF that should update existing entities in the database.
I'll give an example of my problem here (this example is the last time I got into this problem that caused me to write this question).
I have a WCF service that uses Entity Framework as well. The other program that have added a service reference to my service have gotten proxy versions of the Entities as normal.
The case is that the consumer of the service now construct a object of this proxy class, and call the method UpdateEntity on the WCF service. This entity has a foreign key to another type of entities, and the primary key of the entity I want to link this new entity to is also sent as a parameter to this method. In this case, I want the entity with the same primary key in the database to be updated. It seems simple enough right?
My method looks something like this now:
public bool ChangeEntity(MyEntity entity, int otherTableForignKey)
{
//first I verify that the entity to update exist in the system
var entitytochange = entityContext.MyEntities.FirstOrDefault(e => e.Name == entity.Name);
if (systemtochange == null) return false;
try
{
entity.ForignEntity = entityContext.ForeignEntities.FirstOrDefault(f => f.Key == otherTableForignKey);
//code for updating the entity should go here, but I'm nor sure what
entityContext.SaveChanges();
return true;
}
catch (Exception exc)
{
return false;
}
}
I tried many different combinations of ApplyCurrentValues, Attach, setting ObjectState to Modified and so on, but I get either the error message that I can't add a new entity with the same key as an existing entity, that the object state of the new object can't be Added and so on.
So my question is: What is the best way to do this without writing code that looks like a big hack.
The only way I got this working now was to just set the properties of entitytochange manually with the properties of entity, but it is a bad solution since any added properties to MyEntity will break the code if I don't remember to add code in this method as well, and it seems there really should be another way that is better.
EDIT
When I put entityContext.MyEntities.ApplyCurrentValues(entity); where my comment is put above, I get the following exception on this line:
The existing object in the ObjectContext is in the Added state. Changes can only be applied when the existing object is in an unchanged or modified state.
However, if I remove this line above entity.ForignEntity = entityContext.ForeignEntities.FirstOrDefault(f => f.Key == otherTableForignKey); then the ApplyCurrentValues works without any problems.
Why would me setting the ForeignEntity of the object set it to Added state? So it seems that setting a Property on the Detached entity, attaches it to the context with a state of added?
The following scenario is, I would say quite common and although I know one way of resolving it but it lack elegance.
The example I'm giving is based upon https://github.com/sharparchitecture/Sharp-Architecture-Cookbook.
The application I'm coding is an ASP.NET MVC application and has to support multiple users working on the same object.
The following scenario is an edge case but nevertheless a valid one.
Say you have two users working on the same object and whether the dB row can be updated depends upon the value of a particular field. To make it more concrete, let's say you have a Product and to keep things simple, this Product has "Name" and "QuantityInStock" fields.
Say that initially, there are 10 items of the Product and User1 and User2 want to buy this product. When both users are presented the initial form they are told that there are 10 of these items in stock. Now User1 buys all 10 items while User2 goes to have a coffee. So User1's transaction goes through no problem.
Then User2 comes back after his coffee in the belief that there are still 10 items in stock. So he tries to buy 1 but he must be prevented from doing so since there are no items in stock.
So this problem can be solved by using ASP.NET DataAnnotations validation and this will catch the majority of cases. However, in our edge case, say that User1 and User2 perform the same operation but within a fraction of a second such that when User2 submits the form, it passes the ASP.NET Validation but by the time it gets to the persistence layer, the QuantityInStock is 0.
The solution to this is to perform the validation at the latest moment as possible i.e. just before calling the Update method.
Now for some code.
public ProductModel CreateOrUpdate(ProductModel productModel)
{
var currentProductModel = Get(productModel.Id);
var currentQuantityInStock = currentProductModel.QuantityInStock;
if(currentProductModel.QuantityInStock !=0 && productModel.QuantityInStock >= currentQuantityInStock )
{
currentProductModel.QuantityInStock= productModel.QuantityInStock;
currentProductModel.Name = productModel.Name;
this.productModelRepository.SaveOrUpdate( currentProductModel );
return productModel;
}
else
{
//Raise an exception
}
}
Now, the fact that I'm calling:
var currentProductModel = Get(productModel.Id);
means that if I were to just do this:
this.productModelRepository.SaveOrUpdate( productModel );
would cause an exception:
a different object with the same identifier value was already associated with the session: 1
Hence, I have to copy all of the values from productModel to currentProductModel. It's fine when using something like Automapper but still kind of feels wrong to me in the sense that I feel I should just be able to save productModel as is without having to transfer the data from one object to another.
Moreover, having to do the same validation twice, once using DataAnnotation and another time just before updating violates the DRY principle.
The point is that I feel like I'm missing a trick but don't quite know where to start and what to investigate.
This to me is a simple problem but coming up with a nice elegant solution is something else. So the question is how have you dealt with this simple case in the past? Am I overthinking this?
have you tried optimistic Locking with Version?
// Fluent mapping
public EntitiyMap()
{
OptimisticLock.All(); // all properties musn't be changed in db when saving
// or
OptimisticLock.Dirty(); // only dirty properties musn't be changed in db when saving
}
//
public ProductModel CreateOrUpdate(ProductModel productModel)
{
try
{
// productModel is already validated and updated
this.productModelRepository.SaveOrUpdate( productModel );
return productModel;
}
catch (StaleObjectException)
{
// somebody changed the object in database after we have read it
// Raise an exception or whatever
}
}
Update: i handled such things in another way
public void BuySomething(ProductModel productModel, int amount)
{
int tries = 5;
bool success = false;
while(!success && tries > 0)
{
if (productModel.QuantityInStock <= amount)
{
//Raise an exception
}
productModel.QuantityInStock - amount;
try
{
this.productModelRepository.SaveOrUpdate( productModel );
}
catch (StaleObjectException)
{
// somebody changed the object in database after we have read it
this.productModelRepository.Refresh(productModel);
tries--;
}
}
if (tries <= 0)
{
// Raise an exception or whatever
}
}
zero extra roundtrips if nobody changed it in between, and guaranteed serialisation of the transactions
I have a domain model that includes something like this:
public class Customer : EntityBase<Customer>, IAggregateRoot
{
public IList<Comment> Comments { get; set; }
}
public class Comment : EntityBase<Comment>
{
public User CreatedBy { get; set; }
public bool Private { get; set; }
}
I have a service layer through which I retrieve these entities, and among the arguments passed to that service layer is who the requesting user is.
What I'd like to do is be able to construct a DetachedCriteria in the service layer that would limit the Comment items returned for a given customer so the user isn't shown any comments that don't belong to them and are marked private.
I tried doing something like this:
criteria.CreateCriteria("Comments")
.Add(Restrictions.Or(Restrictions.Eq("Private", false),
Restrictions.And(Restrictions.Eq("Private", true),
Restrictions.Eq("CreatedBy.Id", requestingUser.Id))));
But this doesn't flow through to the lazy-loaded comments.
I'd prefer not to use a filter because that would require either interacting with the session (which isn't currently exposed to the service layer) or forcing my repository to know about user context (which seems like too much logic in what should be a dumb layer). The filter is a dirty solution for other reasons, too -- the logic that determines what is visible and what isn't is more detailed than just a private flag.
I don't want to use LINQ in the service layer to filter the collection because doing so would blow the whole lazy loading benefit in a really bad way. Lists of customers where the comments aren't relevant would cause a storm of database calls that would be very slow. I'd rather not use LINQ in my presentation layer (an MVC app) because it seems like the wrong place for it.
Any ideas whether this is possible using the DetachedCriteria? Any other ways to accomplish this?
Having the entity itself expose a different set of values for a collection property based on some external value does not seem correct to me.
This would be better handled, either as a call to your repository service directly, or via the entity itself, by creating a method to do this specifically.
To fit in best with your current model though, I would have the call that you currently make to get the the entities return a viewmodel rather than just the entities;
public class PostForUser
{
public Post Post {get; set;}
public User User {get; set;}
public IList<Comment> Comments}
}
And then in your service method (I am making some guesses here)
public PostForUser GetPost(int postId, User requestingUser){
...
}
You would then create and populate the PostForUser view model in the most efficient way, perhaps by the detached criteria, or by a single query and a DistinctRootEntity Transformer (you can leave the actual comments property to lazy load, as you probably won't use it)