EF Core: Change naming strategy of FK Shadow Properties? - entity-framework-core

In EF Core, when defining Relationships, one can either provide the necessary FK properties explicitly or not:
Explicit FK property:
public class Person
{
public Guid Id { get; set; }
public ICollection<ParentIdentity> Identities { get; set; }
...
}
public class PersonIdentity
{
public Guid Id { get; set; }
public PersonFK { get; set; } //Explicit Data storage FK field in System Logic Entity :-(
...
}
The relationship would be defined in Fluent API as follows:
model.HasMany(x => Identities) // Person can have multiple identities
.WithOne() // Identity does not need a Nav property back up to Person
.WithForeignKey(x => x.PersonFK) // Hardcoded the FK.
The upside is its eminently clarity of how it's hooked up.
The downside is the blurring of domains between system logic and storage -- in that the system entity now has Data storage specific attributes (PersonFK) that have nothing to do with system logic that developers should be concentrating on.
Shadow properties
The alternative is to let EF sort it out, using shadow properties, by not define an FK Property on the Entity:
public class Person
{
public Guid Id { get; set; }
public ICollection<ParentIdentity> Identities { get; set; }
...
}
public class PersonIdentity
{
public Guid Id { get; set; }
...
}
And define the relationship as follows:
model.HasMany(x => Identities) // Person can have multiple identities
.WithOne() // Identity does not need a Nav property back up to Person
//.WithForeignKey(x => x.PersonFK) // Don't provide an FK property
;
EF will step up and add a property to the db table named to the following convention:
<principal primary key property name>Id
//ie, will be created as `PersonId`
But let's say I want to change it to:
<principal primary key property name>FK
//ie, will be created as `PersonFK`
Question
How?
Foraging so far
In case it helps, I'm looking in the following direction:
I can see a SqlServerConventionSetBuilder that inherits from RelationalConventionSetBuilder that inherits from ProviderConventionSetBuilder.
ProviderConventionSetBuilder in turn calls
ForeignKeyIndexConvention
ForeignKeyPropertyDiscoveryConvention
ForeignKeyAttributeConvention
found some sparse documentation at https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.conventions.foreignkeyindexconvention?view=efcore-6.0
but not enough there to know where to look really.
Can someone point me in the right direction as to:
what convention to replace
how to replace it easily?
Thank you!

Related

Mark property as foreign key

I am using Microsoft.AspNetCore.Identity to manage users in my WebApp. I derived an ApplicationUser as follows:
public class ApplicationUser : IdentityUser
{
public string TgUserName { get; set; }
}
Now I have another entity let's call it Player which is managed by an ApplicationUser.
public string Id { get; set; }
public string ApplicationUserId { get; set; }
I purposely do not want to create a property of type ApplicationUser and have to mess around with include statements when retrieving objects from the database. I do want to have a property that holds the primary key which I then use to retrieve those objects on demand.
How can I tell EF Core that ApplicationUserId is a foreign key to ApplicationUser.Id.
You can do that by using the fluent API. First you need to define the relationship using the appropriate Has / With call overloads, and then use the HasForeignKey to map the property as FK.
In this particular case (many-to-one, no navigation properties on both ends, explicit FK property) it should be something like this:
modelBuilder.Entity<Player>()
.HasOne<ApplicationUser>() // no reference navigation property
.WithMany() // no collection navigation property
.HasForeignKey(e => e.ApplicationUserId) // FK property
.IsRequired(); // remove this if the relationship is optional
For more info, see Relationships - Manual configuration, Foreign key, Required and optional relationships sections of the official EF Core documentation.

EF Code first and optional:optional relationship

According to msdn article, the following should create an optional:optional relationship, but instead it creates optional:many relationship. Is the article wrong?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Optional_1>()
.HasKey(o1 => o1.id1);
modelBuilder.Entity<Optional_2>()
.HasKey(o2 => o2.id2);
modelBuilder.Entity<Optional_1>()
.HasOptional(o1 => o1.Dependent)
.WithOptionalPrincipal(o2 => o2.Principal);
}
public class Optional_1
{
public int id1 { get; set; }
public Optional_2 Dependent { get; set; }
}
public class Optional_2
{
public int id2 { get; set; }
public Optional_1 Principal { get; set; }
}
thank you
The table might look like one to many, but Entity Framework will enforce it as optional:optional because of the navigation properties. Since the navigation property is only a single object and not a collection, there is no way to add multiple.
If you look at the generated tables, it creates a nullable foreign key to your principal table (Optional_1). This allows you to create an Optional_2 that is not associated with an Optional_1.
If you were to insert multiple rows into Optional_2 that have the same foreign key to Optional_1 outside of EF, there wouldn't be anything preventing it from going through. If you were to try and load these entities you would get an error. You can't add a unique index to the column because it needs to allow NULL since it is optional.

