NHibernate self-join many-to-many symmetric relationship (person friends problem) - nhibernate

Is there any way to set-up a symmetric self-join relationship mapping in NHibernate? Suppose we have two tables:
Users
id
Relations
id
user1
user2
relation_type
The User and Relation classes should look like this:
class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<Relation> Relations { get; set; }
}
class Relation
{
public virtual int Id { get; set; }
public virtual User User1 { get; set; }
public virtual User User2 { get; set; }
// Let's leave the RealationType as string for the sake of brevity
public virtual string RelationType { get; set; }
}
I do NOT want the relations table to have two rows for the same relation. But the relation MUST be symmetric, which means if there's a relation between two users, A and B, the Relations collection of the user A must contain a relation with user B and the relations of user B must contain a relation to A.
It sounds almost like a challenge. But, can someone solve this? Please, if you can, post the xml mapping. I'm not using Fluent.

You can use Key-Many-To-One mapping and remove the Id field from the relation entity. Also you better use inheritance for different relation types.

I doubt it. If you think about the manual SQL query you'd need to write to pull a User & all his Relations out in an outer join query, you can see why NHibernate would struggle to generate something like this. Updates would be an even bigger headache - how do you decide which ids go in which field for a new Relation?
If you're stuck on this model, all I can suggest as a workaround is to map two private collections and implement a Union()ed read-only public collection. Implement update/remove methods that locate & modify the appropriate relation, and a round-robin Add() method. You won't have any NHibernate query support for queries on this collection.
Your other option is to change your data model so that User has a many-to-many relationship to Relation (eg a UserRelation table), rely on application code to enforce a 'two users per relation' rule, and add convenience methods like IList<User> GetRelations(RelationType)

Related

How to use NHibernate with entity spread over multiple tables?

I have pre-existing tables, using a kind of open schema. I have an Item table, and various entities are classified as Items, and then have properties stored in Item property tables. A single entity type may have fields stored in multiple tables. We expose entities with views. So, most entities correspond to a view, and then when we insert/update we have to systematically update the tables or use stored procedures.
I'm trying to determine if NHibernate will gain us anything over our custom-built repositories (which follow a factory pattern). Right now, I'm seeing great difficulty in getting NHibernate to deal with this kind of database schema. The way I see it, we'd either have to completely refactor our database to follow NHibernate's conventions, or completely refactor or entities somehow.
I'm not seeing much in the documentation about how to do this, except for the very simplest of examples that involve databases that more or less follow NHibernate's conventions.
Here's a representative database diagram. We have Episode as an entity that pulls info from Item, IP_Episode, IP_EpisodeBroadcastInfo, IP_Show, etc. to build all the fields that it needs.
You mention conventions. That is a Fluent NHibernate concept, and yes, what you are doing is not exactly in line with Fluent NHibernate's existing conventions. However, it is well within NHibernate's capabilities. NHibernate excels at being able to be mapped to all sorts of different database schemas. Don't feel constrained to the way Fluent NHibernate wants you to go. I'm not saying don't use Fluent NHibernate. If you are consistent and reasonable in your database schema, you can write your own conventions to match.
To illustate NHibernate's flexibility, let's assume we have a table structure similar to this:
create table Episode (
Id int not null primary key,
NumberInSeries int null
);
create table Show (
Episode_id int not null primary key,
Title nvarchar(100) not null,
foreign key (Episode_id) references Episode (Id)
);
create table Broadcast (
Episode_id int not null primary key,
InitialAirDate datetime not null,
foreign key (Episode_id) references Episode (Id)
);
One row in Episode corresponds to zero or one rows in Show and zero or one rows in Broadcast. You could model this type of relationship several different ways in .NET. Here are the various options available to you via NHibernate:
1. Inheritance
public class Episode
{
public virtual int Id { get; set; }
public virtual int? NumberInSeries { get; set; }
}
public class Show : Episode
{
public virtual string Title { get; set; }
}
public class Broadcast : Episode
{
public virtual DateTime InitialAirDate { get; set; }
}
Use this when you want to model a relationship that does not change. If an Episode is a Show, it is always a Show. Also, this modeling would imply that an Episode cannot be both a Show and a Broadcast. I don't believe this is what you want, but you may find it useful elsewhere in your model.
For more info, see...
Official documentation on inheritance mapping
Ayende's blog post on inheritance mapping
2. one-to-one
public class Episode
{
public virtual int Id { get; set; }
public virtual int? NumberInSeries { get; set; }
public virtual Show Show { get; set; }
public virtual Broadcast Broadcast { get; set; }
}
public class Show
{
public virtual Episode Episode { get; set; }
public virtual string Title { get; set; }
}
public class Broadcast
{
public virtual Episode Episode { get; set; }
public virtual DateTime InitialAirDate { get; set; }
}
This gives you more control over which tables actually contain a row associated with a given Episode, because you can set episode.Broadcast = null for example. It's also fine to have both Show and Broadcast information for a given Episode.
For more info, see...
Official documentation on one-to-one
Ayende's blog post on one-to-one
3. join
public class Episode
{
// These properties come from the Episode table...
public virtual int Id { get; set; }
public virtual int? NumberInSeries { get; set; }
// This one comes from the Show table.
public virtual string Title { get; set; }
// This one comes from the Broadcast table.
public virtual DateTime InitialAirDate { get; set; }
}
This is a nice and simple way to represent the data, but you do not get control over whether on not rows are inserted into the Show and Broadcast tables or not.
For more info, see...
Official documentation on join
Ayende's blog post on join
Since you said, "A single entity type may have fields stored in multiple tables", it sounds to me like join should be able to handle the way you currently have things modeled.

