Convert WithOptional to Entity Framework Core(7) equivalent - c#

Im migrating a project from .Net 4.X to .Net 6 and EF 6 to the latest version (version 7 i believe) using Visual Studio 2022.
I've migrated a bunch of configurations but the below im not sure the best way to proceed (the database already exists)
Here is EF6 code
internal class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
public CustomerConfiguration()
{
this.HasMany(e => e.CustomerDocuments)
.WithOptional(e => e.Customer)
.HasForeignKey(e => e.CustomerID);
}
}
In EF 7 i have the code as
internal class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.HasMany(e => e.CustomerDocuments)
}
}
But i cant find the equivalent for .WithOptional and https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key doesnt really show me any example of how i can configure it although .HasForeignKey seems to exist but i think once WithOptional is resolved it may give some way to convert/use HasForeignKey.
I read WithOptional with Entity Framework Core but then i get confused with if its replacement is HasOne as im already using WithOne (in another Entity configuration) to convert WithRequired (from EF 6)
Anyone know what im missing here or how to convert to the equivalent in EF 7?

In EF Core these are simply separated to WithOne (for relationship cardinality and associated reference navigation property mapping) and IsRequired (whether it required/optional).
So the general conversion of EF6 WithOptional / WithRequired after HasMany / HasOne to EF Core is like
.WithOptional(e => e.Customer)
maps to
.WithOne(e => e.Customer)
.IsRequired(false)
and
.WithRequired(e => e.Customer)
maps to
.WithOne(e => e.Customer)
.IsRequired(true) // or just .IsRequired()
The same applies if you start configuration from the "one" side, i.e. HasOptional / HasRequired become HasOne().With{One|Many}).IsRequired(false|true)

Related

Delete parent if no children in EF Core 7

Using EF Core 7 and .NET 7 (but also in previous versions), it is possible to delete all children of a one-to-many relationship in a SQL server database by configuring the delete behavior of the parent entity in the OnModelCreating-method in the class deriving from the DbContext-class, like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Department>()
.HasMany(d => d.Employees)
.WithOne(e => e.Department)
.OnDelete(DeleteBehavior.Cascade)
}
}
But what if I want to delete the parent if all child entities are deleted?
I've tried mapping a reversed delete pattern from the one above (see below), but to no success.
modelBuilder.Entity<Employee>()
.HasOne(e => e.Department)
.WithMany(d => d.Employees)
.OnDelete(DeleteBehavior.Cascade);
ORM engines are inspired from the relational database management systems. Removing a parent when last child is removed is not a standard operation on a relation change in the DB engines. So EFCore does not support it to. At the database level you can use triggers to achieve what you want.

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.

Ways to map database with Entity Framework?

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)

Method not found HasDatabaseGeneratedOption

I have read all post related to this and i've tried them all but not results.
Im using fluent api to map my models to the database. But when im query i get this Error:
Method not found:
'System.Data.Entity.ModelConfiguration.Configuration.DecimalPropertyConfiguration
System.Data.Entity.ModelConfiguration.Configuration.DecimalPropertyConfiguration.HasDatabaseGeneratedOption(System.Nullable`1<System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption>)'.
My model look like this:
ToTable("table_name");
HasKey(x => x.CounterId)//this property IS NOT NULLABLE
.Property(x => x.CounterId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("dbCounter_Id")
.HasPrecision(10, 0)
.IsRequired();
Property(x => x.HouseId)
.HasColumnName("dbHouseId");
Property(x => x.ApplicationId)
.HasColumnName("dbApplication_id");
For some reason .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) is giving me this error so when i quit it:
HasKey(x => x.CounterId)
.Property(x => x.PageId)
.HasColumnName("dbCounter_Id")
.HasPrecision(10, 0)
.IsRequired();
I get not error but as im lucky as hell, when im trying to add a record to that table i get insert identity exception. I cant neither added nor quit it the HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) method.
Important: The key CounterId is not type INTEGER, is DECIMAL(10,0) instead. Could that be the problem here? I cannot change the datatype of the column since there are a lot of app in production that will be affect in the worst way.
Hope i can get any help.
You should make it in this way
ToTable("table_name");
HasKey(x => x.CounterId); // you should split HasKey from Property
Property(x => x.CounterId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("dbCounter_Id")
.HasPrecision(10, 0)
.IsRequired();
Property(x => x.HouseId)
.HasColumnName("dbHouseId");
Property(x => x.ApplicationId)
.HasColumnName("dbApplication_id");
You should try to remove the nuget package from all projects. Then add it again.
I don't know why but it just worked for me, even if the package was the same.
One of the reasons for this error is because of different .NET versions of your projects. For e.g. if your EF DbContext class is created in a project of .NET v4.5, and if the Web Application you are using it in is of .NET v4.0, then you might get this error at run-time.
Ideally, it should error at compile time. But, some times Visual Studio compiles fine, especially for older "Website" type projects. And, when the application actually runs, that is when the error occurs.
So, to fix this, make sure that .NET framework version of your client application (that uses the EF DbContext class) is >= .NET version of project containing the DbContext.
I had a similar issue with EF6 and ASP.Net 3.1. This answer helped nudge me in the right direction, but I solved it slightly differently. All I needed to do was add the following in my model class that the table was based on:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Round { get; set; }

EF4 CTP5 - Many To One Column Renaming

How do I override the default convention for the foreign key column in EF4 to specify a different column name?
For example, I have one entity with a property called Parent that references to other one of the same type.
EF4 tries to resolve the relation by looking for the foreign key named EntityId, but in my DB schema it is Entity_Id. How do I tell EF that the FK column name is not EntityId?
I've tried the following:
modelBuilder.Entity<SomeEntity>()
.HasOptional(m => m.Parent)
.WithMany()
.IsIndependent()
.Map(m => m.MapKey(k => k.Id, "Entity_Id")));
But I get an exception saying: Sequence contains more than one matching element.
Any help on this?
Thanks!
First: Upgrade to EF 4.1 RTW. CTP 5 is outdated and contains potentially many bugs which are fixed now.
If you have done the upgrade the following should work:
modelBuilder.Entity<SomeEntity>()
.HasOptional(m => m.Parent)
.WithMany()
.Map(c => c.MapKey("Entity_Id"));

Categories

Resources