Repository insert with navigation property throws 'FOREIGN KEY constraint failed' - entity-framework

I'm using EF Core and sqlite. Given following 1-to-1 entities:
public class Entity1:FullAuditedEntity<int>
{
public Entity2 Entity2 { get; set; }
public string Name { get; set; }
}
public class Entity2: FullAuditedEntity<int>
{
public string Name { get; set; }
[ForeignKey("Entity1Id")]
public Entity1 Entity1{ get; set; }
public int Entity1Id { get; set; }
}
and following insert logic:
entity1.Entity2 = new Entity2();
entity1= await entity1Repository.InsertAsync(entity1);//entity1Repository is of type IRepository<Entity1>
Code above throws:
Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
---- Microsoft.Data.Sqlite.SqliteException : SQLite Error 19: 'FOREIGN KEY constraint failed'.
at ...
This approach doesn't work either, throws the same:
var entity2 = new Entity2();
entity1.Entity2 = entity2;
entity2.Entity1 = entity1;
entity1= await entity1Repository.InsertAsync(entity1);//entity1Repository
What I'm doing wrong, what I'm missing? What is the correct way to create Entity1 and Entity2 with Entity2 property being set? Is it possible to create with Entity1 repo only?
Thanks in advance.
Update: Problem solved. In actual code entity2 has it's own reference on entity3, which is obvoiusly was throwing. It was the cause.

Like this?
public class Entity1:FullAuditedEntity<int>
{
public Entity2 Entity2 { get; set; } = new Entity2();
public string Name { get; set; }
}
var entity1 = new Entity1();
entity1 = await entity1Repository.InsertAsync(entity1);//entity1Repository
Or do you want to clone? Check out:
https://entityframeworkcore.com/knowledge-base/48873716/how-to-deep-clone-copy-in-ef-core

See update in my post -- there was other entity which violated fk constraints. Thanks everyone for your time.

Related

Entity Framework firebird code first generators

I am learning EF6 and I want to try it with Firebird. The problem is that my generators are not created, only the tables.
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int MajorVersion { get; set; }
public string PublicKey { get; set; }
public string privateKey { get; set; }
public virtual ICollection<ProductVersion> ProductVersions { get; set; }
}
When executing this code:
using (LicenseContext licenseContext = new LicenseContext())
{
Product p = new Product { Name = "Test" };
licenseContext.Products.Add(p);
licenseContext.SaveChanges();
}
I get the following error:
An exception occurred while initializing the database. See the InnerException for details.
The inner exception reads: An error was reported while committing a database transaction but it could not be determined whether the transaction succeeded or failed on the database server
The database still is being created, but with no generators or triggers for Products table. The PrimaryKey is set.
What can I try to resolve this?
If ProductId is a primary key you should to you product instance p.ProductId = 123;
Or create a trigger and generator
SET TERM;
CREATE OR ALTER TRIGGER "Product_BI" FOR "Product"
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
new.id = gen_id("sq_Product",1);
END
SET TERM;
COMMIT;

Entity Framework self referencing entity

