Fluent NHibernate join on multiple fields issue
Question
Im currently researching how to port the Data Access Layer of an existing .NET 4.0 MVC 3 web application over to an entity framework. There are many reasons, but the primary one being due to thousands of stored procedures, adding just 1 field to a table results in 30 - 50 sproc edits!!
We are using MS SQL Server 2008 R2 and, ideally, we would like to use NHibernate and Fluent for mapping.
I have simplified the problem Im having into a simple example:
Imagine the following 2 tables:
'Products' Table
ID (INT)
DefaultName (NVARCHAR(128))
'Product Names' Table
ProductID (INT)
Name (NVARCHAR(128))
Culture (VARCHAR(10))
The Products table will contain a list of products, each of them will have a default, English, name. The Product Names table will contain the ID of the Product and many translations.
Currently, using stored procedures, we have the following:
SELECT Products.ID,
ISNULL(ProductNames.Name, Products.DefaultName) AS Name
FROM Products
LEFT JOIN ProductNames ON ProductNames.ProductID = Products.ID AND ProductNames.Culture = @Culture;
Note: @Culture is passed into the procedure
This always ensures a single Product with either a localised name or default (English) name is returned.
My question is: Is this possible to do at the Mapping level of Fluent NHibernate? I have been searching for days on 'How to join on 2 columns', but cant find a solution which works. It would seem odd if this is not possible in such a mature framework?
As an example of what I have been experimenting with:
public class ProductMap : ClassMap<Product> {
public ProductMap() {
Id(p => p.Id);
Join("ProductNames", pn => {
pn.Optional()
.KeyColumn("ProductID")
.Map(p => p.Name);
});
}
}
However, this results in the following exception:
More than one row with the given identifier was found: 109, for class: Product
This is because product 109 has 5 translations and thus all 5 cannot be mapped to a single string.
I have managed to use the 'HasMany<>' method to map all translations into a List within a Product. However, this is not what I need.
Solution
if the name is readonly then
public class ProductMap : ClassMap<Product> {
public ProductMap() {
Id(p => p.Id);
Map(p => p.Name).Formula("Select ISNULL(pn.Name, DefaultName) FROM ProductNames pn WHERE pn.ProductID = ID AND pn.Culture = '" + GetCUltureFromSomewhere() + "'");
}
}