NHibernate: Overriding automapping on a one to many relationship - fluent-nhibernate

I've just started to play around with Fluent NHibernate and would like some help / pointers on how I got about mapping this. Here's the domain model I'm working with:
Class User: Your standard user class
Class Preference: A set of key value pairs. A user can have many preferences.
Class Post: Omitted complete description for the sake of brevity, but a user creates a post and it can contain one or more preferences.
So, to recap:
class User
{
IList<Preference> Preferences;
IList<Post> Posts;
}
class Post
{
IList<Preference> PostData;
}
Hibernate automapper comes up with the following DB Structure:
User
------------------------
User_Id
Preference
------------------------
Preference_Id | User_Id | Post_Id
Post
------------------------
Post_Id | User_Id
The preference table looks ugly to me and would much rather have something like:
User
------------------------
User_Id
Preference
------------------------
Preference_Id | User_Id
Post
------------------------
Post_Id | User_Id | Preference_Id
Any help on how to do this is much appreciated! I already have a class that implements the IAutoMappingOverride interface, but I'm not sure how to do the actual mapping.
Thanks,
Teja

I would reference this configuration to get you manual and auto mappings configured correctly:
Mixing Mixing Automapping and manual mapping
I also noticed you have some composite keys in both your preference and post tables I would look into learning how to map composite keys. Here is a link that might help you with mapping composite keys(primary key with more than one column):
Using Fluent NHibernate to map Composite Keys
As far as your fluent maps go the following might get you pointed in the right direction you can map one-to-many relationships using both HasMany or References depending on which table you want the foreign key on:
public class UserMap : ClassMap<User>
{
public UserMap(){
Id(x => x.Id).Column("User_Id").GeneratedBy.Identity();
HasMany(x => x.Preferences);
}
}
public class PostMap: ClassMap<Post>
{
public UserMap(){
Id(x => x.Id).Column("Post_Id").GeneratedBy.Identity();
References(x => x.Preferences);
}
}

since the preferences are the same a simple property is enough
class Post
{
public virtual User User { get; set; }
public virtual IList<Preference> Preferences { get { return User.Preferences; } }
}

Related

How to implement Entity Framework if my tables have unconventional column names?

I've joined a team that uses non standard names for tables and columns, and have trouble building database-first projects with Entity Framework.
Here's my problem:
tFWAClientProcessing (Table)
FWAClientHandling (Primary Key, INT)
iClientID (Foreign Key, INT)
.
tClients (Table)
AClientID (Primary Key, INT)
sClientName (VARCHAR(255))
I need Entity Framework to detect the relationship between these two tables without making changes to those tables in production.
I'd long given up on EDMX and convention-based mapping for relationships and just set up EF via EntityConfiguration classes. Attributes in the entity definitions are another option which should work for simple cases like identifying column names. You can also wire up mapping in the OnModelCreating override directly.
For instance: To have entities called Client and FWAClientProcessing for that table structure:
public class Client
{
public int ClientId { get; set; }
public string ClientName { get; set; }
}
public class FWAClientProcessing
{
public int FWAClientProcessingId { get; set; }
public virtual Client Client { get; set; }
}
public class ClientConfiguration : EntityTypeConfiguration<Client>
{
public ClientConfiguration()
{
ToTable("tClients"); // assumes default schema, i.e. "dbo" in SQL Server. Can add schema name as 2nd parameter otherwise.
HasKey(x => x.ClientId)
.Property(x => x.ClientId)
.HasColumnName("iClientID");
Property(x => x.ClientName)
.HasColumnName("sClientName");
}
}
public class FWAClientProcessingConfiguration : EntityTypeConfiguration<FWAClientPrcessing>
{
public FWAClientProcessingConfiguration()
{
ToTable("tFWAClientProcessing");
HasKey(x => x.FWAClientProcessingId)
.Property(x => x.FWAClientProcessingId)
.HasColumnName("FWAClientHandling");
HasRequired(x => x.Client)
.WithMany()
.Map(x => x.MapKey("iClientID"));
}
}
Assuming that the EntityTypeConfiguration classes are in the same assembly as the entities, and the DBContext, registering them in the context becomes:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.AddFromAssembly(TypeOf(YourDbContex).Assembly);
base.OnModelCreating(modelBuilder);
}
These examples are for EF6, EF Core uses the concept of Shadow Properties for mapping FK relationships without exposing FK properties, and can accommodate the different column naming. EntityTypeConfiguration is available as an Interface with a Configure method accepting the builder.
I favor using the explicit entity type configuration by default as it keeps the configuration nicely isolated and out of the way and can handle all mapping scenarios that might come up that annotations cannot do. It's a bit of a one-off cost to set up, but at least then you have full visibility and control over how the schema is mapped and not simply hoping EF works things out. :)
Use the modern replacement for EDMX-based Database-First and reverse-engineer a code-first model from the existing database. Customizing an EDMX-based model with its mappings is a rabbit-hole of obsolete technology.
This is available for EF Core and EF6.
The reverse-engineered model is then a starting point for you to make model customizations, like mapping the tables and columns to sensible names, and configuring any Navigation Properties that for whatever reason didn't get picked up by the tooling.
You are right, it is easier if people follow the entity framework conventions. However, if you have to deviate from them, OnModelCreating is your friend.
In OnModelCreating, from every Table, column, relation between tables, that are not standard, you can inform entity framework about these deviations.
You can give different table names
You can use other column names
You can say that certain properties should be saved in certain database formats, for instance ProductPrice is a decimal with 2 digits after the decimal point, instead of the default number of digits.
etc.
There seems to be a one-to-many relation between Clients and ClientsProcessing: every Client with primary key Id, has zero or more ClientsProcessings, every ClientProcessing belongs to exactly one Client, namely the Client that the foreign key ClientId refers to.
You want to use unconventional table names, unconventional names for you primary and foreign keys, and you need to inform about what keys are used to define the one-to-many relation.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Configure DbSet<Client>:
ver clients = modelBuilder.Entity<Client>();
clients.ToTable("tClients")
.HasKey(client => client.Id)
// property Id should be in "AClientID"
clients.Property(client => client.Id).HasColumnName("AClientID");
clients.Property(client => client.Name).HasColumnName("sClientName");
Apart from different names of the columns, you can also declare whether the properties are required or optional, what format they should have (is a decimal with two digits after the decimal point, or does it have four digits?), etc.
Do something similar for modelBuilder.Entity<ClientProcessing>();
For the one-to-many relation: every Client has zero or more ClientProcessings; every ClientProcessing belongs to exactly one (required!) Client, namely the foreign key that ClientId refers to:
clients.HasMany(client => client.ClientProcessings)
.WithRequired(clientProcessing => clientProcessing.Client)
.HasForeignKey(clientProcessing => clientProcessing.ClientId);
Or if you want, you can start at ClienProcessing: every ClientProcessing has exactly one Client (required!), using foreign key ClientId. Every Client has many ClientProcessings.
modelBuilder.Entity<ClientProcessing>()
.HasRequired(clientProcessing => clientProcessing.Client)
.WithMany()
.HasForeignKey(clientProcessing => clientProcessing.ClientId);
Note: by default this will cascade on delete: whenever you delete a client, you will also delete all its processings: you did define there are no processings without a client.
In some relations, you don't want this, especially many-to-many relations or one-to-zero-or-one relation: a Student may have zero or one School-supplied-Laptop. If you delete the Laptop, you don't want to delete the Student as well. In that case you'll have to add .WillCascadeOnDelete(false)

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.

