Delete unattached many-to-many self-referencing relationship - c#

What's the best way to delete an unattached entity which has self-referencing relationships?
My example is pretty simple, just a People class with a List<People> Friends property:
Edit: I don't define an extra relationship object but I force Entity Framework to use an extra table:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<People>()
.HasMany(people => people.Friends)
.WithMany()
.Map(configuration =>
{
configuration
.MapLeftKey("From_PeopleId")
.MapRightKey("To_PeopleId")
.ToTable("Friendships");
});
}
The schema:
Id Name
== ======
1 Martha
2 Martin
3 Jim
From_PeopleId To_PeopleId
============= ===========
1 2
1 3
3 2
And how I'd like to delete old Jimmy Boy:
using (var context = new FriendsDbContext())
{
var people = context.Peoples.Find(3);
context.Peoples.Remove(people);
context.SaveChanges();
}
SqlException #1:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Friendships_dbo.People_From_PeopleId".
The conflict occurred in database "FriendsDb", table "dbo.Friendships", column 'From_PeopleId'.
My second approach to get rid of old Jimmy boy including his relations:
using (var context = new FriendsDbContext())
{
var people = context.Peoples
.Include(p=>p.Friends)
.Single(p=>p.Id==3);
context.Peoples.Remove(people);
context.SaveChanges();
}
SqlException #2:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Friendships_dbo.People_To_PeopleId".
The conflict occurred in database "FriendsDb", table "dbo.Friendships", column 'To_PeopleId'.
I know why the SqlExceptions occured (SQL Server is not capable to provide cascade delete allowing the deletion of all relations pointing from and to old Jimmy boy at once). So my question is: How could I do it with the help of Entity Framework easily? Easily like DELETE Friendships WHERE From_PeopleId=3 OR To_PeopleId=3.

Try to delete the relations before or in the meantime
using (var context = new FriendsDbContext())
{
var friendships = context.Friendships.Where(x => x.From_PeopleId == 3 || x.To_PeopleId == 3).ToList();
context.RemoveRange(friendships);
var people = context.Peoples.Find(3);
context.Peoples.Remove(people);
context.SaveChanges();
}

Related

Upgraded to Entity Framework 6 Related entities not being inserted

I upgraded From EF 4 to EF6 in my solution. Now my related entities are not getting inserted. How do I add records to the entity with foreign key relationship from the original enity
var _bp = new BP(); //BP is an entity
workflowList.ForEach(wf =>
{
var wflow = new Workflow
{
currentstep = CustomConvert.ToIntNullable(wf.currentstep),
desc = wf.desc,
name = wf.name,
wfId = wf.id,
BP = _bp,
IsActive = true
};
});
db.BP.AddObject(_bp);
db.SaveChanges();
The code above would add a record in BP table and add multiple records in the workflow table in EF 4 but in EF 6 it does not add any record to the Worflow table. How do I accomplish the same in EF6

asp.NET LINQ Delete from database

