entity framework table with optional foreign keys - entity-framework

First of all, I'm new to Entity Framework and I'm trying to do a project using the Code-First model, so please forgive my ignorance on what may turn out to be a trivial problem...
I'm working on creating some POCO EF classes and I'm having difficulty figuring out how to setup some of the relationships in the DbContext derived class.
If I were to setup the tables with SQL, this is what they would look like (extraneous columns removed for clarity and brevity:
CREATE TABLE DBO.Application (
ApplicationId NUMERIC(18,0) IDENTITY(1,1) NOT NULL,
MinimumVersionId NUMERIC(18,0),
CurrentVersionId NUMERIC(18,0));
CREATE TABLE DBO.ApplicationVersion (
ApplicationVersionId NUMERIC(18,0) IDENTITY(1,1) NOT NULL,
ApplicationId NUMERIC(18,0) NOT NULL;
ALTER TABLE DBO.Application ADD
PRIMARY KEY (ApplicationId),
CONSTRAINT Application_FK1
FOREIGN KEY (MinimumVersionId)
REFERENCES DBO.ApplicationVersion (ApplicationVersionId),
CONSTRAINT Application_FK2
FOREIGN KEY (CurrentVersionId)
REFERENCES DBO.ApplicationVersion (ApplicationVersionId);
ALTER TABLE DBO.ApplicationVersion ADD
PRIMARY KEY (ApplicationVersionId),
CONSTRAINT ApplicationVersion_FK1
FOREIGN KEY (ApplicationId)
REFERENCES DBO.Application (ApplicationId);
The relevant part of the ApplicationModel POCO class is (Application DB Table shown above):
public class ApplicationModel
{
public long ApplicationId { get; set; }
public virtual ApplicationVersionModel CurrentVersion { get; set; }
public long? CurrentVersionId { get; set; }
public virtual ApplicationVersionModel MinimumVersion { get; set; }
public long? MinimumVersionId { get; set; }
public virtual IList<ApplicationVersionModel> Versions { get; set; }
}
And the ApplicationVersionM POCO class (ApplicationVersion DB Table shown above):
public class ApplicationVersionModel
{
public virtual ApplicationModel Application { get; set; }
public long ApplicationId { get; set; }
public long ApplicationVersionId { get; set; }
}
So far, in the OnModelCreating method of the class that inherits from DbContext, I have this:
modelBuilder.Entity<ApplicationModel>()
.HasMany<ApplicationVersionModel>(a => a.Versions)
.WithRequired(av => av.Application)
.HasForeignKey(a => a.ApplicationId);
This is to establish the one to many relationship between Application and ApplicationVersion.
Where I'm getting confused is how to write the entries for the CurrentVersion and MinimumVersion fields. Each of these are to hold a value that would be found in ApplicationVersion.ApplicationVersionId (the primary key). However, these fields are nullable in the database and, therefore, optional.
I'm getting lost in all the options like:
WithMany - I know this one isn't it as I'm pointing to a single record
WithOptionalDependant
WithOptionalPrincipal
WithRequired - I don't think this is it since the field is nullable
And then, I'm not exactly sure what methods would be chained after that.
Any help would be appreciated. It would also be beneficial if, in your answers, you could explain WHY I need to do it that way. Knowing why will help me (and possibly others that may read the question) understand the processes and relationships better.

Related

EF Core: Change naming strategy of FK Shadow Properties?

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!

Entity Framework duplicate composite key values

Using Entity Framework Code First, I'm seeing very strange behavior when inserting a row with a composite key. The composite key consists of a guid ID field and a guid foreign key field, creating an "identifying relationship". The strange behavior is that regardless of what I set the ID and foreign key field to, the generated SQL sets them both to the foreign key value.
My classes look like this:
public class Parent {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public Guid ParentId { get; set; }
}
In my DbContext file I have:
modelBuilder.Entity<Child>().HasKey(c => new { c.Id, c.ParentId });
Doing something like:
var parent = new Parent() { Id = Guid.NewGuid() };
var child = new Child() { Id = Guid.NewGuid(), ParentId = parent.Id };
parent.Children.Add(child);
You'd think the SQL executed would insert a new child with differing Id and ParentId values. But instead, what I'm seeing is:
// Assume parent is already in the DB, with ID of '1b1a6ecd-00ad-4265-ac0d-9a50bd30e247'
INSERT [dbo].[Child]
([Id],
[ParentId])
VALUES ('1b1a6ecd-00ad-4265-ac0d-9a50bd30e247' /* #0 */,
'1b1a6ecd-00ad-4265-ac0d-9a50bd30e247' /* #1 */)
Why is the SQL using the ParentId value for both fields? This doesn't make sense at all.
UPDATE
Unless I totally misunderstand something fundamental to EF, I think this must be a bug. I've uploaded a tiny reproducible project to http://1drv.ms/1kX2oVC
It uses EF 6.1 and .NET 4.5. I'm hoping some EF expert can chime in here and confirm this is a bug, or that I'm doing something fundamentally wrong.
With this set up Entity Framework isn't able to properly infer the associations. You have two options how to fix it:
Add modelBuilder.Entity<Parent>().HasMany(x => x.Children).WithRequired().HasForeignKey(x => x.ParentId); into your OnModelCreating.
or
Add public Parent Parent { get; set; } into your Child entity.

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.

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.

Entity Framework Code First - Defining Relationships/Keys

I am designing my database using code first and I need a little help I think.
I am getting this error:
Introducing FOREIGN KEY constraint 'SalesOrder_Invoices' on table 'Invoices' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
I am trying to have the following relationships/keys:
--> = 1 to Many Relationship
Customer --> CustomerLocation
CustomerLocation --> SalesOrder
SalesOrder --> Invoice
SalesRep --> SalesOrder
PaymentTerm --> Customer
PaymentTerm --> SalesOrder
PaymentTerm --> Invoice
I am trying to define them by the standard of:
<ClassName><PrimaryKeyID>
Example: Customer has ID property, so in CustomerLocation i define the foreign key like so:
Public Property CustomerID AS Integer
All I have to do is define the foreign key correct? Do I also have to have navigation properties for each key I define?
And, can I not have multiple foreign keys on the same primary key of an object?
Updated
So to define a relationship, do you use the ClassName.PrimaryKeyProperty? or do you use navigation properties? Or both? Confused!!
Update 2
So to make a relationship work you have to define both sides... I think.
Public Class Customer
Public Property ID AS Integer
Public Overrideable Property Locations AS ICollection(OF CustomerLocation)
End Class
Public Class CustomerLocation
Public Property ID AS Integer
Public Property CustomerID AS Integer
End Class
This is exception caused by SQL server when you have multiple paths of cascade deletes. If you delete your PaymentTerm it will trigger cascade delete on all three relations. This will blow up when creating either SalesOrder or Invoice. EF creates by default all one-to-many relations with ON DELETE CASCADE you can remap your specific relation to not use it by:
modelBuilder.Entity<...>()
.HasRequired(...)
.WithMany(...)
.HasForeignKey(...)
.WillCascadeOnDelete(false);
Or you can turn it off globaly by removing the convention:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
You can get around this error on a particular migration by editing the generated Up() method with a line something like this:
AddForeignKey("dbo.Payments", "EventID", "dbo.Events", "EventID", cascadeDelete: true)
and change that cascadeDelete: value to false on the offending relationship(s).
Read this, I am sure this will help you find the answer.
Also, according to ScottGu's blogpost, I think in general it should be that you just create the classes as follows (I didn't read it carefully enough, so you should check it out for further details):
public class Customer
{
public int CustomerID { get; set; }
public int CustomerLocationID { get; set; }
public virtual CustomerLocation Location { get; set; }
}
public class CustomerLocation
{
public int CustomerLocationID { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
}