How to create NHibernate HasManyToMany relation - c#

I know there are questions about HasManyToMany but this time I want to put couple fields into middle table like 'Description, CreationDate'.
For my situation I don't want to bind two way. I have company, person and address tables.
And every company or person may have more than 1 address.
In this situation what should I do?
How should I write the code of classes and mappings?
Below you can see the tables:

In this case the answer is pretty simple. Do not use many-to-many. Use pairing object. Exactly for the reasons you've mentioned: Extend the pairing object with more properties:
Check here 24. Best Practices, a cite:
Don't use exotic association mappings.
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.
Other words, create the one-to-many relations refering the pairing objects from boht ends, and many-to-one from the pairing object.
Also check these:
Hibernate: many-to-many relationship table as entity
NHibernate Bidirectional Many-to-Many Mapping List / Bag
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
An example of the Address and Company. First Pairing object
public class AddressCompany
{
// the relation to both sides
public virtual Address Address { get; set; }
public virtual Company Company { get; set; }
// many other settings we need
public virtual string Description { get; set; }
public virtual DateTime CreationDate { get; set; }
...
}
the Address and Company in a nut-shell:
public class Address
{
public virtual IList<AddressCompany> Companies { get; set; }
...
}
public class Company
{
public virtual IList<AddressCompany> Addresses { get; set; }
...
}
The mapping is as expected:
public AddressMap()
{
HasMany(x => x.Companies)
...
}
public CompanyMap()
{
HasMany(x => x.Addresses)
...
}
public AddressCompanyMap()
{
References(x => x.Address)..
References(x => x.Company)..
...
}
So, this is representing the Pairing object
Well, but now we can find some Companies Created after a date:
var subquery = QueryOver.Of<AddressCompany>()
.Where(c => c.CreationDate > new DateTime(2000, 1, 1))
.Select(c => c.Company.ID);
var query = session.QueryOver<Company>()
.WithSubquery
.WhereProperty(c => c.ID)
.In(subquery)
...;
This way we can also filter Company over the Address...

Related

Configure Many-To-Many EF Relationship Between Separate Aggregates Without Using Navigation Lists

I have two entities from separate aggregate groups that have a many-to-many relationship:
public class ClientNotificationMethod
{
public int ID { get; private set; }
private ICollection<int> _personIDs;
//public virtual ICollection<Person> _contactPersonnel { get; set; } //AVOID THIS
}
The ClientNotificationMethod has a collection of Person IDs, and the Person has a collection of ClientNotificationMethod IDs:
public class Person
{
public int ID { get; private set; }
private ICollection<int> _clientNotificationMethodIDs;
//public virtual ICollection<ClientNotificationMethod> _clientNotificationMethods { get; set; } //AVOID THIS
}
From both classes, I commented-out the public virtual ICollection navigation properties, because I'm trying to follow the DDD principle of avoiding overlap between aggregate groups. I also want to prevent eager/lazy-loading lists of entire class objects (I only want the IDs).
When a Person is added/removed from a ClientNotificationMethod, I want EF to update the ICollection<int> properties in each class.
I haven't found any examples of this in my searches. So far, it seems that EF requires navigation properties in both classes to configure a many-to-many relationship. From my understanding of DDD, navigation properties should only be used in this way when the many-to-many relationship is between two objects in the same aggregate group.
Is the many-to-many relationship that I'm aiming for configurable in EF using fluent API?
At one point, I tried configuring a HasMany-WithMany relationship like this:
private void ConfigureClientNotificationMethod(EntityTypeBuilder<ClientNotificationMethod> builder)
{
builder.ToTable("ClientNotificationMethod");
builder.HasKey("ID");
builder.Property("ID")
.IsRequired();
builder.HasMany<Person>().WithMany().UsingEntity<Dictionary<string, object>>("ClientNotificationMethodPersons",
c => c.HasOne<Person>().WithMany(),
c => c.HasOne<ClientNotificationMethod>().WithMany());
builder.Property("_personIDs").HasConversion(new JsonValueConverter<ICollection<int>>())
.HasColumnName("PersonIDs").IsRequired(false);
}
This created a join table, but EF couldn't populate it because I wasn't using any Person or ClientNotificationMethod navigation objects in the classes.

Should I map both sides of bidirectional relations in EF code first?

