I have a design similar to this one (although a teeny-weeny bit more complicated):
This is a many-to-many relationship. A scan can own many located pages, but a located page might belong to many scans.
The trouble I have is I don't know how to add a unique constraint on URL, so that when a page from a scan is being saved and an entry with such a URL already exists, it should not create a new one. Instead, the new scan_locatedpage entry should link to the already existing locatedpage and (if different) update its checksum.
Only, I'm not sure how to do that.
Currently the code I have looks this way:
public LocatedPageMap()
{
Id(x => x.Id);
Map(x => x.Url)
.Not.Nullable();
Map(x => x.Checksum);
HasManyToMany(x => x.ScansFoundWith)
.Cascade.All()
.Inverse()
.Table("Scan_LocatedPage");
}
public ScanMap()
{
Id(x => x.Id);
Map(x => x.Date)
.Not.Nullable();
HasManyToMany(x => x.Located_Pages)
.Cascade.SaveUpdate()
.Table("Scan_LocatedPage");
}
Of course, this doesn't work. Every time a new Scan is being saved, a new LocatedPage gets added, despite the Url.
I have no idea how to achieve that effect and I'm not really sure where/how to look. I have read a bunch of many-to-many questions with "unique" in title, but haven't found anything useful. Surely 'cos I'm not sure what to look for exactly.
EDIT
Or maybe I should simply add the condition handler into my application logic, and not expect Fluent to take care of that for me?
This kind of a problem belongs to upper layer than mapping. What you can/should do is to search for existing object
LocatedPage
with the provided url. If found - use it, if not - create new. We are doing that stuff on upper layers (Service, Business) during the incoming data binding.There are few points I wanted you to be aware of:
1) Chapter 24. Best Practices says:
Please, think about it. We are not using many-to-many at all. Having the middle object as a full entity could bring lot of advantages (e.g. searching based on subqueries)
2) Cascade
Be careful when using the Cascade on many-to-many. This is not a setting for a middle/pairing table. It is setting for the second-end Entity. So Once your
LocatedPageMap
is deleted all theScansFoundWith
items (Scan
) will be deleted as well. Usually ... cascade is not what you want to set on many-to-many. The pairing table is "cascaded always"