How to set "cascade delete" option to "Set Null" in Fluent NHibernate? - fluent-nhibernate

I am new to Fluent nHibernate and would like to know, if I have two classes Profile and Email mapped one-to-many as following... I want to define a nHibernate mapping fluently so when Profile is deleted, Email will remain in the DB, with a key set to Null. Or in other words to have "ON DELETE SET NULL"
ALTER TABLE [dbo].[Email] WITH CHECK ADD CONSTRAINT [FK4239B252F6539048] FOREIGN KEY([ProfileId])
REFERENCES [dbo].[Profile] ([Id])
ON UPDATE SET NULL
ON DELETE SET NULL
Any help is greately appreciated!
public sealed class ProfileMapping : ClassMap<Profile>
{
public ProfileMapping()
{
// Some other fields here ...
HasMany(x => x.Emails);
}
}
public class EmailMapping : ClassMap<Email>
{
public EmailMapping()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Address).Not.Nullable().UniqueKey("UX_EmailAddress").Length(254);
Map(x => x.Confirmed);
}
}

You won't be able to specify this behavior automatically in Fluent NHibernate AFAIK. Although the ON DELETE/ON UPDATE behavior specification is common to all DBs that NHibernate supports, NH/FNH control cascading with specific levels of cascade behavior:
none - do not do any cascades, let the users handles them by themselves.
save-update - when the object is saved/updated, check the assoications and save/update any object that require it (including save/update the assoications in many-to-many scenario).
delete - when the object is deleted, delete all the objects in the assoication.
delete-orphan - when the object is deleted, delete all the objects in the assoication. In addition to that, when an object is removed from the assoication and not assoicated with another object (orphaned), also delete it.
all - when an object is save/update/delete, check the assoications and save/update/delete all the objects found.
all-delete-orphan - when an object is save/update/delete, check the assoications and save/update/delete all the objects found. In additional to that, when an object is removed from the assoication and not assoicated with another object (orphaned), also delete it.
As you can see, "SET NULL" is not one of the available cascade behaviors.
The best you can do in this case is to not cascade at all, and instead to define the relationship as "Inverse" (E-mails "control" what profile they belong to; Profiles do not "own" their E-mails as such), and to implement logic either in your Repository or attached to the NHibernate Session that will remove all references of the child Emails to their parent Profile, then save all the children as "orphan" records before deleting the Profile.

Related

Intershop EDL modelling - How to add dependency with on cascade delete

We have some custom objects modelled through EDL which have foreign keys to system Intershop objects (ISPRODUCT and ISORDER). We need our objects to get deleted when referenced order or product is deleted.
This is the extract from the EDL file:
/**
* Relation to product PO (tariff item)
*/
dependency tariff: ProductPO
{
foreign key(tariffID);
}
/*
* Order relation
*/
dependency order: OrderPO
{
foreign key(orderID);
}
As I can see, it is possible to add delete actions on EDL relations but it is not possible to add delete actions on dependencies.
What we are doing at the moment is modifying the statements in the generated dbconstraints.oracle.ddl files like this:
EXEC staging_ddl.add_constraint('A1APPLICATIONFORM', 'A1APPLICATIONFORM_CO_003', 'FOREIGN KEY (TARIFFID) REFERENCES PRODUCT (UUID) ON DELETE SET NULL INITIALLY DEFERRED DEFERRABLE DISABLE NOVALIDATE');
EXEC staging_ddl.add_constraint('A1APPLICATIONFORM', 'A1APPLICATIONFORM_CO_004', 'FOREIGN KEY (ORDERID) REFERENCES ISORDER (UUID) ON DELETE CASCADE INITIALLY DEFERRED DEFERRABLE DISABLE NOVALIDATE');
But it is only the temporary workaround because these files will get overwritten each time we restart the code generator on the EDL.
On relationship it is possible to define the on delete action like this:
relation promotionBenefitPOs : A1PromotionBenefitPO[0..n] inverse promotionPO implements promotionBenefits delete default;
Is it possible to achieve the same thing on the dependency with the system objects?
I didn't know that was possible with EDL, good to know. My problem with this approach is that the orm cache does not know that these objects are being removed by oracle, so it might have phantom object floating around in the orm cache.
I would use this register listener solution to remove these objects so that everything is updated and flushed out of the cache.
I do wonder how the code generator deals with this delete property on the relation.
I'm afraid you need to do that by hand. Meaning once an instance of the types involved is removed, you need to query for your custom glue object and remove that one a subsequent action by your own. As dependency is merely a weak (unidirectional) relation that orm cannot automatically remove.
See here for documentation about EDL-dependency: https://support.intershop.com/kb/index.php/Display/247P28
For example, I checked ProcessPagelet-Delete pipline. In there we first unassign (i.e. remove the assignment) Label objects from the Pagelet to be deleted. The PageletLabelAssingmentPO contains a dependency to Pagelet as you can see here:
orm class PageletLabelAssignmentPO extends LabelAssignmentPO
{
attribute pageletUUID : uuid;
dependency pagelet : PageletPO
{
foreign key(pageletUUID);
}
}