In Entity Framework, is it possible to auto-map a column to an entity property? ID => [tablename]ID

I want to use .Id in my entity classes for the unique id, but our dba wants [tablename]Id in the database tables. Is there a way that Entity Framework can make this mapping automatically without having to create a new map file for every entity?
As long as I understand you correctly, you have something like:
public class Foo
{
public Int32 ID { get; set; }
// ...
}
public class Bar
{
public Int32 ID { get; set; }
// ...
}
And, without too much effort (or creating multiple entityTypeConfiguration<T> models) you'd like something along the lines of the following outcome:
Current Mapping Desired Mapping
[Foo] [Foo]
ID FooID
... ...
[Bar] [Bar]
ID BarID
... ...
For this, a few methods exist (and depend on which version of EF you're using). With that said, some approachable tactics:
ColumnAttribute
You can visit each entity model and decorate the ID property with the ColumnAttribute. This tells EF that, despite what we named the column, we want something else to be the name within the database. e.g.
public class Foo
{
[Column("FooID")]
public Int32 ID { get; set; }
// ...
}
public class Foo
{
[Column("BarID")]
public Int32 ID { get; set; }
// ...
}
The only problem here is that you're now going to every model and adding the attribute.
OnModelCreating & Fluent Mapping
Another method is to do the mapping but keep it all in one place. The OnModelCreating event is great for this kind of thing.
public class MyDbContext : DbContext
{
public Dbset<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
protected override void OnmodelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>()
.Property(x => x.ID).HasColumnName("FooID");
modelBuilder.Entity<Bar>()
.Property(x => x.ID).HasColumnName("BarID");
}
}
Again, the problem here is that you're creating a configuration for each entity.
Custom Conventions
As of EF6, you can use Custom Conventions which make things easier (Including developing your own convention that would make ID=TableNameID). Unfortunately I don't have the time to write an example, but the docs are pretty enlightening.
According to MSDN , both way should work.
Primary key detection is case insensitive. Recognized naming patterns
are, in order of precedence: 'Id' [type name]Id

An alternative way of implemening navigation properties in Entity Framework

The official approach to defining navigation properties for complex entities is:
public class SuperEntity
{
public int Id { get; set; }
//Other properties
}
public class LowerEntity
{
public int Id { get; set; }
public int SuperEntityId { get; set; }
public virtual SuperEntity SuperEntity { get; set; }
//Other properties
}
The main thing here is that a class that references (allows navigation to linked super entity) has both public SuperEntity SuperEntity { get; set; } property, as well as it's Id in public int SuperEntityId { get; set; }.
I have gone a few days into my entities design ommiting the public int SuperEntityId { get; set; } property in the "lower entities". So I am navigating only by virtual SuperEntity property. And everything works fine! But I had people on SO telling me that it creates an excessive tables in the DB. I've checked, and that is not true. When I use my approach, the DB tables has the SuperEntityId column and just populates it with the referenced entity Id automatically. What's the point in this public int SuperEntityId { get; set; } field then?
Or, perhaps, what I am doing became available in a "fresh" versions of EF like 4.3?
The point of SuperEntityId is that it is sometimes easier to use a foreign key property in apps where your context isn't alive the entire time, e.g. a webapp.
In such a situation, it's a lot easier to just use a foreign key property, than to try to attach object B to object A.
As far as I know, with nav properties, EF uses an object to track the relation between 2 objects. So if you want to couple object B to object A, in a disconnected app, it's not enough to just set the property on object A, you also have to fiddle with the entry of object A in the changetracker to register the relation between B and A.
Setting a foreign key property is the equivalent of this fiddling.
When we were just beginning with EF and didn't know about all of this, every time we wanted to connect 2 objects, e.g. B to A, and B already existed in the DB, the context thought that B was a new object instead of an existing one, and duplicated the record in the DB.
It won't create excessive tables, but it will probably generate extra, or longer, queries on that database. But that depends on how you're using these entities.

Entity Framework Code First One-to-One Required-Required Relationship

