I'm looking for some advice. I'm working with EF 5 and I have a generic repository which handles all of the CRUD transactions with the database. This works fine, But i want to add a "Last Gap" safeguard to ensure that the entity is valid before the Data Access Layer attempts changes in the database.
Right before I do something like this :-
DataLayer.Create<TEntity>(entity);
I want to Validate the entity and throw an exception if the validation fails.
What would you guys use as the preferred method?
Using Data Annotations
You can use data annotations directly in your entity. With data annotations, EF will validate the property for you and if it is not valid, an exception will be thrown.
For example, if you want Name to be required, you can do something like:
public class Person
{
[Required]
public string Name { get; set; }
// other members
}
Aside from validation, EF will also set the corresponding column to be not null instead of the default null for strings.
Using the Fluent API
If you don't want to litter your entities with data annotations, you can use the fluent API instead. Following is the equivalent of the above code:
public class MyContext : DbContext
{
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Entity<Person>().Property(p => p.Name).IsRequired();
}
// other members
}
My answer applies to EF Code First and may not apply for other workflows.
Sometimes you have to go to the database to check whether inserting or updating an entity keeps the repository in a valid state - such as when you need to ensure the natural key is unique. That isn't currently handled by a data annotation or the Fluent API, although it has been discussed. See unique constraints in entity framework And this work item.
In the meantime, when you have to go to the database then DbContext will be involved somewhere, and DbContext has an Overridable method called ValidateEntity. See this article: Entity Framework Validation.
I put the code I use in another answer here
And more about how I've structured the validation in MVC here.
I wouldn't do validation at the DAL, but if you do, you might be interested in Validation with the Data Annotation Validators
Related
Context
I am trying my own spin at DDD architecture. The key difference from other projects I've seen is that I am not using my Domain models as data entities, but instead I have separate models I called Stores that map from the Domain models and represent the state of the database.
If you're not familiar with DDD the idea is to completely decouple the core business logic from other elements of the application, such as database. In order to achieve that I have defined Domain models that contain the business logic and validation and then Entity Models, which represent the same sate as the Domain models (striped of business and validation logic) but also represent EF specific relationship properties.
Problem
The EF operations work for simpler operations. Let's say we have a Contest, which can contain several Trials.
Example in pseudo-code:
contest = new Contest
contest.Add(new Trial(1))
contest.Add(new Trial(2))
data.Save(contest) // performs mapping to ContestEntity and calls dbContext.Add
// So far so good
contestWithTrials = data.Get() // contest comes with 2 Included Trials
contestWithTrials.Add(new Trial(3))
data.Save(contestWithTrials) // performs mapping, calls dbContext.Update and tries to save but fails.
The error is:
The instance of entity type 'Trial' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked
or
Attempted to update or delete an entity that does not exist in the store
For some reason the mapping confuses EF and it tries to re-create the already existing Trial, but I cannot understand why - I can see that the entities are added correctly in DbSet.Local just before SaveChanges is called, but still it throws.
I've setup a PoC branch here. It's a console application with minimal reproducible example per Progrman's advice bellow. Since the setup requires several packages I think it's better in a repo instead of a single file.
It is a good idea to separate domain model classes containing business logic from infrastructure dependencies, in your case database concerns. But as you are utilizing EF Core you can dismiss your Entity Models altogether as EF Core is already designed in a way that allows you to separate domain and database concerns.
Let's look at an example from the Microsoft powered EShopOnWeb project.
The domain model class Order (an aggregate root of the Ordering context) contains the domain logic and is structured so that business invariants can be adhered to the best way.
When you look at the Order class you see that it has no database or other infrastructure dependencies. The domain model class is also located in the
https://github.com/dotnet-architecture/eShopOnWeb/blob/master/src/ApplicationCore/Entities/OrderAggregate/Order.cs
of the solution.
public class Order : BaseEntity, IAggregateRoot
{
private Order()
{
// required by EF
}
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
{
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
Guard.Against.Null(shipToAddress, nameof(shipToAddress));
Guard.Against.Null(items, nameof(items));
BuyerId = buyerId;
ShipToAddress = shipToAddress;
_orderItems = items;
}
public string BuyerId { get; private set; }
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now;
public Address ShipToAddress { get; private set; }
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
public decimal Total()
{
var total = 0m;
foreach (var item in _orderItems)
{
total += item.UnitPrice * item.Units;
}
return total;
}
}
In order to map the business model to the database in order to persist the data built-in functionality from EF Core can be used by simply defining a corresponding configuration class as shown below. To separate it from the business layer it is, amongst other things, also located in the infrastructure layer (or data layer) of the project.
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
var navigation = builder.Metadata.FindNavigation(nameof(Order.OrderItems));
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
builder.OwnsOne(o => o.ShipToAddress, a =>
{
a.WithOwner();
a.Property(a => a.ZipCode)
.HasMaxLength(18)
.IsRequired();
a.Property(a => a.Street)
.HasMaxLength(180)
.IsRequired();
a.Property(a => a.State)
.HasMaxLength(60);
a.Property(a => a.Country)
.HasMaxLength(90)
.IsRequired();
a.Property(a => a.City)
.HasMaxLength(100)
.IsRequired();
});
}
}
The only thing required by EF Core is the private parameterless constructor in the Order domain model class which is, from my point-of-view, an acceptable trade-off considering you can save the effort of writing database mapping classes.
If I am constrained by other frameworks that do not provide such capabilities I often also go a similar way as you are doing now, but in case of having the features of EF Core at hand I would suggest to reconsider you approach an give EF Core configuration features a try.
I know this is not the exact answer to the technical problem you are facing but I wanted to show you an alternative approach.
Your problem is, that as you are loading your entities from the database EF Core starts tracking them in its change tracker to identify changes you make to the loaded entities as soon as SaveChanges() is called. This behaviour works fine as long as you modify the actual object that was loaded by EF.
What you are doing is: loading a DatabaseTrial (lets say it has id 1), then mapping it to DomainTrial, potentially modify it, and then mapping it to NEW instance of DatabaseTrial which also has id 1 and adding it to the context. This confuses EF because it now has two diffent objects (by reference) which both have id 1. This is not allowed as ids have to be unique (if EF did not throw this exception which DatabaseTrial object should used to update the database entry?).
The solution is quite simple: Just use AsNoTracking() when loading the entities from the database. This will prevent the change tracker from keeping track of the originally loaded object and as soon as Update() is called only the new entity will be tracked in the "Modified" state and used to update the database entry. As the documentation states:
For entity types with generated keys if an entity has its primary key value set then it will be tracked in the Modified state. If the primary key value is not set then it will be tracked in the Added state. This helps ensure new entities will be inserted, while existing entities will be updated. An entity is considered to have its primary key value set if the primary key property is set to anything other than the CLR default for the property type.
this will also work for your Trial which is being added to your Contest as its primary key is set to the default value after creation and EF will know that it must be inserted.
We are reviewing two different methods in generic repository patterns.
Currently, want to map primary keys to Ids. The purpose of this is to map to the Generic Repository Interface which utilizes Id. Two solutions are provided below.
What are performance implications of .FindPrimaryKey().Properties. Does it cause a schema lock on database table in trying to find the primary key? Does it cause any application slowness?
How does it compare in performance vs Partial Class Method Solution 2?
What option is better performance-wise?
Note: Architects demand the use of repository pattern at the workplace, so implementing it. Know there is debate surrounding this issue, but not my call.
Scaffolded Model Example:
namespace Datatest
{
public partial class Property
{
public int Property { get; set; }
public int DocumentId { get; set; }
public string Address { get; set; }
}
}
Sample Generic Base Repository for all tables:
public T Get(int id)
{
return Table.Find(id);
}
public async Task<T> GetAsync(int id)
{
return await Table.FindAsync(id);
}
public T Single(Expression<Func<T, bool>> predicate)
{
return All.Single(predicate);
}
public async Task<T> SingleAsync(Expression<Func<T, bool>> predicate)
{
return await All.SingleAsync(predicate);
}
public T FirstOrDefault(int id)
{
return All.FirstOrDefault(CreateEqualityExpressionForId(id));
}
Solution 1: FindPrimaryKey()
Generic Repository in C# Using Entity Framework
use EF FindPrimaryKey()
var idName = _context.Model.FindEntityType(typeof(TEntity))
.FindPrimaryKey().Properties.Single().Name;
Solution 2: Partial classes Mapping
Net Core: Create Generic Repository Interface Id Mapping for All Tables Auto Code Generation
public partial class Property: IEntity
{
[NotMapped]
public int Id { get => PropertyId; set => PropertyId = value; }
}
Regarding the first approach (using EF Core metadata services):
First, EF Core is ORM (Object Relational Mapper), with most important here is Mapper.
Second, it uses the so called code based model, which means all the mappings are provided by code and not the actual database (even though the model is created by reverse engineering of an existing database).
In simple words, EF Core creates at runtime a memory data structure containing the information (metadata) about classes and properties, and their mappings to database tables, columns and relationships. All that information is based on pure code model - the entity classes, conventions, data annotations and fluent configuration.
All EF Core runtime behaviors are based on that metadata model. EF Core uses it internally when building queries, mapping the query results to objects, linking navigation properties, generating create/update/delete commands and their order of execution, updating temporary FK property values after getting the real autogenerated principal key values etc.
Hence the metadata model and discovering services (methods) use optimized data structures and are (has to be) quite efficient. And again, no database operations are involved.
So the first approach is quite efficient. The performance impact of obtaining the PK property name via metadata service is negligible compared to actual query building, execution and materialization.
Also the performance of the first approach is similar to EF Core Find method which you are using in another method. Note that when calling Find method you just pass the PK value(s) and not the properties. So the method implementation should somehow know how to build the Where expression, right? And what it does internally is very similar to the suggested snippet.
Regarding the second approach:
It's simply not comparable because it doesn't work. It's possible to use base class/interface, but only if the actual property name is mapped - like all classes have Id property, and it's mapped to different column name in the database tables using [Column] data annotation or HasColumnName fluent API.
In your example, the Id property is [NotMapped] (ignored). Which means EF Core cannot map to the table column. The fact that your are mapping it to another property via code (property getter/setter) doesn't matter. EF Core is not a (de)compiler, it can't see your code, hence cannot translate a LINQ query using such properties to SQL.
Which in EF Core 2.x leads to either client evaluation (very inefficient, reading to whole table and applying the filter in memory), or exception if client evaluation is configured to do so. And in EF Core 3.0+ it will always be an exception.
So in case you don't remove properties like PropertyId and map the property Id (which would be hard with "database first" models), the second "approach" should be avoided. And even if you can map the actual Id property, all you'll save would be a few milliseconds. And again, when using Find you don't bother about performance, why bother with methods that uses the same (or similar) approach.
Is it possible to have Entity framework generate a table, from a model, with the column marked as not null without using the [Required] annotation on the model's property?
Reason:
The object is posted to an api and I check ModelState.IsValid in the controller. The property is supposed to be generated server side and not come from outside, but if I have the property [Required] the ModelState.IsValid is false (in which case I return with a BadRequest(ModelState);).
Can I tell EF to make the column not null in some other way?
I guess another solution would be to expect another object(some sort of DTO) to be sent to the api and then do a mapping. But that object would look exactly the same save for this single property, which makes it seem a bit unnecessary, right?
Use Fluent API and IsRequired method in your DbContext class like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<YourEntity>().Property(t => t.YourProperty).IsRequired();
base.OnModelCreating(modelBuilder);
}
If I were you I would not use Entity Framework's entities directly, I would use a DTO first and map it into a EF Entity, why? Because your DTO and EF Entity have not the same responsibility.
DTO : Data transfer object, so just use it to transfer data
EF Entity : it's the model binding to your database.
I have the following problem: I have a hierarchy of entities that uses inheritance. I have a two identical fields( the name and type is the same ) in two of the sub-entities. When I try to "Update-Database -Force" on the project EF5 complains that there are there is already a column with name X.
The way EF5 generates the tables is that it actually generates single table and puts there all the fields of the base entity plus the all the fields of the derived entities.
Is there a way to force a different database column name from the property name.
Are there any other solutions( I know it might be architectural problem to duplicate data but making this common will introduce more complex database hierarchy that I don't want to use ).
Thanks:)
This can be done in one of two ways, either using Fluent API or property attributes on your class properties.
[Column("ColumnName")]
public string PropertyName
{
get;
set;
}
See MSDN - ColumnAttribute Class for more details on the column attribute.
Otherwise, use Fluent API. Within your context class-
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<YourClass>().Property(yc => yc.PropertyName).HasColumnName("ColumnName");
See MSDN - HasColumnName extension method for more on this method.
The article linked by Baximilian will be useful in learning more about this.
If I understand you correctly, you need to change column name in DB for field, so you can use ColumnAttribute.
You can find more information here
I am implementing a project using mvc 4 and entity framework.
where i used data first approach. and i am implementing a partial class for my models for various business logic.
my question is how can i set validation rule on my properties. shown in below.
[Required]
public string FirstName { get; set; }
if i manually added this code "[Required]" on a property (entity framework generate models).
and then if i need to change model for database changes. then all my validation rule is gone
how can i over come this problem, without using code first approach.
As you've found out you should never edit the generated files since changes are lost when you regenerate them.
A better architecture than to use the entities as models for your views is to insert a separate View Model between the view and the entity. The view model should correspond closely to the needs of the view and often retrieves data from several underlying entities.
The attributes then goes on the view model properties instead of on the entities.
View models also remedies the risk of mass assignment vulnerabilities in your application, which are particularly dangerous if you are using lazy loading in your entities.
Another way around this (using CodeFirst) is to use a Fluent Validation. The CustomerValidator will always point at the regenerated Customer class (unless you change the Customer class name obviously)
using FluentValidation;
public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator {
RuleFor(customer => customer.Surname).NotNull();
}
}