Fluent NHibernate: foreign key field not being set in unidirectional association

I have a database with a ProbateCases table and a Properties table. The Properties table has a foreign key to the ProbateCases table called ProbateCaseId, so the relationship between ProbateCases and Properties is one-to-many.
My domain layer has a ProbateCase class and a Property class. The ProbateCase class has a collection of Properties defined as follows:
private IList<Property> _properties = new List<Property>();
public virtual IEnumerable<Property> Properties { get { return _properties; } }
public virtual Property AddProperty()
{
Property property = new Property();
_properties.Add(property);
return property;
}
The corresponding part of the Fluent NHibernate mapping looks like this:
HasMany(x => x.Properties).Where("Deleted = 0").KeyColumn("ProbateCaseId").Cascade.All().Access.CamelCaseField(Prefix.Underscore);
Note that the association is unidirectional - the ProbateCase class has a collection of Properties, but the Property class does not have a ProbateCase member.
I'm finding that querying works fine - NHibernate is creating the appropriate SQL to get Properties with the appropriate ProbateCaseId value.
However, when I save a ProbateCase to which I have added a new Property, the INSERT SQL does NOT contain a value for the foreign key field - so I get a SQL Exception complaining of a NULL value in the foreign key:
INSERT INTO AdminOverview.Properties (PropertyName) VALUES ('Name of property') -- Where the hell is the ProbateCaseId field value???
Should I be expecting NHibernate to populate the foreign key value itself, or is there something else I should be doing?
From http://nhibernate.info/doc/nh/en/index.html#collections-onetomany:
Very Important Note: If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true". See the discussion of bidirectional associations later in this chapter.

Fluent NHibernate: Foreign Key violation or Null values

Hey guys, I am having some real issues with mapping using fluent nhibernate. I realise there are MANY posts both on this site and many others focusing on specific types of mapping but as of yet, I have not found a solution that solves my issue.
Here is what I have:
namespace MyProject.Models.Entites
{
public class Project
{
public virtual Guid Id {get; set;}
// A load of other properties
public virtual ProjectCatagory Catagory{get;set;}
}
}
and then the map:
namespace MyProject.DataAccess.ClassMappings
{
public class ProjectMap : ClassMap<Project>
{
public ProjectMap()
{
Id(x => x.Id);
Map(x => x.Title);
Map(x => x.Description);
Map(x => x.LastUpdated);
Map(x => x.ImageData).CustomSqlType("image");
HasOne(x => x.Catagory);
}
}
}
So as you can see, I have a project which contains a catagory property. Im not so hot on relational databases but from what I can figure, this is a many-one relationship where many Projects can have one catagory. No, projects cannot fall into more than one category.
So now we have:
namespace MyProject.Models.Entities
{
public class ProjectCatagory
{
public virtual Guid Id { get; set; }
public virtual String Name { get; set; }
}
}
and its map:
public ProjectCatagoryMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
Issue is, well, it doesn't work ! I will do something similar to the following in a unit test:
Project myproject = new Project("Project Description");
// set the other properties
myProject.Catagory = new ProjectCatagory(Guid.New(), "Test Catagory");
repository.Save(myProject);
Now I have tried a number of mapping and database configurations when trying to get this to work. Currently, the Project database table has a column, "Catagory_id" (which i didnt put there, i assume NH added it as a result of the mapping) and I would LIKE it set to not allow nulls. However, when set as such, I get exceptions explaining that I cannot insert null values into the table (even though during a debug, i have checked all the properties on the Project object and they are NOT null).
Alternatively, I can allow the table to accept nulls into that column and it will simply save the Project object and totally disregard the Category property when saving, therefore, when being retrieved, tests to check if the right category has been associated with the project fails.
If i remember correctly, at one point I had the ProjectMap use:
References(x => x.Catagory).Column("Catagory_id").Cascade.All().Not.Nullable();
this changed the exception from "Cannot insert null values" to a foreign key violation.
I suspect the root of all this hassle comes from my lack of understanding of relational database set up as I have other entities in this project that do not have external dependencies which work absolutely fine with NHibernate, ruling out any coding issues I may of caused when creating the repository.
Any help greatly appreciated. Thank you.
The main issue here is a common misunderstand about the "one-to-one" relation in a relational database and the HasOne mapping in Fluent. The terms in the mapping are relational terms. (Fluent tries to "beautify" them a bit which makes it worse IMO. HasOne actually means: one-to-one.)
Take a look at the Fluent wiki:
HasOne is usually reserved for a
special case. Generally, you'd use a
References relationship in most
situations (see: I think you mean a
many-to-one).
The solution is very simple, just exchange HasOne with References (one-to-one to many-to-one in an XML mapping file). You get a foreign key in the database which references the ProjectCatagory.
A real one-to-one relation in a relational database is ideally mapped by a primary key synchronization. When two objects share the same primary key, then you don't waste space for additional foreign keys and it is ensured to be one-to-one.
To synchronize primary key, you need to hook up one's key to the others. However this works, it is not what you need here.
After playing around with all the available options for mapping. I found the answer to be similar to that suggested.
As was suspected, HasOne() was clearly wrong and References(x => x.Catagory) was part of the solution. However, I still received foreign key violation exceptions until:
References(x => x.Catagory).Column("Catagory_id").Cascade.SaveUpdate().Not.Nullable().Not.LazyLoad();
Just thought id update the thread in case someone else stumbles across this with a similar issue as just using References() did not work.
Its seems ProjectCatagory class is parent class of Project Class. So without parent class how can child class exist.
You have to use -
References(x => x.Catagory).Column("Catagory_id").Foreignkey("Id");
here Foreign Key is your ProjectCatagory table ID.