I have a SQL Server database with 2 tables:
t1 - Category
Id
Name
t2- Product
Id
Name
CategoryId
I want to delete a row from the Category table, but since I have the foreign key I need to handle the products that has the CategoryId I want to delete.
So I did this:
var ProdCatID = (from prod in DataContext.Products
where prod.CategoryId == Convert.ToInt32(Id)
select prod).First();
ProdCatID.CategoryId = null;
DataContext.SubmitChanges();
var DelCat = (from cat in DataContext.Categories
where cat.Id == Convert.ToInt32(Id)
select cat).ToList();
DataContext.Categories.DeleteAllOnSubmit(DelCat);
DataContext.SubmitChanges();
What Im trying to do is to check if there is any product with thatCategoryId, if there is - I want to set theCategoryIDto null and then delete the row from theCategory` table.
It is working when I have a product with a CategoryId but when I can't delete it.
Any ideas?
You're only setting the first product that has this CategoryID to null - you need to handle all products that have that ID !
var products = (from prod in DataContext.Products
where prod.CategoryId == Convert.ToInt32(Id)
select prod).ToList();
foreach(Product p in products)
{
p.CategoryId = null;
}
DataContext.SubmitChanges();
.....
After that, now you should be able to delete the category from the table
Simple!
Change the Product table configuration in Database!
ALTER TABLE Product
ADD CONSTRAINT 'Category_FK'
FOREIGN KEY (CategoryId)
REFERENCES Category(Id)
ON DELETE SET NULL;
whenever you delete a primary key will automatically put null!
Cascade on Delete is there in entity framework. Cascade delete automatically deletes dependent records or set null to foreignkey properties when the principal record is deleted.
This is one to many reletionship between parent and child
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(a => a.UserDetail)
.WithOptionalDependent()
.WillCascadeOnDelete(true);
}
For more details check this: http://www.entityframeworktutorial.net/code-first/cascade-delete-in-code-first.aspx

Forcefully Preventing EF from Inserting an Already Existing Object

We have Users, and Roles with a Many-to-Many relationship defined.
The Fluent API that defines Role:
public RoleMap()
{
// Primary Key
this.HasKey(t => t.RoleString);
// Properties
this.Property(t => t.RoleString)
.IsRequired()
.HasMaxLength(50);
// Table & Column Mappings
this.ToTable("Roles");
this.Property(t => t.RoleString).HasColumnName("Role");
// Relationships
this.HasMany(t => t.Users)
.WithMany(t => t.Roles)
.Map(m =>
{
m.ToTable("UserRoles");
m.MapLeftKey("Role");
m.MapRightKey("UserName");
});
}
the following code (as expected) wants to insert a new Role in Roles, then a matching record in UserRoles for the user "MrAdmin".
var user = db.Users.Find("MrAdmin");
user.Roles.Add(new Role("Administrator"));
db.SaveChanges();
I very specifically DO NOT want to lookup the already existing "Administrator" role from the Roles table. The actual application has an enum of Roles that get passed in, so the user does not need to be aware of the Roles table or any of its content at all.
In other words, I know the following code works, but I am looking to manipulate EF at a lower level:
var adminRole = db.Roles.Find("Administrator");
var user = db.Users.Find("MrAdmin");
user.Roles.Add(adminRole);
db.SaveChanges();
What I'm trying to do is force EF to avoid adding any -- or already existing -- records to the Roles table.
I have attempted two approaches:
1) Create an INSTEAD OF INSERT TRIGGER on the DB side, to simply accept the insert request and not let EF worry about it. But EF still knew something was fishy and threw an exception.
Trigger:
CREATE TRIGGER [dbo].[tr_DontThrowOnInsert] ON [dbo].[Roles]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
DECLARE #ROLE as varchar(50)
SELECT #ROLE = [Role] from inserted
IF EXISTS(SELECT [Role] FROM dbo.Roles WHERE [Role] = #ROLE)
UPDATE dbo.Roles
SET [Updated] = GetUtCDate()
output inserted.[Role]
WHERE [Role] = #Role
ELSE
INSERT INTO dbo.Roles
output inserted.[Role]
SELECT * FROM inserted
WHERE [Role] not in (SELECT [Role] FROM dbo.Roles)
END
2) override the SaveChanges() in DBContext, and mark the suspect entries as Unchanged:
public override int SaveChanges()
{
this.ChangeTracker.Entries<Role>().ForEach(r => r.State = EntityState.Unchanged);
return base.SaveChanges();
}
Exception:
Result Message: System.InvalidOperationException : Saving or accepting changes failed because more than one entity of type
'Manufacturing.Domain.LookupRole' have the same primary key value.
Ensure that explicitly set primary key values are unique. Ensure that
database-generated primary keys are configured correctly in the
database and in the Entity Framework model. Use the Entity Designer
for Database First/Model First configuration. Use the
'HasDatabaseGeneratedOption" fluent API or
'DatabaseGeneratedAttribute' for Code First configuration.
I have decided to use EF 6 Interceptors. IDbCommandTreeInterceptor and IDbCommandInterceptor
...
http://msdn.microsoft.com/en-us/data/dn469464.aspx#BuildingBlocks/

Error in MySQL syntax when adding object to objectcontext

I seem to have trouble adding objects to tables that have a 'n to n' relationship.
Tables are defined as follows:
Table A
ID (PRIMARY)
...
...
...
Table B
ID (PRIMARY)
...
...
...
Table C
TableA_ID (index)
TableB_ID (index)
So basically Table C links Table A and B, by their IDs. Using the entity framework we now have an object TableA containing an Entity Collection of TableB entities.
However when I add an existing object of type TableB to the TableA.TableBs entity collection property, I receive an exception:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(SELECT\n TableC.TableA, \n ' at line 1
It seems that I'm trying to do a very normal / common thing, however I've not been successful getting this to work.
C# code:
var database = new DatabaseEntities();
var tableAObject = database.SingleOrDefault(e => e.ID == 1);
var tableBObject = database.SingleOrDefault(e => e.ID == 1);
tableA.TableBEntities.Add(tableBObject);
database.SaveChanges();
Apparently I'm doing something wrong, so my question is, how should I add an object to Table C?

Table with 2 foreign keys entity framework

I have a table which consists of 2 foreign keys. And those are only elements of the table. The table is meant to create association between 2 other tables. For example: The table is Users_Products, and the only 2 columns are UserId and ProductID, both foreign keys. When I generated the EF object from database it didn't create Users_Products object, it only automatically created navigation properties. Now how can I insert data in my Users_Products table using EF?
You can get some user object and add product into its navigation property.
User user = context.Users.Where(u => u.Id == 1);
Product product = context.Products.Where(p => p.Id == 1);
user.Products.Add(product);
context.SaveChanges();
For code examples that show how to work with many-to-many relationships in EF see the Working with Many-to-Many Relationships section in
The Entity Framework 4.0 and ASP.NET – Getting Started Part 5.
That is EF 4.0 / Database First; for an example using the DbContext API, see Adding Course Assignments to the Instructor Edit Page in Updating Related Data with the Entity Framework in an ASP.NET MVC Application (6 of 10).
using ( var ctx = new ...)
{
var user = new User();
var product = new Product();
user.Products.Add(product);
ctx.Users.AddObject(user);
ctx.SaveChanges();
}
If you want to create relation (insert record to User_Products table) you just need to use navigation property either on User or Product:
user.Products.Add(product);
or
product.Users.Add(user);
That means you must have navigation property on at least one side to be able to create the relation. If you have loaded entities from the current contest you can use the approach described by #Pavel.
If you don't have loaded entities or if you don't want to do two queries to the database just to make a relation you can use this workaround:
// Make dummy objects for records existing in your database
var user = new User() { Id = 1 };
var product = new Product() { Id = 1 };
// Ensure that your context knows instances and does not track them as new or modified
context.Users.Attach(user);
context.Products.Attach(product);
// Again make relation and save changes
user.Products.Add(product);
ctx.SaveChanges();

Categories