Eager loading an optional one-to-one with NHibernate

Consider the following simplified domain:
public class Movie
{
public virtual int Id { get; set; }
public virtual MovieDetail MovieDetail { get; set; }
}
public class MovieDetail
{
public virtual int Id { get; set; }
public virtual Movie Movie { get; set; }
}
A MovieDetail cannot exist without a Movie, but a Movie could exist without a MovieDetail (i.e. we have no details about it).
Our database has a separate table for Movie with columns Id, and a separate table for MovieDetail with columns Id and MovieId. There is also a foreign key from MovieDetail.MovieId to Movie.Id.
We've got this all mapped in NHibernate, but when getting a collection of Movie instances, we want a left outer join with MovieDetail. If not, we could have a N+1 problem when iterating over the Movie instances. That is the case now: there is a separate query for every call to the Movie.MovieDetail property.
I've tried one-to-one mapping, but that seems to be for the case when you have both instances. In our case, we don't always have a MovieDetail. Also, they don't share the same primary key.
I've researched formula's, but that would require me to make my MovieDetail implement IUserType, essentially putting NHibernate into my domain. I'd like to avoid that.
Maybe you could try adding a many-to-one relation in the Movie mapping to MovieDetail, it will act as a one to one mapping.
When you set the option 'not-null' to "false" it is also nullable I suppose.
I don't know if you are lazy loading or not, when this is so the MovieDetailis loaded when needed and not by a left join construction.
Shouldn't all the properties be virtual in both classes?
<many-to-one name="MovieDetail" column="Id" class="MovieDetail" not-null="false" lazy="false"/>
I'm in a bit of a hurry and I don't know if you can modify your domain / db schema but you might want to try and take a look at http://ayende.com/blog/3937/nhibernate-mapping-component.
It seems to me that a Movie can have at most one MovieDetail which might not be there. MovieDetail might have properties like Description, ReleaseDate, Actors, etc. I don't really understand why you separated these concepts. By bringing them together you would have 1 less table and 1 less FK to join on each time you want to list movies.
The component allows you to isolate your data into a separate entity while mapping to the same table as Movie.

NHibernate Attributes Mapping List