Nhibernate entity with multiple Many-To-Many lists of the same type?

Does anybody know how I would map an entity with two many-to-many collections of the same child type.
My database structure is this....
The "normal" relationship will be....
tbl_Parent
col_Parent_ID
tbl_Parent_Child_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The alternative relationship is...
tbl_Parent
col_Parent_ID
tbl_Include_ParentChild_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The entity and mapping look like this...
public partial class ParentEntity : AuditableDataEntity<ParentEntity>
{
public virtual IList<ChildEntity> Children { get; set; }
public virtual IList<ChildEntity> IncludedChildren { get; set; }
}
public partial class ParentMap : IAutoMappingOverride<ParentEntity>
{
public void Override(AutoMapping<ParentEntity> mapping)
{
mapping.Table("tbl_Parent");
mapping.HasManyToMany(x => x.Children)
.Table("tbl_Parent_Child_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
mapping.HasManyToMany(x => x.IncludedChildren)
.Table("tbl_Include_ParentChild_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
}
}
The error that I'm getting is
"System.NotSupportedException: Can't figure out what the other side of the many-to-many property 'Children' should be."
I'm using NHibernate 2.1.2, FluentNhibernate 1.0.
It seems FNH is confused because you seem to map the same object (ChildEntity) to two different tables, if I'm not mistaken.
If you don't really need the two lists to get separated, perhaps using a discriminating value for each of your lists would solve the problem. Your first ChildEntity list would bind to the discriminationg value A, and you sesond to the discriminating value B, for instance.
Otherwise, I would perhaps opt for a derived class of your ChildEntity, just not to have the same name of ChildEntity.
IList<ChildEntity> ChildEntities
IList<IncludedChildEntity> IncludedChildEntities
And both your objects classes would be identitical.
If you say it works with NH, then it might be a bug as already stated. However, you may mix both XML mappings and AutoMapping with FNH. So, if it does work in NH, this would perhaps be my preference. But think this workaround should do it.
You know I'm just shooting in the dark here, but it almost sounds like your ChildEntity class isn't known by Hibernate .. that's typically where I've seen that sort of message. Hibernate inspects your class and sees this referenced class (ChildEntity in this case) that id doesn't know about.
Maybe you've moved on and found the issue at this point, but thought I'd see anyway.
Fluent is confused because you are referencing the same parent column twice. That is a no-no. And as far as I can tell from the activity i have seen, a fix is not coming any time soon.
You would have to write some custom extensions to get that working, if it is possible.
To my great pity, NHibernate cannot do that. Consider using another ORM.

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.