NHibernate: Laziness.NoProxy prevents soft delete - c#

I have this for an NHibernate mapping:
public AnswerSet_AnswerMap() {
Table("DB.AnswerSet_Answer");
Id(x => x.AnswerSet_AnswerId);
References(x => x.Answer, "BaseAnswerID").LazyLoad(Laziness.NoProxy);
References(x => x.AnswerSet, "AnswerSetID").Fetch.Join();
Map(x => x.Format);
}
It's for a junction table between an "AnswerSet" and an "Answer". The .LazyLoad(Laziness.NoProxy) on the answer reference is necessary in our application, but it prevents an AnswerSet_Answer object from being soft deleted in the database (it remains in the database unchanged). Does anyone know why this is happening?

Did you try adding a cascade? Cascade.DeleteAllOrphan ?
NHibernate Cascades:
Entities has associations to other objects, this may be an association to a single item (many-to-one) or an association to a collection (one-to-many, many-to-any).
At any rate, you are able to tell NHibernate to automatically traverse an entity's associations, and act according to the cascade option. For instance, adding an unsaved entity to a collection with save-update cascade will cause it to be saved along with its parent object, without any need for explicit instructions on our side.
Here is what each cascade option means:
none - do not do any cascades, let the users handles them by themselves.
save-update - when the object is saved/updated, check the associations and save/update any object that require it (including save/update the associations in many-to-many scenario).
delete - when the object is deleted, delete all the objects in the association.
delete-orphan - when the object is deleted, delete all the objects in the association. In addition to that, when an object is removed from the association and not associated with another object (orphaned), also delete it.
all - when an object is save/update/delete, check the associations and save/update/delete all the objects found.
all-delete-orphan - when an object is save/update/delete, check the associations and save/update/delete all the objects found. In additional to that, when an object is removed from the association and not associated with another object (orphaned), also delete it.
But cascade will delete your orpahnd record,not "soft-delete" it.
for soft deletion look into this link : Soft Deletes In NHibernate

Related

Code first of EF, how to define navigation property relationship without setting foreign key between table in database

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);

Is it possible to specify child objects that should also be deleted with their parents in Linq?

In order to make sure that all child elements are deleted, I am currently having to do this:
ComponentType type = db.ComponentTypes.First(t => t.ID == Convert.ToInt32(button.CommandArgument));
db.RigActions.DeleteAllOnSubmit(type.Components.SelectMany(c => c.RigActions));
db.Components.DeleteAllOnSubmit(type.Components);
db.ComponentTypes.DeleteOnSubmit(type);
db.SubmitChanges();
For maintainability purposes, this is scaring me that I (or another developer) might overlook all of the cleanup necessary when a parent (or in this case - a parent of a parent) element is deleted.
Does Linq have any type of dependency property I can set so that when a Component type is deleted, it deletes all component records, and all actions belonging to each component record?
I'm not sure it can be done in Linq (it probably can). It would be far easier if you defined a CASCADE DELETE on your database though. That way, child records would be deleted automatically and you'd not need to worry about forgetting anything.
This article might give you a bit more information
http://www.mssqltips.com/sqlservertip/2743/using-delete-cascade-option-for-foreign-keys/
This article is about setting up cascade delete within EF
Entity Framework on delete cascade

How to manually load related entities in a N:N relationship?

I am using EF5 and when the the relationship is 1:N, if I want to load related entities I do the following:
With T-SQL I load from database the main entities with a T-SQL like that:
select *
from MainEntities
where ...
with T-SQL I load the related entities
select *
from RelatedEntities
where IDMainEntity IN (---)
At this point EF populate the property navigation of the main entities with the related entities. Also, in the local property of the type of each entity in the dbContext I have all the entities of each type.
However, if i do the same with a N:N relationship, I don't have the entity of the middle table of the relation, and when I execute the queries I have in the local of the dbContext the entities of each type, but the property navigation is not populated.
I would like to know why and if it exists some alternative.
I use this way because I want to use T-SQL for create dynamic queries. If I use eager loading I don't have the same flexibility to dynamic queries than when I use TSQL, and it is less efficient. If I use explicit loading I to do N additional queries, one of each record in the results of the main entity With my way, I only one additional query, because I get all the related entities at once. If I use lazy loading I have the same problem, N additional queries.
Why EF does not populate the related properties when the relation is N:N?
Thanks.
The feature you are talking about is called Relationship Span or Relationship Fixup and indeed - as you have noticed - it does not work for many-to-many relationships. It only works if at least one end of the association has multiplicity 1 (or 0..1), i.e. it works for one-to-many or one-to-one relationships.
Relationship Span relies on an entity having a foreign key. It doesn't matter if it has an explicit foreign key property (foreign key association) or only a foreign key column in the corresponding database table without a property in the model (independent association). In both cases the FK value will be loaded into the context when the entity gets loaded. Based on this foreign key value EF is able to figure out if a related entity that has the same primary key value as this FK value is attached to the context and if yes, it can "fixup the relationship", i.e. it can populate the navigation properties correctly.
Now, in a many-to-many relationship both related entities don't have a foreign key. The foreign keys are stored in the link table for this relationship and - as you know - the link table does not have a corresponding model entity. As a result the foreign keys will never be loaded and therefore the context is unable to determine which attached entities are related and cannot fixup the many-to-many relationship and populate the navigation collections.
The only LINQ queries where EF will support you to build the correct object graph with populated navigation collections in a many-to-many relationship are eager loading...
var user = context.Users.Include(u => u.Roles).First();
...or lazy loading...
var user = context.Users.First();
var rolesCount = user.Roles.Count();
// Calling Count() or any other method on the Roles collection will fill
// user.Roles via lazy loading (if lazy loading is enabled of course)
...or explicit loading with direct assignment of the result to the navigation collection:
var user = context.Users.First();
user.Roles = context.Entry(user).Collection(u => u.Roles).Query().ToList();
All other ways to load the related entities - like projections, direct SQL statements or even explicit loading without assignment to the navigation collection, i.e. using .Load() instead of .Query().ToList() in the last code snippet above - won't fixup the relationship and will leave the navigation collections empty.
If you intend to perform mainly SQL queries rather than LINQ queries the only option I can see is that you write your own relationship management. You would have to query the link table in addition to the tables for the two related entities. You'll probably need a helper type (that is not an entity) and collection that holds the two FK column values of the link table and probably a helper routine that fills the navigation collections by inspecting the primary key values of the entities you find as attached in the DbSet<T>.Local collections and the FK values in the helper collection.
To add on #Slauma answer:
I faced the same problem recently, getting frustrated that the navigation property is not being set after calling Query().Where().Load(), although I can see that the objects are loaded into the DbContext.
I needed the collection to be part of my main object and use it as you would any other navigation property and not just manage a separate collection, so I did this:
project.Labels = this.Context
.Entry (project)
.Collection (p => p.Labels)
.Query ()
.Where (l => l.CreateUserName == this.UserId)
.ToList();
The problem with this is that EF thinks I added new relationships, which I can't blame it, but it is not what I wanted. As a result, when trying to save the Project object I got an exception when EF tried to insert the relationship into the link table because a row with the same key (projectId + labelId) already exists.
So, the final was to reset the state of the relationships between the project and the labels:
foreach (Label l in project.Labels)
{
((System.Data.Entity.Infrastructure.IObjectContextAdapter)this.Context.AsDbContext ()).ObjectContext.ObjectStateManager.ChangeRelationshipState<Project> (project, l, p => p.Labels, EntityState.Unchanged);
}
After that I was able to use the Labels property just like any other navigation property, not caring that behind the scenes it's a many-to-many relationship.