Assume I have the following entity classes:
public class Customer {
public int Id { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Order {
public int Id { get; set; }
public virtual Customer Customer { get; set; }
}
How should those be mapped in Entity Framework 6 fluent code-first mapping? I want to be explicit about the mapping and not rely on automatic mapping conventions.
Option 1
Just map the local properties of both classes. That's how I would do it in Fluent NHibernate.
public class CustomerMap : EntityTypeConfiguration<Customer> {
public CustomerMap() {
HasMany(x => x.Orders);
}
}
public class OrderMap : EntityTypeConfiguration<Order> {
public OrderMap() {
HasRequired(x => x.Customer);
}
}
Option 2
Map both sides of the relationship in both classes.
public class CustomerMap : EntityTypeConfiguration<Customer> {
public CustomerMap() {
HasMany(x => x.Orders).WithRequired(x => x.Customer);
}
}
public class OrderMap : EntityTypeConfiguration<Order> {
public OrderMap() {
HasRequired(x => x.Customer).WithMany(x => x.Orders);
}
}
Option 3
Map both sides of the relation, but only in one of the classes. The code would be similar to option 2, just one of the two constructors would be empty.
Is there any difference between those options? If yes, please also explain why I should or shouldn't use a specific option.
I would go for option 3.
In option 1 you can forget to map the inverse end of an association. In this simple example it's clear that Order.Customer and Customer.Orders are two ends of the same association. When things get more complex, this isn't always obvious. Also, it is redundant code.
In option 2 you could have conflicting mappings. For instance when you have...
HasOptional(x => x.Customer).WithMany(x => x.Orders);
...in OrderMap, you will get a runtime exception telling you that both mappings don't match. And again, it is redundant code.
So option 3 is DRY and safe. The only issue is that it's a bit arbitrary where to configure the mappings. I tend to adhere to mapping children in their parent's mapping.
One more comment. You may want to add a primitive property CustomerId in Order. The mapping would look like:
public class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
HasMany(x => x.Orders).WithRequired(x => x.Customer)
.HasForeignKey(o => o.CustomerId);
}
}
Now you have full control over both ends of the association and the foreign key name to be used. Besides that, there are some advantages of these foreign key associations as opposed to independent associations (without a primitive foreign key property). For instance, the ability to establish an association without having to fetch the parent object from the database. You can just by set an Id value.

Entity Framework Code First Relationships; what am I missing?

I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}
Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx
Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.

Mapping an Array using Fluent N Hibernate

I am not sure if fluent n hibernate can do this or not, but I cannot figure out how.
I have a table - cases and some properties
ownerId, brokerId, shipperId
I want to map this to my property:
int[] OrgsWithAccess
Is this possible?
This way when I am checking if an org has access to the case, I can check the property OrgsWithAccess rather than OwnerId == myorg.id or brokerId == myorg.id etc.
If I understand your question correctly, I wouldn't recommend trying to map in the way that you have asked.
cases table looks like it is some form of junction table between other tables. I'll assume that these other tables each contain data that are represented as entities in the application, and that there are three tables, Owner, Broker and Shipper.
OrgsWithAccess should be mapped using the references to the entities that is has in the application i.e. assume the class looks something like
public class OrgsWithAccess
{
public virtual Owner { get; set; }
public virtual Broker { get; set; }
public virtual Shipper { get; set; }
}
Then the mapping will look like
public class OrgsWithAccessMap : ClassMap<OrgsWithAccess>
{
public OrgsWithAccessMap()
{
References(x => x.Owner);
References(x => x.Broker);
References(x => x.Shipper);
}
}
Then when querying, you would simply look at the properties on OrgsWithAccess
session.QueryOver<OrgsWithAccess>().Where(x => x.Owner.Id == id);

Fluent NHibernate - Cascade delete a child object when no explicit parent->child relationship exists in the model

I've got an application that keeps track of (for the sake of an example) what drinks are available at a given restaurant. My domain model looks like:
class Restaurant {
public IEnumerable<RestaurantDrink> GetRestaurantDrinks() { ... }
//other various properties
}
class RestaurantDrink {
public Restaurant Restaurant { get; set; }
public Drink { get; set; }
public string DrinkVariety { get; set; } //"fountain drink", "bottled", etc.
//other various properties
}
class Drink {
public string Name { get; set; }
public string Manufacturer { get; set; }
//other various properties
}
My db schema is (I hope) about what you'd expect; "RestaurantDrinks" is essentially a mapping table between Restaurants and Drinks with some extra properties (like "DrinkVariety" tacked on).
Using Fluent NHibernate to set up mappings, I've set up a "HasMany" relationship from Restaurants to RestaurantDrinks that causes the latter to be deleted when its parent Restaurant is deleted.
My question is, given that "Drink" does not have any property on it that explicitly references RestaurantDrinks (the relationship only exists in the underlying database), can I set up a mapping that will cause RestaurantDrinks to be deleted if their associated Drink is deleted?
Update: I've been trying to set up the mapping from the "RestaurantDrink" end of things using
References(x => x.Drink)
.Column("DrinkId")
.Cascade.All();
But this doesn't seem to work (I still get an FK violation when deleting a Drink).
The answers to this question suggest that what I want to do isn't possible: how to define an inverse cascade delete on a many-to-one mapping in hibernate

Categories

Resources