1

I have a design similar to this one (although a teeny-weeny bit more complicated):

MySQL layout design

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?

moskalak
  • 271
  • 2
  • 12

1 Answers1

3

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:

Good usecases for a real many-to-many associations are rare. Most of the time you need additional information stored in the "link table". In this case, it is much better to use two one-to-many associations to an intermediate link class. In fact, we think that most associations are one-to-many and many-to-one, you should be careful when using any other association style and ask yourself if it is really neccessary.

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 the ScansFoundWith 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"

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • This is the thrid version of the comment and I find it really unintuitive, that the "Enter" key is used to submit. :] Back to the topic: you're right, a one-to-many and many-to-one relationship will probably be a better solution here. Would it be possible for you to show me a simple example how such a conversion would look like? I'm not sure if I got it right. :) Furthermore, I'm just a little unsure what kind of data should I store in the Link Entity. Should those be "linkage-specific" or just those it would be great to have access to without querying the other entity's table? :) – moskalak Nov 30 '13 at 11:10
  • 1
    Please, try to read this: http://stackoverflow.com/q/15510748/1679310, here I tried to explain that. Or the similar here http://stackoverflow.com/q/19687006/1679310. The biggest advantage is the simplified handling. You can do subqueries see example here (http://stackoverflow.com/q/18363386/1679310). So you can later find some `Scan` or `LocatedPage` by querying the pairing object... – Radim Köhler Nov 30 '13 at 11:18