Entity Framework - Deleting Complex Object

I'm have the following Entity Framwork objects:
Evaluation, Stage, Apartment
Evaluation contains many Stages and Stage contains many Apartments.
I'm trying to deleted a certain stage as follows:
var deletedStages =
originalEvaluation.Stages.Where(s => s.State == StateTypes.Deleted);
deletedStages.ToList().ForEach(stage =>
{
stage.Apartments.ToList().ForEach(
apartment => stage.Apartments.Remove(apartment)
);
originalEvaluation.Stages.Remove(stage);
});
deletedStages.ToList().ForEach(stage =>
{
stage.Apartments.ToList().ForEach(apartment =>
shechtmanEntities.Apartments.DeleteObject(apartment)
);
shechtmanEntities.Stages.DeleteObject(stage);
});
}
}
try
{
shechtmanEntities.SaveChanges();
}
But I'm keep getting an Exception : "The relationship could not be changed because one or more of the foreign-key properties is non-nullable".
I know it has to do with a null foreign-key, but I can't understand which? and Why?
Thanks.
If you cannot Cascade Delete (SQL server can be funny about that: https://stackoverflow.com/a/6065583/613004) asdutzu suggests, then:
If it's one-to-many relationship (sounds like it is), then you'll need to manually delete each child object and save-changes before deleting the parent. I.E. delete the apartment (or re-assign it to another Stage), then delete the stage, and so on.
Otherwise, if it's many-to-many, and the joining table is exposed through the entity model, delete the joins between the stages and apartments first, then delete the stage. If not, then detach the apartment from the stage and save changes before deleting the stage.
Try just deleting the Evaluation or Stage and rely on a Cascade Delete to remove it's children entities (in your case Apartments) rather than removing them each individually.

Nhibernate 3.3.0.4000 incorrectly orphaning child entities

I have an intermittent problem in a production system that I haven't been able to recreate locally. What appears to be happening is that for no obvious reason during an edit of a child entity and saving the parent containing the collection the parent association on the child entity is removed in the database. Effectively orphaning the child entity.
Here is the fluent mapping:
mapping.HasMany<ExpenseItem>(x => x.ExpenseItems).Cascade.AllDeleteOrphan();
The entity can also be directly saved (it is also aggregate root), but during testing this has not be shown to remove the parent association.
There are no references to the parent itself in the child object the association is all in the DB handled by Nhibernate.
There is also no possibility of assigning the same child entity to another parent as expense item (child entity) cannot be added outside of its parent directly. See code below.
public virtual ExpenseItem AddNewExpenseItem(ExpenseAnalysis analysis,
string recipientName,
string purchaseAccountReference,
string expenseDescription,
string expenseNotes,
Money value,
VATAnalysis vat)
{
Validate.IsNotNull<ExpenseAnalysis>(analysis);
Validate.IsNotNull(expenseDescription);
....
ExpenseItem newExpenseItem = new ExpenseItem(analysis,
recipientName,
purchaseAccountReference,
expenseDescription,
expenseNotes,
value,
vat,
expenseItemUniqueReference,
true,
Candidate.Assessment);
_expenseItems.Add(newExpenseItem);
....
Any ideas?
It would definitely help to see your mapping but take a look at
I'd also map the ExpenseItem with a reference to its parent
How to delete child object in NHibernate?
basically you ve to set inverse on the relationship so that NHibernate does not try to update the deleted record with a null victim. And you set Cascade.All | Cascade.DeleteOrphans so that we aren't just breaking the relationship by nulling out the victim, but deleting the entire child record.
This problem was caused by a bug in NHibernate. I had set the session to flush never however when you call isdirty on the session the session was being flushed first. The user was deleting the expense item then cancelling the edit but during the isdirty check the deletion was being flushed. Managed to work around this by changing how the session was being managed for this type of dialog.

Categories

Resources