I have a problem with the Entity Framework.
public class User : Receiver
{
public User()
{
if (Groups == null)
Groups = new List<Group>();
if (Buddies == null)
Buddies = new List<User>();
}
[Required]
public string PhoneNumber { get; set; }
[ForeignKey("Guid"), JsonIgnore]
public IList<User> Buddies { get; set; }
[ForeignKey("Guid"), JsonIgnore]
public IList<Group> Groups { get; set; }
}
public class Receiver
{
public Receiver()
{
Guid = Guid.NewGuid();
Created = DateTime.Now;
}
[Key]
public Guid Guid { get; set; }
[Required]
public DateTime Created { get; set; }
}
When i try to add a user...
User user = new User
{
Guid = new Guid("8cd094c9-e4df-494e-b991-5cf5cc03d6e3"),
PhoneNumber = "+4991276460"
};
cmc.Receivers.Add(user);
... it ends in follogwing error.
The object of the Type "System.Collections.Generic.List`1[Project.Models.User]" can't be converted to "Project.Models.User".
When i comment out following two lines:
[ForeignKey("Guid"), JsonIgnore]
public IList<User> Buddies { get; set; }
...the programm runs fine.
I hope someone can help me to fix this problem.
Otherwise it runs into an error at this line : cmc.Receivers.Add(user);
In your mapping...
[ForeignKey("Guid"), JsonIgnore]
public IList<User> Buddies { get; set; }
...you specify that User.Buddies is part of a one-to-many relationship and that User.Guid (=Receiver.Guid) is the foreign key in this relationship. But User.Guid is also the primary key, hence it must be unique. As a result a User cannot have a list of Buddies but only a single reference.
The mapping makes no sense but the exception is not very helpful and difficult to understand. (Somehow EF seems to recognize internally that the Buddies cannot be a list with that mapping and wants to cast the list to a single reference. It should detect in my opinion that the mapping is invalid in the first place.)
For a correct one-to-many mapping you need a foreign key that is different from the primary key. You can achieve that by either removing the [ForeignKey] annotation altogether...
[JsonIgnore]
public IList<User> Buddies { get; set; }
...in which case EF will create a default foreign key in the Receivers table (it will be some column with an underscore in its name, but you can rename that with Fluent API if you don't like the default name) or by adding your own foreign key property to the User class:
public Guid? BuddyGuid { get; set; }
[ForeignKey("BuddyGuid"), JsonIgnore]
public IList<User> Buddies { get; set; }

EF - Cascade Delete Not Working, Can't Delete Object

I get this error:
System.Data.SqlClient.SqlException The DELETE statement conflicted
with the REFERENCE constraint "FK_comments_postId__164452B1". The
conflict occurred in database "awe", table "dbo.comments", column
'postId'. The statement has been terminated.
I have this structure:
public class Post
{
public long Id { get; set; }
public string Body { get; set; }
public long? ParentId { get; set; }
public virtual Post Parent { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public long Id { get; set; }
public long PostId { get; set; }
public virtual Post Post { get; set; }
public string Body { get; set; }
}
my delete method:
public void Delete(long id)
{
var p = context.Set<Post>().Get(id);
if(p == null) throw new MyEx("this post doesn't exist");
if (p.Posts.Count > 0) throw new MyEx("this post has children and it cannot be deleted");
context.Set<Post>().Remove(p);
context.SaveChanges();
}
my DbContext:
public class Db : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
}
It sounds like the Post that you are trying to delete has child Comments.
Entity Framework will not take responsibility for cascading a delete in the database – it expects that you will achieve this by setting a cascading delete on the foreign key relationship in the RDBMS.
Having said this, if you delete a parent entity in Entity Framework, it will attempt to issue delete statements for any child entities which have been loaded into the current DbContext, but it will not initialize any child entities which have not yet been loaded. This may lead to the RDBMS throwing foreign key constraint violation exceptions if a cascading delete has not been specified, like the one you are seeing. For more details about how cascade delete “works” in Entity Framework, see this blog post.

MVC 3 EF 4.1 dbContext - Deleting one-to-many data object with non-nullable foreign-key relation

I am using MVC 3, EF 4.1, and dbContext. I need to know how to delete an entity in one-to-many relation with a non-nullable foreign-key.
When I Remove the child entity and execute SaveChanges I get the error:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
From other posts, I understand that using Remove(entity) marks the entity for delete. During SaveChanges, EF sets the the foreign-key to Null and the above error occurs.
I have found some posts that use DeleteObject on the child entity rather than Remove; however, the DeleteObject approach seems to have been dropped because of addition to dbContext and DbSet.
I have found posts that suggest modifying the EDMX foreign-key relation to be Nullable. Modifying the EDMX is fine, but whenever an Update Model for Database is done, these changes get nuked and must be reapplied. Not optimal.
Another post suggested creating a proxy entity with the foreign-key relations set to Nullable but I do not understand that approach. It seems to suffer from the same issue as modifying the EDMX in that the context gets automatically updated when changes to the EDMX are saved.
My simplified model is:
public partial class User
{
public User()
{
this.UserContacts = new HashSet<UserContact>();
}
public long userId { get; set; }
public string userEmail { get; set; }
public string userPassword { get; set; }
public string userFirstName { get; set; }
public string userLastName { get; set; }
. . .
public virtual Country Country { get; set; }
public virtual State State { get; set; }
public virtual ICollection<UserContact> UserContacts { get; set; }
}
}
public partial class UserContact
{
public long userContactId { get; set; }
public long userContactUserId { get; set; }
public long userContactTypeId { get; set; }
public string userContactData { get; set; }
public virtual ContactType ContactType { get; set; }
public virtual User User { get; set; }
}
The userContactUserId and userContactTypeId are required foreign-keys.
In the dbContext container both Users and UserContact are DbSet.
I have a ViewModel for the User and a ViewModel for UserContact as follows
public class UserContactViewModel
{
[HiddenInput]
public long UserContactId { get; set; }
[HiddenInput]
public long UserContactUserId { get; set; }
[Display(Name = "Contact")]
[Required]
public string ContactData { get; set; }
[Required]
public long ContactType { get; set; }
[HiddenInput]
public bool isDeleted { get; set; }
}
public class MyProfileViewModel
{
[HiddenInput]
public long UserId { get; set; }
[Required]
[Display(Name = "First Name")]
[StringLength(100)]
public string FirstName { get; set; }
[Required]
[StringLength(100)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
....
public IEnumerable<UserContactViewModel> Contacts { get; set; }
}
When saving changes to the user profile, I loop over the list of UserContactViewModel entities to determine which have been added, modified, or deleted.
foreach (var c in model.Contacts)
{
UserContact uc = usr.UserContacts.Single(con => con.userContactId == c.UserContactId);
if (uc != null)
{
if (c.isDeleted == true) // Deleted UserContact
{
ctx.UserContacts.Remove(uc); // Remove doesn't work
}
else // Modified UserContact
{
uc.userContactData = c.ContactData;
uc.userContactTypeId = c.ContactType;
ctx.Entry(uc).State = EntityState.Modified;
}
}
else // New UserContact
{
usr.UserContacts.Add(new UserContact { userContactUserId = model.UserId, userContactData = c.ContactData, userContactTypeId = c.ContactType });
}
}
I'd appreciate any help.
I managed to solve the problem as follows:
First, I was able to fetch the ObjectContext by casting my DbContext (eg "ctx") to an IObjectContextAdapter and then obtaining reference to the ObjectContext.
Next, I simply called the DeleteObject method passing the UserContact record to be deleted.
When SaveChanges gets the deletes in the database happen as expected.
if (c.isDeleted == true) // Deleted UserContact
{
ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
oc.DeleteObject(uc)
}
Here is a snippet of the relevant code:
foreach (var c in model.Contacts)
{
UserContact uc = null;
if (c.UserContactId != 0)
{
uc = ctx.UserContacts.Find(c.UserContactId);
}
if (uc != null)
{
if (c.isDeleted == true) // Deleted UserContact
{
ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
oc.DeleteObject(uc);
}
else // Modified UserContact
{
uc.userContactData = c.ContactData;
uc.userContactTypeId = c.ContactType;
ctx.Entry(uc).State = EntityState.Modified;
}
}
else // New UserContact
{
usr.UserContacts.Add(new UserContact { userContactData = c.ContactData, userContactTypeId = c.ContactType });
}
}
ctx.Entry(usr).State = EntityState.Modified;
ctx.SaveChanges();
Hope this helps someone in future.
I solved the same problem following the section "Cascade Delete Rules on Relationships" at the MSDN guide page here http://msdn.microsoft.com/en-us/library/bb738695.aspx
Hope tu be helpfull :D
well, implement your own ICollection and mark those child objects for deletion along with removing it. And then, in your own SaveChanges method override, delete those objects.
You can configure the relation to cascade ... this will propagate the delete to dependent entities.
But it's very dangerous :)
I prefer setting a flag in the row that prevents the data tier from including it in future queries, most applications do not need physical delete (and will be a chance to undo).

How do I get a delete trigger working using fluent API in EF CodeFirst CTP5?

I am having trouble getting referential integrity dialled down enough to allow my delete trigger to fire.
I have a dependent entity with three FKs. I want it to be deleted when any of the principal entities is deleted.
For principal entities Role and OrgUnit (see below) I can rely on conventions to create the required one-many relationship and cascade delete does what I want, ie: Association is removed when either principal is deleted.
For Member, however, I have multiple cascade delete paths (not shown here) which SQL Server doesn't like, so I need to use fluent API to disable cascade deletes.
Here is my (simplified) model:
public class Association
{
public int id { get; set; }
public int roleid { get; set; }
public virtual Role role { get; set; }
public int? memberid { get; set; }
public virtual Member member { get; set; }
public int orgunitid { get; set; }
public virtual OrgUnit orgunit { get; set; }
}
public class Role
{
public int id { get; set; }
public virtual ICollection<Association> associations { get; set; }
}
public class Member
{
public int id { get; set; }
public virtual ICollection<Association> associations { get; set; }
}
public class Organization
{
public int id { get; set; }
public virtual ICollection<Association> associations { get; set; }
}
My first run at fluent API code looks like this:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
DbDatabase.SetInitializer<ConfDB_Model>(new ConfDBInitializer());
modelBuilder.Entity<Member>()
.HasMany(m=>m.assocations)
.WithOptional(a=>a.member)
.HasForeignKey(a=>a.memberId)
.WillCascadeOnDelete(false);
}
My seed function creates the delete trigger:
protected override void Seed(ConfDB_Model context)
{
context.Database.SqlCommand("CREATE TRIGGER MemberAssocTrigger ON dbo.Members FOR DELETE AS DELETE Assocations FROM Associations, deleted WHERE Associations.memberId = deleted.id");
}
PROBLEM --
When I run this, create a Role, a Member, an OrgUnit, and an Association tying the three together all is fine. When I delete the Role, the Association gets cascade deleted as I expect, same with OrgUnit. -- HOWEVER -- when I delete the Member I get an exception with a referential integrity error. I have tried setting ON CASCADE SET NULL because my memberid FK is nullable but SQL complains again about multiple cascade paths, so apparently I can cascade nothing in the Member-Association relationship.
To get this to work I must add the following code to Seed():
context.Database.SqlCommand("ALTER TABLE dbo.ACLEntries DROP CONSTRAINT member_associations");
As you can see, this drops the constraint created by the model builder.
QUESTION: this feels like a complete hack. Is there a way using fluent API for me to say that referential integrity should NOT be checked, or otherwise to get it to relax enough for the Member delete to work and allow the trigger to be fired?
Thanks in advance for any help you can offer. Although fluent APIs may be "fluent" I find them far from intuitive.
var listTriggers = new List<string>();
var listStoreProcedures = new List<string>();
using (var command = _context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "select group_concat(TRIGGER_NAME) from information_schema.TRIGGERS where TRIGGER_SCHEMA = 'yourschema'";
command.CommandType = CommandType.Text;
_context.Database.OpenConnection();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var value = reader[0].ToString();
if (!string.IsNullOrEmpty(value))
listTriggers.AddRange(value.Split(","));
}
}
command.CommandText = "select group_concat(ROUTINE_NAME) from information_schema.ROUTINES where ROUTINE_TYPE = 'PROCEDURE' and ROUTINE_SCHEMA = 'yourschema'";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var value = reader[0].ToString();
if (!string.IsNullOrEmpty(value))
listStoreProcedures.AddRange(value.Split(","));
}
}
}
foreach (var item in listTriggers)
_context.Database.ExecuteSqlRaw($"drop trigger if exists {item}");
foreach (var item in listStoreProcedures)
_context.Database.ExecuteSqlRaw($"drop procedure if exists {item}");