Ways to map database with Entity Framework? - c#

How many ways are there to map a database with Entity Framework in .NET?
I understand there is code-first and database-first (using .EDMX wizard for example).
Within the context of database-first, can I map my tables and relationships manually without using .EDMX? How many ways exist and which do you recommend?
Are there libraries for manual table mapping, which are the best?

I think there is not a best way, but maybe there is a way that fits best your needs.
I'll try to explain the ways you have, than you can choose the best for you.
On high level, there are two options:
DB first: you define the database and let a tool to create your model classes
Code first: you define your classes and let EF manage the tables for you
Mainly DB first is the best for:
Map an already existing database: in this situation your DB is already designed, so you have only to map entities
Your focus is the DB structure: in this situation, better if you design your DB as you want, then let a tool to map your entities
Code first is the best when you don't mind about the DB but you want to think about the object model. Of course, you can change how the DB is generated using data annotation or any other way EF gives you, but the focus for you has to be the object model.

Hi yes can can absolutely Map a database from EF. It is called scaffolding. What it does is it creates the database as models and required files for you.
Once you open the package manage or cmd you can type the following one-liner to scafford you database:
CMD:
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
Package Manager:
Scaffold-DbContext "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
See the EF Core tutorial on it on the official windows website:
https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding?tabs=dotnet-core-cli
And for EF6 there is a great tutorial right here:
https://www.illucit.com/en/asp-net/entity-framework-7-code-first-migrations/

