I am having problems with my model and trying to delete records. I have reduced it to try and show the issue I am having
I have a entity called CollectedBags with an Id and name.
I then have a entity called BankingRun which contains a list of CollectedBags
public virtual List<CollectedBags> Bags { get; set; }
This model automatically adds a relationship between the two, and in the database it adds a column to collectedbags to reference the BankingRun.
The problem occurs when I want to delete the BankingRun without affecting the CollectedBags table at all. A CollectedBags record doesn't always belong to a BankingRun.
Anything I try to delete a record results in a conflict between the two tables obviously, but my lack of knowledge with entity framework is leaving me stuck without writing some SQL to physically remove the Banking Run id in CollectedBags
public class CollectedBags
{
public long CollectedBagsId { get; set; }
public string Name { get; set; }
}
public class BankingRun
{
public long BankingRunId { get; set; }
public DateTime DateTimeVisited { get; set; }
public virtual List<CollectedBags> Bags { get; set; }
}
I am then trying to delete a BankingRun after its been created with multiple CollectedBags
With Fluent API use this code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(a => a.UserDetail)
.WithOptionalDependent()
.WillCascadeOnDelete(false);
}
This is just an illustration, but the important thing here is the .WillCascadeOnDelete(false);
It will prevent that when you delete one entity all others related get deleted as well, basically.
Related
I've been task with creating a web app using code-first, I've added a first one-to-one relationship which works as I expected, I then added a second one-to-one relationship but can't get it to work.
Here are my models (most properties removed for brevity)...
FollowUpAssessment.cs
public class FollowUpAssessment
{
[Key]
public int FollowUpAssessmentId { get; set; }
public virtual FullBloodCount FullBloodCount { get; set; }
public virtual AdverseEvent AdverseEvent { get; set; }
}
FullBloodCount.cs
public class FullBloodCount
{
[Key]
[ForeignKey("FollowUpAssessment")]
public int FollowUpAssessmentId { get; set; }
public virtual FollowUpAssessment FollowUpAssessment { get; set; }
}
AdverseEvent.cs
public class AdverseEvent
{
[Key]
[ForeignKey("FollowUpAssessment")]
public int FollowUpAssessmentId { get; set; }
public virtual FollowUpAssessment FollowUpAssessment { get; set; }
}
Context
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<FollowUpAssessment>()
.HasOptional(s => s.FullBloodCount)
.WithRequired(s => s.FollowUpAssessment);
modelBuilder.Entity<FollowUpAssessment>()
.HasOptional(s => s.AdverseEvent)
.WithRequired(s => s.FollowUpAssessment);
}
When adding a follow up the full blood count data is added to its table correctly with the foreign key, but try as I may I cannot seem to make the adverse event data populate the table.
I've spent most of today looking at this, and have search the internet and only seem able to find .NET Core solutions for this. I'm having to work with .NET 4.7.1, MVC 5 and EF 6.2.0 to complete this task. I would be grateful for any suggestions to make it work as needed thanks!
I've resolved this issue, it wasn't related to the fluent API code that I was working on at the time - it was related to a DateTime problem.
I have searched for empty migrations, and those solutions (ie. clean solution/build) have not worked for me. I am wondering if there might be something more going on.
I have an entity...
public class Term
{
public int Id { get; set; }
public string Title { get; set; }
public virtual Organization Organization { get; set; }
}
When this entity is built in the database, it creates a column "Organization_Id". The Organization entity is straight forward. Nothing weird. Per usual when running add-migration, it also creates a foreign key constraint and index on the "Organization_Id" pointing towards the Organization entity.
We are dropping the link to the Organization entity. So, naturally we delete the virtual property and then run add-migration. I expect to see the dropping of the "Organization_Id" column, but it is an empty migration class.
public partial class termremoveorg : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
After trying a hundred different things, I am at a loss. I even manually added dropping the column by manually setting up...
public partial class termremoveorg : DbMigration
{
public override void Up()
{
DropIndex("dbo.Terms", new[] { "Organization_Id" });
DropForeignKey("dbo.Terms", "Organization_Id", "dbo.Organizations");
DropColumn("dbo.Terms", "Organization_Id");
}
public override void Down()
{
AddColumn("dbo.Terms", "Organization_Id", c => c.Int());
AddForeignKey("dbo.Terms", "Organization_Id", "dbo.Organizations", "Id");
CreateIndex("dbo.Terms", "Organization_Id");
}
}
But when I run the update-database, the database table successfully has the "Organization_Id" column deleted. But I get the following error when the Seed() method gets to the point in the code where a few Term objects are inserted into the database (they do not reference any link to Organization records)...
System.Data.SqlClient.SqlException: Invalid column name 'Organization_Id'.
Thanks for any insight or help.
Update #1
Per requested, here is the Organization class.
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Term> Terms { get; set; }
}
Looking closer at this code I realized there was a property pointing back to Terms that was not removed before running the add-migration.
Trying it out, I removed this property as well and the add-migration ran as expected and successfully.
Due to 1 : M relationship with the Organization : Terms where you have to remove both end points.In other words you have to remove public virtual Organization Organization { get; set; } on the Term and public virtual ICollection<Term> Terms { get; set; } on the Organization.Then you will not have any issue with the Migration script.
I have two classes. The class BC_Instance can have many BC_InstanceSession and a BC_InstanceSession is dependent on a BC_Instance and should be deleted when it's relative BC_Instance is deleted.
//Instance
public class BC_Instance
{
public int ID { get; set; }
//sessions
public ICollection<BC_InstanceSession> sessions { get; set; }
}
//Instance session
public class BC_InstanceSession
{
[Key]
public int ID { get; set; }
[ForeignKey("Instance")]
public int InstanceID { get; set; }
public virtual BC_Instance Instance { get; set; }
}
I have detected a few problems with this configuration. First Sessions are not deleted when it's Instance is deleted. Is it possible to specify that a session cannot exist without an instance or I need to manually delete them?
Second there seems to be a problem in the mapping to the database. A Session has two foreign keys on the Database InstanceID and BC_Instance_ID as show in the image below:
Finally Lazy loading does not work. Explicit loading is needed to access the Sessions for an instance (code below)
BC_Instance instance = db.BiocloudInstances.Include(i => i.sessions).Where(i => i.ID == id).First();
For the first question you can use a CascadeOnDelete, something like:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BC_Instance>()
.HasMany(i => i.sessions)
.WithRequired(s => s.Instance)
.WillCascadeOnDelete(true);
}
For the second one, if you look on the EntityFramework documentation they specify that a 1-n relationship is used without the ForeignKey adnotation. So, because you declare your relationship virtual, EF will add 2 keys. To fix this, remove the ForeignKey adnotation and the public int InstanceID { get; set; } row. (More on their page here)
Third, as i specified in the comment,
the lazy loading is not working because you didn't specify virtual to your ICollection. Like: public virtual ICollection<BC_InstanceSession> sessions { get; set; }
I can't get EF 6 Code First to map the following two relationships the way I want them.
There are two entities: Template and TemplateVersion.
Every TemplateVersion has exactly one ParentTemplate.
A Template has a collection of TemplateVersions.
This was the first, simple, 1:many relationship, with navigation properties on both sides.
Now for the second:
From all TemplateVersions associated to a Template, only one (e.g. the "newest") is the CurrentTemplateVersion for that Template.
So: Template has a navigation property CurrentVersion, and an associated property CurrentVersionId.
There is no corresponding navigation property on the TemplateVersion side.
So, I would say, this second Template : TemplateVersion relation is 0..1 : 1.
Here are the models:
public class Template
{
public int Id { get; set; }
[...]
public virtual int CurrentVersionId { get; set; }
public virtual TemplateVersion CurrentVersion { get; set; }
public virtual ICollection<TemplateVersion> Versions { get; set; }
}
public class TemplateVersion
{
public int Id { get; set; }
[...]
public virtual int ParentTemplateId { get; set; }
public virtual Template ParentTemplate { get; set; }
}
I like to keep my model classes free from DB specifics, so I defined the relationships in the context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Template>()
.HasMany(template => template.Versions)
.WithRequired(version => version.ParentTemplate)
;
modelBuilder.Entity<Template>()
.HasRequired(template => template.CurrentVersion)
.WithOptional()
;
}
The problem is, the 2nd relation doesn't work as expected.
Using EF Power Tools plugin, I reverse-engineer the model diagram. Here's what I get:
1st relation, 1:many (ok)
2nd relation, 0..1:1
Notice that CurrentVersionId property is not part of the relation, and Template.Id is !
The generated DB tables mirror exactly this: CurrentVersionId is not part of any foreign key, and Id on the Template table incorrectly is defined as a foreign key to Id on the TemplateVersion table.
What am I missing ?
PS. Even if I remove the 1st relationship completely, the 2nd one is the same.
In a one-to-one relationship, EF requires the PK of the dependent end also has to be the FK of the relationship:
public class Foo
{
public int Id{get;set;}
//...
}
public class Boo
{
[Key,ForeignKey("Foo")]
public int FooId{get;set;}
public virtual Foo Foo{get;set;}
//...
}
If you need that TemplateVersion has its own Id, then, to resolve your issue you could configure that relationship this way:
modelBuilder.Entity<Template>()
.HasRequired(template => template.CurrentVersion)
.WithMany().HasForeignKey(t=>t.CurrentVersionId);
This seems like the most common relationship but for some reason I cannot get code-first EF working. When I run the code below I get the following error:
*{"Introducing FOREIGN KEY constraint 'Recording_RecordingLocation' on table 'Recordings' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.\r\nCould not create constraint. See previous errors."}*
I have researched SO and other places but have not been able to figure this out. I must be having a minor stroke so if this is duplicative I apologize. I don't think it is because all other reference questions I found were for many-to-many relationships... many-to-one.
My scenario is quite simple...
I have an entity (Recording) that has two required properties RecordingLocation and EditingLocation which are both of the same type WorkLocation. Each Recording has exactly one RecordingLocation and one EditingLocation (not many-to-many). I also have the requisite navigation properties.
Each WorkLocation is stand-alone and is not intrinsically linked to the Recording -- it's just a physical place where some work on that Recording took place. So when I delete a recording I do not want to delete the associated WorkLocations.
public class Recording
{
[Key]
public virtual int Id { get; set; }
//... other properties not shown here
public virtual int RecordingLocationId { get; set; }
public virtual WorkLocation RecordingLocation { get; set; }
public virtual int EditingLocationId { get; set; }
public virtual WorkLocation EditingLocation { get; set; }
{
public class WorkLocation
{
[Key]
public virtual int Id { get; set; }
public virtual WorkLocationType Type { get; set; }
public virtual string Description { get; set; }
public virtual LogicalStatus Status { get; set; }
}
// I'll use this on the front-end to filter a selection list
// but don't necessarily assume a Work Location is bound to only items of this type
public enum WorkLocationType
{
RecordingLocation,
EditingLocation,
MasteringLocation
}
What am I missing to get this working?
Your navigation properties RecordingLocation and EditingLocation are required because the corresponding foreign key properties are not nullable. By convention EF assumes that cascading delete is active for a required one-to-many relationship which causes a problem if you have more than one such relationship refering to the same table - hence the exception.
You must disable cascading delete (also your business logic seems to require it) which is only possible in Fluent API:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Recording>()
.HasRequired(r => r.RecordingLocation)
.WithMany()
.HasForeignKey(f => f.RecordingLocationId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Recording>()
.HasRequired(r => r.EditingLocation)
.WithMany()
.HasForeignKey(f => f.EditingLocationId)
.WillCascadeOnDelete(false);
}
}