The problem is i cannot perform cascade deletion using only EF codefirst conventions. They, in particular, say: "If a foreign key on the dependent entity is not nullable, then Code First sets cascade delete on the relationship"
I have parent and child entities:
[Table("AssociationPages")]
public class AssociationPage
{
[Column("AssociationPageID"), Required, Key]
public int Id { get; set; }
[Required, ForeignKey("AssociationSetting")]
public int AssociationId { get; set; }
public virtual AssociationSetting AssociationSetting { get; set; }
}
[Table("AssociationSetting")]
public class AssociationSetting
{
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AssociationId { get; set; }
public virtual ICollection<AssociationPage> Pages { get; set; }
}
My AssociationPages table in MS SQL Server looks like:
CREATE TABLE [dbo].[AssociationPages](
[AssociationPageID] [int] IDENTITY(1,1) NOT NULL,
[AssociationId] [int] NOT NULL,
...
)
and a FK (but it shouldnt matter as EF has its own conventions):
ALTER TABLE [dbo].[AssociationPages] WITH CHECK ADD CONSTRAINT [FK_ChamberPages_Chambers] FOREIGN KEY([AssociationId])
REFERENCES [dbo].[AssociationSetting] ([AssociationId])
GO
So i have non-nullable FK everywhere but once i try to delete parent AssociationSetting row then getting the "The DELETE statement conflicted with the REFERENCE constraint FK_ChamberPages_Chambers. The conflict occurred in database ..., table dbo.AssociationPages, column AssociationId message". I know i can set constraints inside database or with EF fluent API but why this is not working?
Thanks for your ideas!
update
WillCascadeOnDelete() doesnt work as well :(
It may be that Code First is not setting up the cascade since you are not following the naming conventions.
Try this:
public class AssociationPage
{
public int Id { get; set; }
public int AssociationSettingId { get; set; }
public virtual AssociationSetting AssociationSetting { get; set; }
}
public class AssociationSetting
{
public int Id { get; set; }
public virtual ICollection<AssociationPage> Pages { get; set; }
}
Okay, in investigation purposes i created a simpliest parent-child tables, put two rows - one per table, created FK relationship as "No Action" on cascade delete and wrote EF Code First entities.
Then I set up FK relationship variuos ways - via column attributes, Fluent API, explicitly specifying WillDeleteOnCascade() method or alltogether but had no luck trying to delete parent row. The only way I achieved this when retrieved both parent and child records prior to removing. At this point SQL profiler shown that rows being deleted one by one both for parent and children tables.
Summarizing the above I suppose the cascading in EF Code First means the setting constraints on the database being created by EF. I might be missing something thu.
Related
I am facing an issue in using SQLite-Net Extensions to save data in local DB in scenario where the foreign key is referencing the same entity (self-join).
Example – Employee and Manager. Every employee has a manager and a manager is also an employee. I am facing issues in saving data in such cases. It will be really helpful if you can provide some insights. Does this extension support this kind of relationship?
Yes, relationships between objects of the same class are supported, but the foreign keys and inverse properties must be explicitly specified in the relationship property attribute because the discovery system will get it wrong as there are be two relationships with the same type.
This example is extracted from the project readme:
public class TwitterUser {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "LeaderId", "Followers",
CascadeOperations = CascadeOperation.CascadeRead)]
public List<TwitterUser> FollowingUsers { get; set; }
// ReadOnly is required because we're not specifying the followers manually, but want to obtain them from database
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "FollowerId", "FollowingUsers",
CascadeOperations = CascadeOperation.CascadeRead, ReadOnly = true)]
public List<TwitterUser> Followers { get; set; }
}
// Intermediate class, not used directly anywhere in the code, only in ManyToMany attributes and table creation
public class FollowerLeaderRelationshipTable {
public int LeaderId { get; set; }
public int FollowerId { get; set; }
}
As you can see here we have a many-to-many between Twitter users. In your case it will be a one-to-many, so you won't need the intermediate table and you'll need the foreign key (ManagerId for example) in your Person class.
I have two classes: Customer and Association.
A customer can have an association with many customers. Each association is of a defined type (Family, Friend, etc) i.e Customer A is a friend of Customer B. Customer A is related to Customer C. The type of association is defined by an enum AssociationType.
In order to create this in EF i've defined the following classes
public class Customer
{
public string FirstName {get; set;}
public string LastName {get; set;}
public virtual ICollection<Association> Associations { get; set; }
}
public class Association
{
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public int AssociatedCustomerId { get; set; }
public virtual Customer AssociatedCustomer { get; set; }
public AssociationType AssociationType { get; set; }
}
I've removed the Data Annotations as I was unable to get this to compile. I get the error:
"Model compatibility cannot be checked because the database does not
contain model metadata".
Does anyone have any ideas?
It happens sometimes when an error occurs during database creation. The database schema is created then - except the __MigrationHistory table. When you run your application again EF wants to check against the __MigrationHistory table if the schema is still up-to-date with the model and if that table doesn't exist it throws the exception you are having.
To fix the problem either delete the database manually or set the initializer to DropCreateDatabaseAlways<MyContext> (with Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>()) - only once. After the DB is created set it back to your original initializer.
BTW: For your model you will have to specify explicitly that Customer.Associations is related to Association.Customer, either with data annotations...
[InverseProperty("Customer")]
public virtual ICollection<Association> Associations { get; set; }
...or with Fluent API:
modelBuilder.Entity<Customer>()
.HasMany(c => c.Associations)
.WithRequired(a => a.Customer)
.HasForeignKey(a => a.CustomerId);
Thank you Slauma,
your answer got us going in the right direction.
We added the following configuration to the Association configuration:
HasRequired(x => x.AssociatedCustomer).WithMany().WillCascadeOnDelete(false);
I miss a table in my data model
I have an existing DB, and tried:
EF Power Tools like THIS description
tables:
User (Id, ...)
Project (ID, Name,..)
Timestamp (ID,Start, End, UserID, ProjectID..)
But I have no Table "Poject_Favorit" (UserId, ProjectID)
So I write the Table myself:
public class Project_Favorite
{
[Key]public Guid GuidUser { get; set; }
public Guid GuidProject { get; set; }
}
and I added:
public DbSet<Project_Favorite> Project_Favorite { get; set; }
to my DbContext
EF searched for "dbo.Project_Favorite" but it should search for "Timeworx.Project_Favorite"
so I added:
modelBuilder.Configurations.Add(new Project_FavoriteMap());
to my DbContext
and created the file "Project_FavoriteMap.cs:
class Project_FavoriteMap : EntityTypeConfiguration<Project_Favorite>
{
public Project_FavoriteMap() {
//Primary Key
this.HasKey(t => t.GuidProject);
this.HasKey(t => t.GuidUser);
this.ToTable("Project_Favorite", "Timeworx");
}
}
Now there is an error. It says "...Project_Favorite already defined..."
It looks like the Poject_Favorit table is a many-to-many join between User and Project. In that case you don't need a corresponding entity class. Your User class will probably have an ICollection<Project> and your Project class will probably have an ICollection<User>. That represents a many-to-many hierarchy. A project can favorited by many users and a user can favorite many projects. Hence the two collections.
I regularly have the following structure:
MyClass
public virtual ICollection<Version> Versions { get; set; }
public virtual Version CurrentVersion { get; set; }
That is, there is a list of stuff, and some class both points to that list, and one specific item in that list - either the current version of many versions, the next upcoming event in a list of events, etc.
In my schema what I'd like to end up with is a Foreign Key pointing from Version to MyClass - that much works out properly. But then I'd like a Foreign Key pointing from MyClass to Version representing the CurrentVersion property, with no Foreign Key pointing back - I don't want the extra storage or bother of telling a Version what MyClass it's the CurrentVersion for, if any. Put another way, I'd like this second relationship to be one-way from MyClass to Version, even though it's one-to-one.
What EF Code First gives me instead is the normal one-to-many on the first property, with the FK from Version to MyClass, but then a full one-to-one relationship on the second property with an FK pointing in both directions - so the underlying schema for Version ends up with MyClass_Id and MyClass_Id1.
So, is there a way to get a one-way relationship in EF Code First without resorting to the Fluent API? It looked like maybe System.ComponentModel.DataAnnotations.Schema.InverseProperty had a shot at it, but it didn't seem to offer a way to say "Don't generate one."
The key is to specify the InverseProperty on the property that points back, so that EF realizes it's to the Many-to-Many, not to the One-to-One.
public class MyClass
{
public int Id { get; set; }
public Version CurrentVersion { get; set; }
public ICollection<Version> Versions { get; set; }
}
public class Version
{
public int Id { get; set; }
[InverseProperty("Versions")]
public Versioned Versioned { get; set; }
}
I'm using EF 4.3.1 Code First Migrations. I have a table like:
public class Product
{
[Key]
[Column(Order=0)]
[MaxLength(100)]
public string Store { get; set; }
[Key]
[Column(Order=1)]
[MaxLength(100)]
public string Sku { get; set; }
}
I have an existing table created with the above code. I then moved it to a single-column Primary Key:
public class Product
{
[MaxLength(100)]
public string Store { get; set; }
[Key]
[MaxLength(100)]
public string Sku { get; set; }
}
This causes EF to fail in the next automatic migration, complaining:
ALTER TABLE [Product] ALTER COLUMN [Store] nvarchar
The object 'PK_Product' is dependent on column 'Store'. ALTER
TABLE ALTER COLUMN Store failed because one or more objects access this
column.
Clearly the PK_Product needs to be dropped before attempting to fire this ALTER statement (why is it altering the column at all?), but instead the migration fails.
Am I doing something wrong or is this a bug? Workarounds?
You won't be able to do this with an automatic migration. You'll have to create a migration using Add-Migration and then change it so it only modifies the PK.
The migration can be as simple as:
public partial class TheMigration : DbMigration
{
public override void Up()
{
DropPrimaryKey("Products", new[] { "Store", "Sku" });
AddPrimaryKey("Products", "Sku");
}
public override void Down()
{
DropPrimaryKey("Products", new[] { "Sku" });
AddPrimaryKey("Products", new[] { "Store", "Sku" });
}
}
EF is altering the column because, when it's part of a Key, it's implicitly NOT NULL.
You can leave it as-is, add a [Required] attribute, or allow EF to alter the column after dropping the PK.