public class Email
{
public int Id { get; set; }
/// ...
public int? ReplyTo { get; set; }
public int? ForwardOf { get; set; }
}
I would like to configure ReplyTo and ForwardOf to be FK to Email.Id property with cascade Delete.
Tried this:
e.HasOne(nameof(Email.ReplyTo)).WithMany().OnDelete(DeleteBehavior.Cascade);
but it gives an error
The specified type 'System.Nullable`1[System.Int32]' must be a non-interface reference type to be used as an entity type.
I would prefer not to have navigation properties of type Email as they will never be used by my code.
This should allow a shadow navigation property:
.HasOne<Email>()
.WithMany()
.HasForeignKey(x => x.ReplyTo)
.OnDelete(DeleteBehaviour.Cascade);
Though I'm not sure you'd want a delete cascade on such a relationship.
Related
I have a table named Provider with three relations to another table State. Of these relations one is required and the other two are optional. See the relationship in the diagram below:
Here are the entities along with the fluent configurations for each.
Provider
public class Provider
{
public int Id { get; set; }
[Required]
public int PrimaryStateId { get; set; }
public virtual State PrimaryState { get; set; }
public int? BillingStateId { get; set; }
public virtual State BillingState { get; set; }
public int? ShippingStateId { get; set; }
public virtual State ShippingState { get; set; }
}
class ProviderConfig : IEntityTypeConfiguration<Provider>
{
public void Configure(EntityTypeBuilder<Provider> entity)
{
entity.HasOne(x => x.PrimaryState)
.WithMany(x => x.ProvidersPrimary)
.IsRequired(true)
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(x => x.BillingState)
.WithMany(x => x.ProvidersBilling)
.IsRequired(false)
.OnDelete(DeleteBehavior.SetNull);
entity.HasOne(x => x.ShippingState)
.WithMany(x => x.ProvidersShipping)
.IsRequired(false)
.OnDelete(DeleteBehavior.SetNull);
}
}
State
public class State
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Provider> ProvidersPrimary { get; set; } = new List<Provider>();
public virtual ICollection<Provider> ProvidersBilling { get; set; } = new List<Provider>();
public virtual ICollection<Provider> ProvidersShipping { get; set; } = new List<Provider>();
}
class StateConfig : IEntityTypeConfiguration<State>
{
public void Configure(EntityTypeBuilder<State> entity)
{
entity.Property(x => x.Name).IsRequired();
entity.HasIndex(x => x.Name).IsUnique();
}
}
As you can see, I want to set DeleteBehavior.Restrict for the PrimaryState, and DeleteBehavior.SetNull for the other two relations. However, this throws error on update-database with the following message:
Introducing FOREIGN KEY constraint 'FK_Provider_State_ShippingStateId' on table 'Provider' 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 or index. See previous errors.
However, if I get rid of one of the optional relations, it works fine. That is, it works when I have one required relation and one optional relation, but not when I have one required and two optional relations. Also, it works fine if I get rid of OnDelete(DeleteBehavior.SetNull) from the optional relations, but then in the database the delete rule for foreign key BillingStateId ends up being Set Null, but that for ShippingStateId is No Action. No idea why it is different for two similarly configured optional relations.
Questions:
Why does update-database fail with that error? I don't understand how cycles or multiple cascade paths may be caused.
Why getting rid of OnDelete(DeleteBehavior.SetNull) creates a foreign key with Set Null delete rule for one optional relation but No Action for the other?
What is the correct way to configure this relationship? That is, one required relation and two optional relations, and the foreign keys for the optional relations should have a delete rule of Set Null.
VS Solution link: click
I have the following scenario:
Color Class
public int ID
{
get;
set;
}
public string Name
{
get;
set;
}
public string Hex
{
get;
set;
}
Widget Class
public int ID
{
get;
set;
}
public int HeaderBackgroundColorID
{
get;
set;
}
public Color HeaderBackgroundColor
{
get;
set;
}
Using Code-First, I am trying to create a one-way relationship between Widget to Color classes with the HeaderBackgroundColor / HeaderBackgroundColorID Fields.
normally i would do this in the config class:
this.HasOptional(r => r.HeaderBackgroundColor )
.WithMany(m => m.Widgets)
.HasForeignKey(fk => fk.HeaderBackgroundColorID);
but i am not intrested in adding a Widgets collection to the Color class.
tried this:
this.HasOptional(r => r.HeaderBackgroundColor )
.WithMany()
.HasForeignKey(fk => fk.HeaderBackgroundColorID);
but that throws a validation error.
What's the way to do this right?
You are getting an error because HeaderBackgroundColorId is a non-nullable int, so it cannot be optional.
All you need to do to achieve what you're looking for is to turn the foreign key into a nullable int...
public int? HeaderBackgroundColorID { get; set; }
Because you named the foreign key to match the navigation property (HeadBackgroundColorId and HeaderBackgroundColor) which follows Code First conventions, you do not need to create any explicit mappings. Simply making the above change will make the relationship optional.
I have the following model:
public class Job
{
[Key]
public int JobID { get; set; }
public string Status { get; set; }
public DateTime JobDate { get; set; }
public string JobTitle { get; set; }
public int? Cleaner { get; set; }
public int? Client { get; set; }
public int EstTime { get; set; }
public virtual Client ClientInfo { get; set; }
public virtual Valeter ValeterInfo { get; set; }
}
This in OnModelCreating:
// Relationship Job -> Valeter
modelBuilder.Entity<Job>()
.HasOptional<Valeter>(u => u.ValeterInfo)
.WithMany()
.HasForeignKey(e => e.Cleaner);
(NOTE: it is using an existing database). When I try to perform the following:
if (ModelState.IsValid)
{
db.Entry(job).State = EntityState.Modified;
db.SaveChanges();
}
It generally works fine UNLESS I change the Cleaner value to something else and then I get the error:
A referential integrity constraint violation occurred: The property
values that define the referential constraints are not consistent
between principal and dependent objects in the relationship.
This exception usually occurs if job.ValeterInfo != null and job.ValeterInfo.ValeterId != job.Cleaner. So, the simplest solution is to set the navigation property to null before you attach the job to the context:
if (ModelState.IsValid)
{
job.ValeterInfo = null;
db.Entry(job).State = EntityState.Modified;
db.SaveChanges();
}
This looks a bit strange and like a hack. But the question is why job.ValeterInfo is NOT null when you post the data to the controller action. When you set the state of the job to Modified you are only updating the job's scalar properties (including Cleaner) but not any properties of job.ValeterInfo or any relationships. So, you don't need to send job.ValeterInfo properties to the server in the first place.
Anyway, you have an inconsistency: The FK job.Cleaner is changed but the related entity job.ValeterInfo (especially its primary key property ValeterId) is not. EF doesn't know which represents the correct relationship: The foreign key property value or the navigation property value? This ambiguity causes the exception.
Very simply I am using Entity Framework 4.1 code first and I would like to replace my [ForeignKey(..)] attributes with fluent calls on modelBuilder instead. Something similar to WithRequired(..) and HasForeignKey(..) below which tie an explicit foreign key property (CreatedBySessionId) together with the associated navigation property (CreatedBySession). But I would like to do this for a one to one relationsip instead of a one to many:
modelBuilder.Entity<..>().HasMany(..).WithRequired(x => x.CreatedBySession).HasForeignKey(x => x.CreatedBySessionId)
A more concrete example is below. This works quite happily with the [ForeignKey(..)] attribute but I'd like to do away with it and configure it purely on modelbuilder.
public class VendorApplication
{
public int VendorApplicationId { get; set; }
public int CreatedBySessionId { get; set; }
public virtual Session CreatedBySession { get; set; }
}
public class Session
{
public int SessionId { get; set; }
[ForeignKey("CurrentApplication")]
public int? CurrentApplicationId { get; set; }
public virtual VendorApplication CurrentApplication { get; set; }
public virtual ICollection<VendorApplication> Applications { get; set; }
}
public class MyDataContext: DbContext
{
public IDbSet<VendorApplication> Applications { get; set; }
public IDbSet<Session> Sessions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Session>().HasMany(x => x.Applications).WithRequired(x => x.CreatedBySession).HasForeignKey(x => x.CreatedBySessionId).WillCascadeOnDelete(false);
// Note: We have to turn off Cascade delete on Session <-> VendorApplication relationship so that SQL doesn't complain about cyclic cascading deletes
}
}
Here a Session can be responsible for creating many VendorApplications (Session.Applications), but a Session is working on at most one VendorApplication at a time (Session.CurrentApplication). I would like to tie the CurrentApplicationId property with the CurrentApplication navigation property in modelBuilder instead of via the [ForeignKey(..)] attribute.
Things I've Tried
When you remove the [ForeignKey(..)] attribute the CurrentApplication property generates a CurrentApplication_VendorApplicationId column in the database which is not tied to the CurrentApplicationId column.
I've tried explicitly mapping the relationship using the CurrentApplicationId column name as below, but obviously this generates an error because the database column name "CurrentApplicationId" is already being used by the property Session.CurrentApplicationId:
modelBuilder.Entity<Session>().HasOptional(x => x.CurrentApplication).WithOptionalDependent().Map(config => config.MapKey("CurrentApplicationId"));
It feels like I'm missing something very obvious here since all I want to do is perform the same operation that [ForeignKey(..)] does but within the model builder. Or is it a case that this is bad practise and was explicitly left out?
You need to map the relationship as one-to-many and omit the collection property in the relationship.
modelBuilder.Entity<Session>()
.HasOptional(x => x.CurrentApplication)
.WithMany()
.HasForeignKey(x => x.CurrentApplicationId)
I read quite a number of posts of programmers that run into the Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values -exception when using a self-referencing relationship in Entity Framework.
I am trying to get a parent-child relationship to work:
public class Category {
public int CategoryId { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
public Category Parent { get; set; }
public List<Category> Children { get; set; }
}
This is the configuration I use (Fluent API):
Property(c => c.ParentId).IsOptional();
HasMany(c => c.Children).WithOptional(c => c.Parent).HasForeignKey(c => c.ParentId);
//HasOptional(c => c.Parent).WithMany(c => c.Children).HasForeignKey(c => c.ParentId);
Both the HasMany() and HasOptional() configurations result in a "Unable to determine a valid ordering for dependent operations..." exception when I try to save a new category like this:
context.Categories.Add(new Category { Name = "test" });
I don't understand why EF doesn't insert the Category with a null parentId. The database allows the ParentId foreign key to be null.
Would you be able to tell me how to do this?
You must define the ParentId in the category class as nullable to use it as the foreign key property for an optional relationship:
public int? ParentId { get; set; }
An int property cannot take the value null and therefore cannot represent a NULL as value in a database column.
Since someone asked in a comment about doing this with attributes. You can also utilize data annotations to set this up. Using the same example as above:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Category {
// You can also add [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
// as an attribute, if this field is to be generated by the database
[Key] // Define this as the primary key for the table
public int CategoryId { get; set; }
public string Name { get; set; }
[ForeignKey(nameof(Parent))] // Link the Parent object to the ParentId Foreign Key
public int? ParentId { get; set; }
public Category Parent { get; set; }
public List<Category> Children { get; set; }
}
This is tested and works in EF 6.