Fluent NHibnernate HasManyToMany with Index

I'm trying to map a many-to-many collection with Fluent NHibnernate. My model class has this property:
public virtual IList<Resource> Screenshots
{
get { return _screenshots; }
protected set { _screenshots = value; }
}
And my fluent mapping is:
HasManyToMany(x => x.Screenshots)
.AsList(x => x.WithColumn("Index"))
.Cascade.AllDeleteOrphan();
When I run my application, I get the following exception message:
The element 'list' in namespace
'urn:nhibernate-mapping-2.2' has
invalid child element 'many-to-many'
in namespace
'urn:nhibernate-mapping-2.2'. List of
possible elements expected: 'index,
list-index' in namespace
'urn:nhibernate-mapping-2.2'.
There should be a way to do this. Does anyone know what I am doing wrong?
The current FluentNHibernate syntax for this is as follows:
HasManyToMany(x => x.Screenshots)
.AsList(i => i.Column("`Index`"));
The index column defaults to Index, but that's a reserved word on SQL Server (and probably other databases too), so you must quote it with back-ticks.
Also, I'd recommend against setting a cascade on this relationship. Consider the following code:
x.Screenshots.Remove(s);
session.SaveOrUpdate(x);
NHibernate will correctly delete rows from the linking table even without a cascade specified. However, if you specify AllDeleteOrphan, then NHibernate will delete the row from the linking table and also delete Resource s. I doubt this is the behavior you want on a many-to-many relationship.

Fluent Nhibernate composed entity, specify parent key

In this question I was answered hot to map a composed entity from the primary key of the table.
So given:
public UserMap()
{
WithTable("aspnet_Users");
Id(x => x.Id, "UserId")
.GeneratedBy.Guid();
Map(x => x.Name, "UserName");
Map(x => x.Login, "LoweredUserName");
WithTable("LdapUsers", m => {
m.Map(x => x.FullName, "FullName");
m.WithKeyColumn("UserId");
});
}
everithing works if in the "LdapUser" and in the "aspnet_Users" there is a column named "UserId".
What If I want to specify both the colum name for the foreign key table and the column name for the key from the main table as this is not the pk so use another column to do the join?
It looks like this is not currently supported at all in NHibernate. There is an open issue for it on NHibernate's JIRA. Actually, I think this is for collections but probably applies for what you're doing as well. The idea is you would add a property-ref attribute on your <key> element that points to the property you want to reference instead of the primary key.
Supposedly the change is already available in the Alpha2 release of NHibernate 2.1.0. Of course, it might be sometime after NH2.1 is a general release before this gets added to FNH. If you can, I would recommend getting a hold of the Alpha2 release and trying it with your situation. If it doesn't work for your composed entity, only collections, you might want to submit another issue on JIRA so it gets added as well.