When using Entity Framework Code First 4.3.1 it is possible to create relationships with a multiplicity of 1-to-1. That is, one entity on each end of the relationship.
It is possible to configure 1-to-1 relationships to be required-required or required-optional ^. However, when I switch between the two I do not see any differences in:
The database schema generated. I am targeting SQL Server 2008.
The runtime behaviour of EF.
As such, I am able to create a RequiredPrincipalAs record without a corresponding RequiredDependentAs record, despite the relationship being configured as required-required. This seems to contradict the documentation for HasRequired(...):
Configures a required relationship from this entity type. Instances of the entity type will not be able to be saved to the database unless this relationship is specified. The foreign key in the database will be non-nullable.
http://msdn.microsoft.com/en-us/library/gg671317
The required-required relationship entities:
public class RequiredPrincipalA
{
public int Id { get; set; }
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
The required-optional relationship entities:
public class RequiredPrincipalB
{
public int Id { get; set; }
public virtual OptionalDependentB DependentB { get; set; }
}
public class OptionalDependentB
{
public int Id { get; set; }
public virtual RequiredPrincipalB PrincipalB { get; set; }
}
The DbContext and model configuration:
public class AppContext : DbContext
{
public DbSet<RequiredPrincipalA> PrincipalAs { get; set; }
public DbSet<RequiredDependentA> DependentAs { get; set; }
public DbSet<RequiredPrincipalB> PrincipalBs { get; set; }
public DbSet<OptionalDependentB> DependentBs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RequiredPrincipalA>()
.HasRequired(o => o.DependentA)
.WithRequiredPrincipal(o => o.PrincipalA);
modelBuilder.Entity<RequiredPrincipalB>()
.HasOptional(o => o.DependentB)
.WithRequired(o => o.PrincipalB);
}
}
The test code:
Database.SetInitializer(new DropCreateDatabaseAlways<AppContext>());
using (var ctx = new AppContext())
{
ctx.Database.Initialize(force: false);
ctx.PrincipalAs.Add(new RequiredPrincipalA());
ctx.PrincipalBs.Add(new RequiredPrincipalB());
ctx.SaveChanges();
}
I am aware I could add a [Required] data attribute to the navigation properties of RequiredPrincipalA.DependentA and RequiredDependentA.PrincipalA. This would cause EF validation to prevent the scenario above. However, I do not want to do this because it also validates the navigation property is populated when updating an existing entity. This means the application has to pre-fetch the entity at the other end of the relationship for every update.
Why do I not see any difference in the behaviour of EF just when changing a relationship between required-required and required-optional?
^ Note that optional-optional is also supported but this doesn't form part of my question. There are obvious differences in the generated database schema and runtime behaviour when an optional-optional relationship is configured.
I don't know why required-required is allowed for this case but it cannot exist in the database because relation is build on primary keys. Required-required means that A cannot be inserted if related B doesn't exist and B cannot be inserted if related A doesn't exist => neither A or B can be inserted.
Database relation has always principal and dependent entity - principal can always exist without dependent.
Real required-required in EF can be achieved only when both A and B are mapped to the same table (table splitting) because in such case they are both inserted with single insert command.
Not really an answer but I have more to say than will fit in comments. But you know, I write 900 page books...it's just how I roll. :)
Oddly I would expect the fluent configuration to behave the same way as the data annotation and am confused that it's not doing it. (I've pinged Rowan Miller with a link to this thread to get his feedback.) And the behavior I mean is: validating the constraint during SaveChanges.
On the database side, I'm with Ladislav.In the model, EF defines the 1:1 using the keys of the related entities. But in the database, you can't have FKs in both tables, so only the dependent table in the database will require that constraint that it's PK maps to an existing PK in the principal table.
And finally, I understand your reason for not wanting EF to enforce the relationship if you aren't going to always deal with teh full graph. I think 1:1 relationships are the most confusing of the EF relationship mappings and I always find myself having to go back for reminders of the rules and how things should work.
Old question. But since EF6 is still used and even available for .Net standard and this issue can be a real nuisance, I think it's worth mentioning something I couldn't find in other answers.
It is true that both HasRequired - WithRequiredPrincipal and HasOptional - WithRequired produce the same database schema and the same runtime behavior. That is, with both mappings it's possible to save a principal without a dependent entity and to remove the dependent later. So much for HasRequired.
But there is a way to make EF validate the required relationship when creating the entities, which is by simply adding a [Required] attribute:
public class RequiredPrincipalA
{
public int Id { get; set; }
[Required] // <== here
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
As said, only when creating the entities. It's still possible to set RequiredPrincipalA.RequiredDependentA = null and save it successfully. But I think that, fortunately, the likelihood of that happening in code is far lower than forgetting to set the required dependent.