I'm a new NHibernate developer. I'm using attributes and not map files and I have configured the application to create the tables automatically.
I Have two classes , Group and User.
Withing the Group class I have a list of users
public class Group
{
[NHibernate.Mapping.Attributes.Id(Name = "GroupId")]
[NHibernate.Mapping.Attributes.Generator(Class = "guid")]
public virtual Guid GroupId { get; set; }
// What Attributes do I place here
public virtual List<User> Users { get; set; }
}
I can't find the right attributes so that there will be two tables that have one to many relation.
Can anyone help?
Thanks,
Ronny
[ManyToMany], [OneToMany] or [ManyToOne] (those linked docs are fairly useless though) depending on how you want it setup. Probably [OneToMany], and then the same on a User.
You could avoid the pain by using the Fluent NHibernate library instead, if you haven't already tried it.

Joins in fluent nhibernate

I am using fluent nhibernate.
example:
i have 3 tables i.e.
CUSTOMER
CustomerId pk
CustomerName
PRODUCT
ProductId pk
ProductName
Cust_Product
cust_prodId pk
ProductId fk
CustomerId fk
Now, I want to show customername, productnae
so, how do i write mapping class for the same.
i want to use
session.CreateCriteria(typeof("className")).List()
like this. how do i do this..?
If you're looking for a full tutorial on how to do this, I recommend the FNH wiki or one of the many blog postings which can be found through Google.
However, you're trying to implement a many-to-many relationship here, and that seems to throw a lot of people off. Here's a rough guide:
On your Customer class, you'll need a collection like:
IList<Product> Products { get; private set; }
And similarly, on your Product class:
IList<Customers> Customers { get; private set; }
You start off a many-to-many map with the HasManyToMany function:
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
// other mappings
HasManyToMany<Product>(x => x.Products)
.WithTableName("Cust_Product") // Specifies the join table name
.WithParentKeyColumn("CustomerId") // Specifies the key joining back to this table (defaults to [class]_id, Customer_id in this case)
.WithChildKeyColumn("ProductId")
.FetchType.Join(); // Instructs NHibernate to use a join instead of sequential select
}
}
Then repeat the process for the other side of the relationship (the Customers property on the Product class).

Fluent NHibernate Architecture Question

I have a question that I may be over thinking at this point but here goes...
I have 2 classes Users and Groups. Users and groups have a many to many relationship and I was thinking that the join table group_users I wanted to have an IsAuthorized property (because some groups are private -- users will need authorization).
Would you recommend creating a class for the join table as well as the User and Groups table? Currently my classes look like this.
public class Groups
{
public Groups()
{
members = new List<Person>();
}
...
public virtual IList<Person> members { get; set; }
}
public class User
{
public User()
{
groups = new Groups()
}
...
public virtual IList<Groups> groups{ get; set; }
}
My mapping is like the following in both classes (I'm only showing the one in the users mapping but they are very similar):
HasManyToMany<Groups>(x => x.Groups)
.WithTableName("GroupMembers")
.WithParentKeyColumn("UserID")
.WithChildKeyColumn("GroupID")
.Cascade.SaveUpdate();
Should I write a class for the join table that looks like this?
public class GroupMembers
{
public virtual string GroupID { get; set; }
public virtual string PersonID { get; set; }
public virtual bool WaitingForAccept { get; set; }
}
I would really like to be able to adjust the group membership status and I guess I'm trying to think of the best way to go about this.
I generally only like to create classes that represent actual business entities. In this case I don't think 'groupmembers' represents anything of value in your code. To me the ORM should map the database to your business objects. This means that your classes don't have to exactly mirror the database layout.
Also I suspect that by implementing GroupMembers, you will end up with some nasty collections in both your user and group classes. I.E. the group class will have the list of users and also a list of groupmembers which references a user and vice versa for the user class. To me this isn't that clean and will make it harder to maintain and propagate changes to the tables.
I would suggest keeping the join table in the database as you have suggested, and add a List of groups called waitingtoaccept in users and (if it makes sense too) add List of users called waitingtoaccept in groups.
These would then pull their values from your join-table in the database based on the waitingtoaccept flag.
Yes, sure you need another class like UserGroupBridge. Another good side-effect is that you can modify user membership and group members without loading potentially heavy User/Group objects to NHibernate session.
Cheers.