Reputation: 183
I have ASP.NET MVC website and a Class Library for Entity FrameWork
Now i need to had some data valitation using IValidatableObject.
So i have in Class Library entity framework this:
public class ClientA
{
public Guid ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And in MVC project I have:
public class Client: ClientA, IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){..}
}
But i got error make cast here:
Client client = new Client();
client.FirstName = "aaaa";
ClientA cl = (ClientA)client;
context.Clients.Add(cl); //error here: Type are different
Any ideas?
Upvotes: 3
Views: 1127
Reputation: 4450
Mapping the objects might be preferable to casting, for example
ClientA c1 = new ClientA
{
ID = client.ID,
FirstName = client.FirstName,
LastName = client.LastName
}
This of course could get very tedious, especially if you have big objects, but you could use a tool like Automapper, which can be a time saver when mapping objects with same named properties.
Also, as other answers have suggested, there is no need for your view model (that's what Client
essentially is) to inherit from ClientA
, even if they have the same properties. Validation is not a concern of class 'ClientA' and this would be more obvious with a more complex class, for example if you ever need to hide properties from the UI or address concerns like globalization/localization.
Upvotes: 2
Reputation: 17804
What you are trying to do is wrong for many reasons:
context.Clients.Add(cl);
You are trying to add a ClientA
variable to a Client collection. This is not possible since Client is a superType of ClientA. The C# compiler cannot upcast ClientA
variable to its super type Client
even when cl
actually holds a Client object. Why are you casting to ClientA anyway? It should compile if you add client
variable instead
...Client: ClientA ...
This line by default will make EF treat the hierarchy as two different entities using TPH (Table per Hierarchy). This is definitely not what you are trying to achieve since you are only using Client
as validation
... Client: ClientA, IValidatableObject
As @Aron has stated validation logic is the resposability of the Client (or ClientA) class. Therefore there should be only one class
public class Client : IValidatableObject
This class is responsible for holding its properties, validation and logic. IValidatableObject
interface its considered a core library so feel free to implement it on your Entity
Enough with the problems now the solutions...
public class Client : IValidatableObject
This solution is simple, well encapsulated, works great with EntityFramework and keeps logic where it should be.
This is how you must think when constructing a Domain layer
But unfortunately this layer doesn't live by itself. It should be consumed by your presentation layer and probably you were splitting this class in two because your Validate
method contains presentation logic
So this give us the next solution...
//In your domain layer
public class Client
{
....
}
//In your presentation layer
public class ClientViewModel : IValidatableObject
{
//...
// Same properties as Client
//...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){ ... }
}
Notice how ClientViewModel does not inherit from Client. It keeps the same properties as the entity but they aren't related in an inheritance hierarchy. So you have two ways of instantiating a ClientViewModel
out of a Client
entity.
Manually using a Constructor, BuilderMethod, or anything else that involves calling a new ClientViewModel()
somewhere and manually coping the properties, one by one or with reflection (Please don't do reflection)
With a OOM (Object-Object Mapper): Being Automapper one of the most popular libraries. With this option you can register a map with Mapper.CreateMap<Client, ClientViewModel>();
and get a new ClientViewModel
from a Client
with var clientViewModel = Mapper.Map<ClientViewModel>(client);
Q. But wait, what about the logic in the Entity.
A. You can still make the entity an IValidatableObject
too. that way it will handle pure domain validation, leaving the viewmodel with the UI validation.
Stay tuned for more solutions comming soon...
Upvotes: 1
Reputation: 15772
First answer I am going to give you.
Your instinct is wrong. The validation logic SHOULD live in your data objects in the Business Layer (not the persistence layer, perhaps your issue is that you didn't separate out BL and persistence).
This is why IValidatableObject
lives in System.ComponentModel.DataAnnotations
and not in System.Web
.
This way when your application is a hit and you need to port it to windows phone (pfffhahahahaha), you don't need to rewrite your validation code.
It would also allow you to override 1 run your validation just before you save.SaveChanges()
on your DbContext to
1 You actually don't need to do that because EF already WILL do that for you...
Upvotes: 1
Reputation: 15772
You can use AOP instead of inheritance.
Look at projects like PostSharp to add your validation logic to your data class after you compile.
Upvotes: 0
Reputation: 15772
Use composition instead of inheritance. Most programmers after learning OOP think only in Inheritance/Polymorphism/Encapsulation.
You can instead put a thing inside another thing that validates it.
public abstract class Validator<T>
: IValidatableObject
{
public T Value { get; }
public abstract IEnumerable<ValidationResult>
Validate(ValidationContext validationContext);
}
Upvotes: 1