I am using the Ado.Net Entity Framework with ASP.NET MVC.
In my MSSQL 2008 Database I have for example the following simplified tables and relations:
(Song) 1--* (Version) 1 -- 1 (VersionInfo)
Is it possible to automatically have the linked Versions and their VersionInfo's deleted when I delete a Song?
Currently I am using something like the following code, which is a lot of manual work, since some tables have up to 8 relations and those relations have subrelations too sometimes:
db = new Database() //Entities
Song song = db.Song.First();
Song.Version.Load();
foreach(Version version in Song.Version.ToList())
{
//Remove Song-Version Reference.
song.Version.Remove(version);
//Get The VersionInfo so we can delete it after we deleted the Version object.
version.VersionInfoReference.Load();
VersionInfo versionInfo = version.VersionInfo;
//Now we can delete the Version Object.
db.DeleteObject(version);
//Now we can also delete the versionInfo, since the reference is gone.
db.DeleteObject(versionInfo);
}
db.DeleteObject(song);
There must be an easier way to get cascading deletions. I already tried setting the relationship setting in MSSQL to Cascade when Deleting, but it didn't do a thing... Did I miss something there?
Anyway, how do other people solve this problem?
You should not be doing this in the Entity Framework. All popular relational databases support ON CASCADE DELETE on foreign keys which is a lot more efficient as well. I suggest you just go with that.
It appears in your case you may have to cascade deletes in song to version and deletes in version to version info. Just load up the table designer in SQL Manager and you should see the relevant options under relationships.
I tried it with a simpler database, 2 tables, and found out that cascade is only from the 1 side of a 1-many.
table A
id - int
b_id - int
table B
id - int
Relationship is set between A.b_id and B.id.
Delete rule is cascade.
When I delete A, B is not deleted.
When I delete B, A is deleted.
Problem is, I want to have B deleted when I delete A. I guess that is only possible manually.
If you set it correctly in the database then it should definitely cascade the deletes. I think they improved this in the next version of the Entity Framework but I am not certain. I just remember seeing cascade somewhere. I'd recommend you have a look in the database again.
For instance, are there any other relations that also needs to be cascaded?
in the RTM of EF, select a relationship in the designer, press F4 to see properties. you'll see two OnDelete properties (one for each side of the relationship) set to "none". you can set either side to "cascade" (I think you have to set it on the parent)
Related
I use code first of Entity framework. There are two classes "Question" and "User". I defined a relationship as below:
this.HasRequired(v => v.Creator).WithMany(v => v.Questiones)
.HasForeignKey(v => v.CreatorId).WillCascadeOnDelete(false);
After gernerating the database I found that it always create foreign key between Id of User and CreatorId of Question. Because of lower performance of FK(and other reason),I want to define navigation property relationship without setting foreign key in database? Delete FK after EF created it?
If cannot do this using fluent api, could you tell me why EF designed in this way please?
About the lower performance of FK. I have a User table with 5 Million records in it. when I insert a Question into db, since the db check the question.CreatorId validation from User table, it always slower than without FK.
And there are many other reasons that I need to remove FK.
I think I am somewhat obsession because I think that deleting FK after created it is strangely and ugly. What i want is implementing this by using something like WithoutForeignKey in fluent api:
this.HasRequired(v => v.Creator).WithMany(v => v.Questiones)
.WithoutForeignKey(v => v.CreatorId).WillCascadeOnDelete(false);
Without questioning why are you trying to do this strange thing and going just to the answer: you could delete fk constraint after generated, or you could use migrations and remove FK generation from the migration code.
SQL code generated when traversing nav properties will work even if fk constraint doesn't exist, except for cascade deleting
If you want a relationship between two tables, you need to define a foreign key. No way around it. Even if you use Map() in fluent api, you can only hide the foreign key in your model, in the background EF will still use it and it will exist in the database.
Also I don't get what you mean by "performance" of foreign key? One extra (likely small) column won't make a difference. If you mean the navigation properties for the performance part, you can do 3 things:
Don't include them in your model
Make them non-virtual to disable lazy loading
Disable lazy loading all together with ctx.Configuration.LazyLoadingEnabled = false;
If you don't want to tell db about relation and treat both entities as not related (I wonder why), then just ignore these navigation properties and FK field. Note that you will be responsible for managing related entities: saving and loading them from db, updating ids etc
this.Ignore(q => q.Creator);
this.Ignore(q => q.CreatorId);
And you also need to ignore other side of relation, otherwise EF will generate FK column with default name Creator_CreatorId. So in Creator entity configuration:
this.Ignore(c => c.Questiones);
In EF cross reference tables are abstracted away by creating many-to-many relationships. E.G.
There's a SQL table dbo.TrialContactCrossReference that relates TrialContactId to TrialID. Now, EF did not generate an Entity TrialContactCrossReference because it went with this MANY-MANY relationship thing. How do I add a new row to said table?
I tried
context.TrialContacts.??? and context.ClinicalTrials.??? and just don't know what to do with this. If I have a new Contact that I want to relate to a trial how am I supposed to go about it?
trial.Contacts.Add(contact);
OR
contact.Trials.Add(trial);
OR (and my advise)
you could create an additional entity for cross reference table. this will convert many-many to 2 one-many relationships. more then %90 cases crosstables has additional columns (at least IsActive, RecordDate etc.) even it doesnt, it may be so in future and it requires you make lots of changes in code.
If I have a new Contact that I want to relate to a trial how am I supposed to go about it?
Assuming you have an existing Contact instance just do:
trial.TrialContacts.Add(contact);
context.SaveChanges();
EF will take care of the intermediate table insert for you. Note that adding Contacts and Trials works the same as if they weren't related.
There's a trick that was not obvious to me when setting this up. TableA must be added to TableB, not just to itself. In fact, looking at the generated entities each entity has a List<> of the other entity.
class TableA
{
List<TableB> TableB;
}
class TableB
{
List<TableA> TableA;
}
For example, if I want to add a TrialContact to a ClinicalTrial then I write:
context.ClinicalTrials.TrialContacts.Add(trialContact);
context.SaveChanges()
Then the xRef table be updated to reflect the relationship.
I am using the Northwind sample database. I have this code:
var db = new NorthwindEntities();
int id = 2; // Example
var delObject = (from o in db.Orders.Include("Order_Details")
where o.OrderID == id
select o).First();
db.Orders.DeleteObject(delObject);
db.SaveChanges();
I have an (1-to-many) association in Order - Order Details, with cascading deletes. (If I delete one Order, all Order_Details with the same OrderID will be deleted).
I have LazyLoading enabled.
If I delete the .Include("Order_Details") in the from clause, the cascade delete won't work.
Why does this happen? Isn't lazy initialization supposed to "include" the Order_Details for me, and eventually let me cascade delete?
The cascading deletes is defined in your EF model.
EF will therefore generate delete statements for data that it has loaded. EF will not go to the database to check what it should delete.
You can define cascading deletes (depending on your database) at the database level. In this case EF will delete the top node and the database will delete the related rows.
Do you have the cascading delete defined in both the database and the entity configuration? I've seen where having it defined in only one and not the other can cause this problem.
Cascade deletions in EF model deletes only those details which has been loaded in context. In case if you do use Include Order_Details are loaded during query along with Orders. If you enabled LazyLoading and not use Include then they are loaded on as needed basis, i.e. when you reference navigation properties.
Thus if you don't care about details and agree that they will be silently deleted with master record you have to define cascade deletion both in EF model and DB schema.
Pardon the massive headline.
I'm in the situation of having to build an application on top of a database, that I cannot make any changes to. The database does not have any primary- or foreignkeys set.
I'm using linq-2-sql, and I'm interested in having some properties exposed on the entities generated from my dbml. For instance, in the hypothetical example of a one-to-many relationship between table education and student - where each student record has a reference to an education id, I'd like to be able to go:
var student = GetAStudentFromContextOrWhatever();
var studentsEducation = student.Education;
It is my experience, that this kind of property is automatically generated when I drag'n'drop tables with foreignkey relationships from the server explorer.
However as previously mentioned, in this case I do not have these foreign key relationships - rather I am adding the relationships manually in the dbml file, specifying parent and child class.
When I add these relationships, I expect the involved entities in the designer.cs of my context to get populated with properties of a kind like those described above.
This, however, does not happen.
What must I do for my dbml to create these properties for me - based on these manually mapped associations between entities/tables that, on a database level, do not have foreign key associations?
Cheers!
L2S is just that Linq-to-SQL. If it isn't in SQL it won't be generated. The expression trees behind L2S just can't understand what you are doing. The place for your association is in a partial class file which you will have create manually. Also it probably won't update or insert through the association.
I know this is a very old question, but I just ran into the same problem. In order for the relationship in the DBML designer to automatically create the association properties for you, you need to have primary keys on your objects. If you click the column name in the designer, you'll see that your PK field has PrimaryKey = false. Switch that to True and build; all should be well.
Patrick
I have the following situation:
Customers contain projects and projects contain licenses.
Good because of archiving we won't delete anything but we use the IsDeleted instead.
Otherweise I could have used the cascade deletion.
Owkay I work with the repository pattern so I call
customerRepository.Delete(customer);
But here starts the problem. The customer is set to isdeleted true. But then I would like to delete all the projects of that customer and each project that gets deleted should delete all licenses as well.
I would like to know if there is a proper solution for this.
It has to be performant though.
Take note that this is a simple version of the actual problem. A customer has also sites which are also linked to licenses but I just wanted to simplify the problem for you guys.
I'm working in a C# environment using sql server 2008 as database.
edit: I'm using enterprice libraries to connect to the database
One option would be to do this in the database with triggers. I guess another option would be use Cascade update, but that might not fit in with how your domain works.
Personally I'd probably just bite the bullet and write C# code to do the setting of IsDeleted type field for me (if there was one and only one app accessing the DB).
I recommend just writing a stored procedure (or group of stored procedures) to encapsulate this logic, which would look something like this:
update Customer set isDeleted = 1
where CustomerId = #CustomerId
/* Say the Address table has a foreign key to customer */
update Address set isDeleted = 1
where CustomerId = #CustomerId
/*
To delete related records that also have child data,
write and call other procedures to handle the details
*/
exec DeleteProjectByCustomer(#CustomerId)
/* ... etc ... */
Then call this procedure from customerRepository.Delete within a transaction.
This totally depends on your DAL. For instance NHibernate mappings can be setup to cascade delete all these associated objects without extra code. I'm sure EF has something similar. How are you connecting to your DB?
If your objects arent persisted, then the .NET GC will sweep all your project objects away once there is no reference to them. I presume from your question though that you are talking about removing them from the database?
If your relationships are fixed (i.e. a license is always related to a project, and a project to a customer), you can get away with not cascading the update at all. Since you're already dealing with the pain of soft deletes in your queries, you might as well add in the pain of checking the hierarchy:
SELECT [...] FROM License l
JOIN Project p ON l.ProjectID = p.ID
JOIN Customer c on p.CustomerID = c.ID
WHERE l.IsDeleted <> 1 AND p.IsDeleted <> 1 AND c.IsDeleted <> 1
This will add a performance burden only in the case where you have queries on a child table that don't join to the ancestor tables.
It has an additional merit over a cascading approach: it lets you undelete items without automatically undeleting their children. If I delete one of a project's licenses, then delete the project, then undelete the project, a cascading approach will lose the fact that I deleted that first license. This approach won't.
In your object model, you'd implement it like this:
private bool _IsDeleted;
public bool IsDeleted
{
get
{
return _IsDeleted || (Parent == null ) ? false : Parent.IsDeleted;
}
set
{
_IsDeleted = value;
}
}
...though you must be careful to actually store the private _IsDeleted value in the database, and not the value of IsDeleted.