For full manual control with a Database-First project you can leverage a combination of convention, attributes, and/or entity configurations to configure the entities. Scaffolding I find works 90% of the time, but usually there will be some aspect of a production schema, especially where you don't have the flexibility to change the schema to make it more ORM-friendly, that scaffolding doesn't quite handle.
Also, if you're adopting something like bounded contexts (think DbContexts with fit-for-purpose mappings) and want to customize how tables/views map to entities, then it helps to be more explicit with the mapping. For example, for general entities I will map navigation properties, but in cases where I want raw performance over larger operations I will want to forgo declaring navigation properties and work strictly with FK columns. The less "mapping" a DbContext has to worry about and fewer entities it is tracking, the faster it performs.
Attributes: Here you declare your entity classes and use the appropriate attributes to describe the table, key, and other aspects such as column renames etc.
I.e.
[Table("tblOrders", "App")] // tblOrders table in App namespace
public class Order
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int OrderId { get; set; }
[Column("OrderNum")]
public string OrderNumber { get; set; }
public string OrderRef { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
This works for the 90-%ile of cases where you want to set up entities. For simple columns that you don't need to rename etc. you don't need to add attributes and leave it to convention.
Entity Configuration: The commonly referenced means of doing this is to use the DbContext's OnModelCreating override and use modelBuilder to configure the entities. For smaller system with a couple handfuls of entities this can be manageable, but for larger systems this can get rather bloated since everything ends up in one method, or a chain of method calls to break it up.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Order>()
.ToTable("tblOrders", "App")
.HasKey(x => x.OrderId)
.Property(x => x.OrderId)
.HasDatabaseGenerated(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Order>()
.Property(x => x.OrderNumber)
.HasColumnName("OrderNum);
modelBuilder.Entity<Order>()
.HasRequired(x => x.Customer)
.WithMany(x => x.Orders)
.HasForeignKey(x => x.CustomerId);
}
The lesser documented option is to leverage EntityTypeConfigration<TEntity> (IEntityTypeConfiguration<TEntity> in EF Core)
public class OrderConfiguration : EntityTypeConfiguration<Order>
{
public OrderConfiguration()
{
ToTable("tblOrders", "App");
HasKey(x => x.OrderId)
.Property(x => x.OrderId)
.HasDatabaseGenerated(DatabaseGeneratedOption.Identity);
Property(x => x.OrderNumber)
.HasColumnName("OrderNum");
HasRequired(x => x.Customer)
.WithMany(x => x.Orders)
.HasForeignKey(x => x.CustomerId);
}
}
From there the DbContext just needs to be initialized to load the entity type configurations. This is done in the OnModelCreating which you can do explicitly, or add all Configurations by assembly.
modelBuilder.Configurations.AddFromAssembly(GetType().Assembly);
Personally, I default to declaring EntityTypeConfigurations for all entities as I prefer to rely on convention as little as possible. Being explicit with the configuration means you have something to investigate and work with where a convention doesn't work the way you expect, and it allows you to declare mappings for things like ForeignKeys without declaring FK properties in the entities. (Highly recommended to avoid two sources of truth about a relationship, being the FK and the navigation property)
My projects will commonly have a .Data project where I will keep the Entities, DbContext, and Repositories for a project. The EntityTypeConfiguration instances I place under /Entities/Configuration. They could just as easily be housed in the entity class files, as internal members of the entity class itself, or nested under the Entity class. (I.e. using a plugin like NestIn)

Related

Avoid using discriminator in EF Core when dealing with inheritance

Problems started after switching from EF to EF Core (3.1).
I have a base abstract class and a derived class which is created dynamicaly in runtime (using reflection).
entity configuration of my base type was (EF):
ToTable("TableName", "dbo");
HasKey(t => t.Id);
HasRequired(t => t.prop1).WithMany().HasForeignKey(t => t.prop1);
Property(t => t.prop2).IsRequired();
Property(t => t.prop3).IsRequired();
I built base class with this configuration and dynamic class with modelBuilder.Entity(type).
And everything worked fine. I could get instances of my base class using context.Objects and instances of the dynamic class using Activator.CreateInstance(type).
Now I have same configuration but for EF Core:
builder.ToTable("TableName", "dbo");
builder.HasKey(t => t.Id);
builder.HasOne(t => t.prop1).WithMany().HasForeignKey(t => t.prop1);
builder.Property(t => t.prop2).IsRequired();
builder.Property(t => t.prop3).IsRequired();
But in EF Core getting objects from context gives an error "Invalid column name 'Discriminator'". Yes, I don't have discriminator column in my table (apparently it's required when TPH pattern is used) but it worked perfectly without it in EF. How did EF dealt with inheritance in that case? Moreover, creating such column and populating it with the same data (derived class name) seems to be useless. It feels like there should be something I'm missing.
So, my question is:
Is there any way to fix the problem without creating a discriminator column?
A default EF TPH will generally go across to EF Core without too many issues, however the customisation options are different between the two, for instance in core we can now easily manipulate the discriminator via fluent notation: https://www.learnentityframeworkcore.com/configuration/fluent-api/hasdiscriminator-method
Check that your base class is NOT abstract: https://stackoverflow.com/a/34646164/1690217
If your base class IS abstract then you will have to manually configure the Discriminator column: https://www.learnentityframeworkcore.com/inheritance/table-per-hierarchy#configuration
Also check that your database schema from the previous EF migrations actually has the Discriminator column and that it is a string type, the actual values should be the name of the types, however it is possible that you have configured or applied conventions elsewhere that override the default behaviour (in either the EF or the EF Core implementations)
If you include the actual schema in the database or the migration entries that build the tables you might get a more definitive answer.

Entity Framework, Fluent API mapping, should it be done somewhere other than OnModelCreating?

The Question:
My question is if I'm going about this correctly, or whether there's a tidier/more-robust way of mapping entities with Fluent API? Mainly, should I be doing all the mapping within the overidden OnModelCreating method? Secondly, is there any reason not to do it this way? And finally, if my datamodel is working, and I'm able to successfully access the database, why would Entity Framework Power Tools not be able to generate a read-only .edmx file?
Here's the back-story and examples of what I'm doing:
So I have a working code-first data model of ~70 very interconnected entities. I reverse engineered it from an existing database that had been using and Entity-Framework designer to relate objects and map them to the SQL db.
I modeled my approach on the tutorials I've done, and I'm using Fluent API for mapping, not Data Annotations.
So I have all the entity models defined as POCOs, each in their own source file. I have another class inheriting DbContext, we'll call this MyDb. Inside MyDb I have the ~70 DbSet properties
public virtual DbSet<SomeEntity> SomeEntities{ get; set; }
public virtual DbSet<OtherEntity> OtherEntities{ get; set; }
//...You get the idea, there another few dozen of these below
public virtual DbSet<LastEntity> LastEntities{ get; set; }
//Well that was tedious
Following the DbSets that provide access to all the entities, I have ~6000 lines of fluent API mappings inside protected override void OnModelCreating(DbModelBuilder mb) , which is how I learned to do it in the examples, and in the smaller test projects I've done.
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<SomeEntity>()
.ToTable("SomeEntities")
.HasKey(se => se.ID);
mb.Entity<SomeEntity>()
.Property(se => se.ID)
.HasColumnName("ID")
.HasColumnType("bigint")
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
mb.Entity<SomeEntity>()
.Property(se => se.Contents)
.HasColumnName("Content")
.HasColumnType("varchar")
.IsMaxLength()
.IsUnicode(true)
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
mb.Entity<SomeEntity>()
.HasMany(se => se.OtherEntities)
.WithRequired(oe => oe.SomeEntity)
.HasForeignKey(oe => oe.SomeEntity_ID);
//and so on, there are another ~70 of these that go on for the
//next 6000 lines
}
Obviously all my entities aren't this simple, and there are many that are unconventional in their property naming, table naming, table types, and so on; that's why I've been quite verbose with the Fluent API.
#Gert Arnold did a good job of answering your first questoins. Regarding the EF Power Tools, they're probably just getting hung up while trying to discover your model. You can do the same thing manually using the following code snippet.
using (var db = new MyContext())
using (var writer = XmlWriter.Create("MyContext.edmx"))
{
EdmxWriter.WriteEdmx(db, writer);
}
I think in terms of performance there's not a markable difference whether you do the configuration by separate EntityTypeConfiguration classes or in OnModelCreating only.
But computer languages were not invented to help computers, but us, binary dyslectic types. Therefore, I would prefer EntityTypeConfigurations, because that helps you to get to a specific mapping quicker than scrolling through 6000 lines of code. We developers spend a lot of time referring back to what we or others wrote before.
I think the issue about EF Power Tools not generating a read-only .edmx file should be a different question. It requires more details on that specific problem.

