Use the DTO that is derived from EF Entity to do the insert

I have ASP.NET MVC website and class library for Entity FrameWork

Now I need to have some data validation using IValidatableObject.

So, in my infrastructure, the class library entity is:

public class ClientA
{
    public Guid ID { get; set; }       
    public string FirstName { get; set; }      
    public string LastName { get; set; }
}

      

And in an MVC project I have:

public class Client: ClientA, IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){..}
}

      

But I got the error doing here:

Client client = new Client();
client.FirstName = "aaaa";
ClientA cl = (ClientA)client;

context.Clients.Add(cl); //error here: Type are different

      

Any ideas?

+3


source to share


5 answers


Displaying objects may be preferable for casting, for example

ClientA c1 = new ClientA
{
    ID = client.ID,
    FirstName = client.FirstName,
    LastName = client.LastName
}

      



This can of course get very tedious, especially if you have large objects, but you can use a tool like Automapper , which can be a time keeper when matching objects with the same named properties.

Also, like the other answers, there is no need for your viewmodel (which is essentially Client

) to inherit from ClientA

even though they have the same properties. Validation is not an issue for the "ClientA" class, and it would be more obvious with a more complex class, for example if you ever need to hide properties from the UI or address issues like globalization / localization.

+2


source


The first answer I will give you.

Your instinct is wrong. The validation logic MUST live in your data objects in the business layer (not the persistence layer, perhaps your problem is that you haven't allocated BL and persistence).

That is why he IValidatableObject

lives in System.ComponentModel.DataAnnotations

and not in System.Web

.



That way, when your app is a hit and you need to port it to your Windows phone (pfffhahahahaha), you don't need to rewrite your validation code.

This will also allow you to override SaveChanges()

in your DbContext to
1 to do the validation just before saving.

1 You really don't need to do this because EF will already do it for you ...

+1


source


Use composition instead of inheritance. Most programmers, after learning OOP, think only in Inheritance / Polymorphism / Encapsulation.

Instead, you can put a thing inside another thing that checks for it.

public abstract class Validator<T>
    : IValidatableObject 
{
    public T Value { get; }
    public abstract IEnumerable<ValidationResult> 
          Validate(ValidationContext validationContext);
}

      

+1


source


What you are trying to do is wrong for many reasons:

Problems

1. Security type

context.Clients.Add(cl);

      

You are trying to add a variable ClientA

to the customer collection. This is not possible because client is a supertype of ClientA. The C # compiler cannot convert a variable ClientA

to its super type Client

, even though it cl

actually contains a Client object. Why are you submitting to ClientA anyway? It should compile if you add the variable insteadClient

2. Entity Framework

...Client: ClientA ...

      

This default line will cause EF to treat the hierarchy as two different entities using TPH (table for hierarchy). This is definitely not what you are trying to achieve as you are using Client

as a check

3. Encapsulation

... Client: ClientA, IValidatableObject

      

As @Aron stated the validation logic is the ability to map the Client (or ClientA) class. So there should only be one class

public class Client : IValidatableObject

      

This class is responsible for storing its properties, validation and logic. IValidatableObject

an interface it thinks is the main library, so feel free to implement it in your Entity

Enough problems with solutions ...

Decision

1. Only one client class

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 should think when building the domain level

But, unfortunately, this layer does not live on its own. It should be consumed by your presentation layer, and perhaps you split this class in two because your method Validate

contains presentation logic

So this gives us the following solution ...

2. ViewModels

//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){ ... }
}

      

Please note that ClientViewModel does not inherit from Client. It retains the same properties as the object, but they are not linked in the inheritance hierarchy. Thus, you have two ways to create an instance ClientViewModel

from an object Client

.

  • Manually use a constructor, BuilderMethod, or anything else that requires calling new ClientViewModel()

    somewhere and manually traversing properties one by one or with reflection (please don't do reflection)

  • With OOM (Object-Object Mapper): Be Automapper one of the most popular libraries. With this option you can register a card with Mapper.CreateMap<Client, ClientViewModel>();

    and get a new ClientViewModel

    one Client

    fromvar clientViewModel = Mapper.Map<ClientViewModel>(client);

Q. But wait, what about the logic in the Entity.

and. You can still make object a IValidatableObject

too. this way it will handle pure domain validation, leaving the view model with UI validation.

Stay tuned for more solutions ...

+1


source


You can use AOP instead of inheritance.

Look at projects like PostSharp to add your validation logic to your data class after compilation.

0


source







All Articles