EFCodeFirst : The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects

I am trying to find out what is causing this error, I have listed some of the relevant areas of my code that should hopefully help answer my problem.
The recipe entity's members collection is as shown below:
public virtual IList<Member> Members { get; set; }
and here is the Recipes collection on the member entity:
public virtual IList<Recipe> Recipes { get; set; }
I do the below when creating my DbContext in order to make a many-to-many relationship in a separate table
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// have to specify these mappings using the EF Fluent API otherwise I end up with
// the foreign key fields being placed inside the Recipe and Member tables, which wouldn't
// give a many-to-many relationship
modelBuilder.Entity<Recipe>()
.HasMany(r => r.Members)
.WithMany(m => m.Recipes)
.Map(x => {
x.ToTable("Cookbooks"); // using a mapping table for a many-to-many relationship
x.MapLeftKey("RecipeId");
x.MapRightKey("MemberId");
});
modelBuilder.Entity<Recipe>()
.HasRequired(x => x.Author)
.WithMany()
.WillCascadeOnDelete(false);
}
I also seed my database when the model changes and all I have had to do is add to a recipe's member collection and it seems to be able to sort the rest out for me and place the relevant keys in my cookbook relationship table.
This is some of the code in my recipe controller action that performs the work:
var memberEntity = memberRepository.Find((int)memberId);
var recipeEntity = recipeRepository.Find(recipeId);
recipeEntity.Members.Add(memberEntity);
recipeRepository.InsertOrUpdate(recipeEntity);
recipeRepository.Save();
Here is the insert or update method on my Recipe repository
public void InsertOrUpdate(Recipe recipe)
{
if (recipe.Id == default(int))
{
// New entity
context.Recipes.Add(recipe);
} else
{
// Existing entity
context.Entry(recipe).State = EntityState.Modified;
}
}
I get an error of "InvalidOperationException : The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects." on this line:
context.Entry(recipe).State = EntityState.Modified;
Does anyone know why that would happen? Do I have to add the member to the recipe and vice versa to get this to work? I'm not sure what the problem is because the recipeEntity seems to be the correct one.
Any help would be appreciated, thanks.
EDIT
The context is being created in each repository (RecipeRepository & MemberRepository) as shown, so I presume this is the problem in that a different context is being used for each .Find() request? and that causes problems?
private EatRateShareDbContext context = new EatRateShareDbContext();
I'm not sure this is the solution but it seems like you're using different contexts in your repository.
First make sure your have the same context for each lifetime. lifetime could be different based on your project type. (e.g. for web projects, usually it is the same for each HttpContext). You can use IoC to manage your context lifetime. Good IoC libraries for .Net are autofac and Castle Windsor
Also, I think your call to InsertOrUpdate method is unnecessary (unless you're calling Find method with no tracking.) just remove the line and see if it works:
var recipeEntity = recipeRepository.Find(recipeId);
recipeEntity.Members.Add(memberEntity);
recipeRepository.Save();
One easy way to share your DbContext per HttpRequest is mentioned here.
If you are using AutoFac, you must add SingleInstance() to your register code.
Example:
builder.Register(a => new EntityContainer()).As().SingleInstance()
Where EntityContainer is your DbContext

When using Entity Framework Code First, is it acceptable to add properties to a class that are not mapped to a database table at all?

Just as the title states, Can I add an extra property to one of my POCOs that does not map to a DB column(database was created first), and will not be persisted. This property will only be used within the application and never needs to be persisted.
Are there any extra measures to take to accomplish this besides defining the property as normal?
Yes, you absolutely can do that. Here's an example from a configuration class I have:
public class ForCommentEntities:EntityTypeConfiguration<Comment> {
public ForCommentEntities(String schemaName) {
this.HasRequired(e => e.SystemUser)
.WithMany()
.Map(m => m.MapKey("SystemUserID"));
this.Ignore(e => e.Remarks);
this.ToTable("Comment", schemaName);
}
}
The this.Ignore call is the important part. It takes a lambda expression to one of the properties on your class. This is part of what makes EFCF great (IMO) as it keeps configuration detail out of your POCOs.
The configuration class would be used like this in your Context:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
var schemaName = Properties.Settings.Default.SchemaName;
modelBuilder.Configurations
.Add(new Configuration.ForCommentEntities(schemaName))
// ...other configuration options here
;
}
The nice part about Code First and POCO is you can now have business objects which are used by EF without the need of a mapper (e.g. AutoMapper or your own). Also means not having to decorate your objects with EF attributes and more (hence Yuck's answer above). However a additional advantage is, yes, the ability to add methods or properties to the object. An example would be a collection (e.g. addresses) and you would like to have a sorted or filtered projection. Another would be business rule validation before calling SaveChange(). As we all know, the possibilities are endless but the point is you can and should use these objects as business objects who get populated from your data layer.

In EF4.1 code first, What is the difference between configuring entities using annotations, configuration files, or in the OnModelCreating?

Currently I am using separate configuration files and calling them like:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ProductConfiguration());
base.OnModelCreating(modelBuilder);
}
It seems like most of the examples online are really basic so they define their models, the DbContext and the model configurations all in a single class. Are there and performance issues or other compelling reasons to use one over the other?
I don't know what you mean exactly with "configuration files" but there are indeed basically three options to define a model:
Conventions: When you create your model classes you name your properties in a way that EF can detect primary keys, foreign keys, relationships and so on automatically. If you do this consequently you neither need any data annotations nor to overwrite OnModelCreating.
Data annotations: Useful when you cannot or don't want to follow the convention rules. An example might be that you have an existing database whose column names doesn't match the standard naming rules of EF, for instance if you have a key column with a name which EF wouldn't recognize as a key:
public class User
{
[Key]
public int User_Code { get; set; }
}
Fluent API in OnModelCreating: For advanced mapping scenarios which you can't define with data annotations. Examples are here.
For the performance I believe it doesn't matter what you use. The approach is a question of taste and of the complexity of your model. EF creates an internal model represention only once during the lifetime of an application instance. (You can see this by setting a breakpoint in OnModelCreating: You'll reach this breakpoint only once, no matter how often you create a new DbContext.)
No, it's only a matter of readability. As your model grows you will probably get a very large configuration in OnModelCreating. As to make this more readable you break it up into separate configurations.

Categories

Resources