If I have a Table Bob with a foreign key to Frank how do configure the mapping such that the class Bob looks like this (without the class Frank):
public class Bob{
public int FrankId{get;set;}
///not public Frank Frank{get;set;}
}
Related
Sorry for a lengthy question. But it is worth giving all the details so please bear with me through to the end.
I'm working against a legacy database over which I do not have much control. I want to be able to map a class to multiple database tables. Here is how my tables look
Lookup
+--------+--------------+------------+
| Column | DataType | Attributes |
+--------+--------------+------------+
| Id | INT | PK |
| Code | NVARCHAR(50) | |
+--------+--------------+------------+
Culture
+--------------+--------------+------------+
| Column | DataType | Attributes |
+--------------+--------------+------------+
| Id | INT | PK |
| Culture_Code | NVARCHAR(10) | |
+--------------+--------------+------------+
Lookup_t9n
+----------------+---------------+---------------------+
| Column | DataType | Attributes |
+----------------+---------------+---------------------+
| Id | INT | PK |
| Culture_Id | INT | FK to Culture table |
| Localised_Text | NVARCHAR(MAX) | |
+----------------+---------------+---------------------+
As you can see, I have a lookup table where all lookups are stored. The display text for a lookup is localized and stored in a separate table. This table has a foreign key to culture table to indicate the culture for which the localized text exists.
My class looks like this
public class Lookup {
public virtual int Id {get; set;}
public virtual string Code {get; set;}
public virtual string DisplayText {get; set;}
}
And my FNH mapping class looks like this
public class LookupMappings : ClassMap<Lookup> {
public LookupMappings()
{
Table("Lookup");
Id(x => x.Id).Column("Id");
Map(x => x.Code).Column("Code");
Join("Lookup_t9n", join => {
join.Map(x => x.DisplayText).Column("Localised_Text"); //Note this place, my problem is here
})
}
}
In the above mapping, in Join part I want to provide some where clause like WHERE Lookup_t9n.Culture_Id = Culture.Culture_Id AND Culture.Culture_Code = System.Threading.Thread.CurrentUICulture.CultureCode.
I know this is not a valid SQL but conveys the intent I hope. Has anyone have any experience of doing such a thing.
I can add a mapping layer where I can have classes that map one-to-one with database tables and then write plain c# to map those classes back to my Lookup class. I have rather done that as an interim solution. I was wondering if I can remove that mapping layer with some smart NH use.
I do not have simple answer, like CallThis(). I would like to give you suggestion, based on how we are using similar stuff. The solution is base on the standard mapping, hidding its complexity in C# Entities. It is just a draft of the solution so I'll skip the middle Culture table, and will expect that in Lookup_t9n we do store just a culture name (en, cs...)
Let's have this class
public class Lookup {
public virtual int Id {get; set;}
public virtual string Code {get; set;}
// for simplicity skipping null checks
public virtual DisplayText { get { return Localizations.First().LocalizedText; } }
public virtual IList<Localization> Localizations {get; set;}
}
public class Localization { // mapped to Lookup_t9n
public virtual string CultureName {get; set;}
public virtual string LocalizedText {get; set;}
}
Having this, we can map the collection of Localizations as HasMany. It could even be mapped as a component (see example of component mapping)
Now, what we do need is to introduce a filter. Example with Fluent. The essential documentation: 18.1. NHibernate filters.
Simplified mapping
filter:
public class CulturFilter : FilterDefinition
{
public CulturFilter()
{
WithName("CulturFilter")
.AddParameter("culture",NHibernate.NHibernateUtil.String);
}
collection:
HasMany(x => x.Localization)
.KeyColumn("Id")
...
.ApplyFilter<CulturFilter>("CultureName = :culture"))
.Cascade.AllDeleteOrphan();
Finally, we have to introduce some AOP filter, IInterceptor... which will be triggered each time (needed) and adjust the ISession
session
.EnableFilter("CulturFilter")
.SetParameter("culture"
,System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
And now we have Localized string based on current culture, while using standard mapping of localized values as a collection.
I am using Nhibernate and I have a problem when fetching a base class with multiple derived classes (each class mapping a different table). When I watch the request, Nhibernate joins on every derived tables which has a huge an impact on the performances...
Here is a simplified vision of my classes :
public class Animal{
public virtual int ID { get; set;}
public virtual string Name { get; set;}
}
public class Dog : Animal{
//others properties
}
public class Cat: Animal{
//others properties
}
public class Person{
public virtual int ID { get; set;}
public virtual IEnumerable<Animal> Animals { get; set;}
}
A person has a list of Animals and I just want their names. The example is not perfect and more it's more complicated (a banking program) but it reflect well my problematic.
I KNOW it can be done differently etc, but it is a legacy so I don't have a choice...
Thanks in advance.
IMO NHibernate will only joind tables which contain projected columns. define a query but do not return Person but project into a dto/anonymous class the properties you need
After all, I created a class AnimalBase which is inherited by Dog, Cat and so forth and a class Animal without any child (both having the interface IAnimal).
As in 95% of my request, I only need Animal, I reference this class in my other objects like Person.
Not perfect but I did not find anything better...
Thanks Firo for your help.
Using FluentNhiberante is there a way to map the following:
Parent Table (Employee)
EmployeeId INT Primary Key
FirstName
LastName
EmployeeTypeId
Lookup Table (EmployeeType)
EmployeeTypeId INT Primary Key
EmployeeTypeDescription
My class is defined as:
public class Employee
{
int EmployeeId {get; set;}
...
string EmployeeTypeDescription {get; set;}
}
Is there a way via the FluentNhibernate mapping to populate the EmployeeTypeDescription property in the Employee object from the EmployeeTypeDescription table by looking up using the EmployeeTypeId column in Employee?
I'm pretty sure the normal and proper way to do this is by using References in the mapping file and then by adding a EmployeeType property to the Employee class and accessing the description using Employee.EmployeeType.EmployeeTypeDescription. I'm unable to change the code to do that at this time so am wondering how to just set the EmployeeTypeDescription property for now.
it should be possible tweaking the examplecode below:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
...
Join("EmployeeType", join =>
{
join.KeyColumn("EmployeeTypeId");
join.Map(k => k.TherapieOK, "somecolumn"));
}
...
}
}
You can map the class to a view.
You're correct as far as the normal way to do this.
Hoping you can help me with a mapping problem I have. At the moment I have a view which returns something such as:
Name | Title | Price | Date | Format | FormatPriority |
Example data may be:
Bob | Credits | 340 | 01/01/2010 | BAR | 10 |
Bob | Credits | 340 | 01/01/2010 | FOO | 20 |
Bob | Credits | 340 | 01/01/2010 | WOO | 40 |
What I want is a domain model which looks like this:
string Name;
string Title;
int Price;
DateTime Date;
IEnumerable Formats;
Format class would then have:
string Type
int Priority
At the moment we are using the ClassMap approach within Fluent NHibernate (not auto config). How would we map this? The Component doesn't seem to support a collection and this isn't a HasMany relationship as it's coming back as part of the same query.
Any ideas??
Thanks
Ben
Disclaimer: This is such a huge hack, it pains me to post it.
This is based on the schema you've provided, so it might need to be modified to accomodate a different design. There probably is a much better way to do this, but hopefully this should get you going again at least.
The issue is you have a bit of a mismatch in your model and query. Your query returns multiple rows which you intend to be for a single entity with multiple components, but NHibernate is geared to interpret that as multiple entities each with a single component.
NHibernate supports collections of components, but only when they're stored in a separate table/view. These components are joined via a foreign key back to the entity table. If you can change your design to support this, please do so!
If not, the only option I could think of is a self-join on your view. It won't produce the most optimised query, but it should do the trick.
You didn't mention what your entity was called, so I've gone with Transaction.
public class Transaction
{
public virtual string Name { get; set; }
public virtual string Title { get; set; }
public virtual decimal Price { get; set; }
public virtual DateTime Date { get; set; }
public virtual ISet<Format> Formats { get; set; }
}
public class Format
{
public virtual string Type { get; set; }
public virtual int Priority { get; set; }
// OVERRIDE EQUALITY MEMBERS!
}
The mapping I've used is:
public class TransactionMap : ClassMap<Transaction>
{
public TransactionMap()
{
Table("vwTransactions");
Id(x => x.Name);
Map(x => x.Title);
Map(x => x.Price);
Map(x => x.Date);
HasMany(x => x.Formats)
.Table("vwTransactions")
.KeyColumn("Name")
.Component(c =>
{
c.Map(x => x.Type, "Format");
c.Map(x => x.Priority, "FormatPriority");
})
.Fetch.Join();
}
}
So you can see the mapping is pointed at the vwTransactions view. You didn't specify an id in your schema, so I've used Name as a identity (this is important). Skip down to the HasMany now, you can see that also points at vwTransactions; NHibernate will see this and do a self-join on the view. Then the key column is set to Name, the same as the entity Id; this way NHibernate will use that to resolve the references between the component and the entity, rather than trying to use an integer foreign key. The Fetch.Join will force NH to eagerly fetch this relationship, so at least we save a bit there. Last thing of note, the Formats property is an ISet, if you don't do this you'll end up with duplicate components.
If you now create a criteria (or hql) query for Transaction, you'll get back your entities with their components; however, you'll get duplicates due to the multiple rows being brought back per entity. This is fairly common, and easily resolved using the DistinctRootEntity transformer.
var transactions = session.CreateCriteria(typeof(Transaction))
.SetResultTransformer(Transformers.DistinctRootEntity)
.List<Transaction>();
That should be it, you'll now end up with just one entity (based on your dataset) with 3 components.
Nasty, I know.
I have a scenario in NHibernate where I have a one-to-many relationship between entities Employee and EmployeeStatus.
Employee has properties eg: ID, Name and an IList of EmployeeStatus, whilst EmployeeStatus, for the purposes of this question, just has it's own ID and some free text.
I don't need to hold a reference to Employee from EmployeeStatus, the management of status' will be done purely through the Employee entity - adding to the IList property. IE: I want to quite simply be able to do the following;
Employee e = new Employee();
e.Name = "Tony";
e.StatusList.Add( new EmployeeStatus("Status A") );
e.StatusList.Add( new EmployeeStatus("Status B") );
session.Save(e);
I've tried various methods, including creating a one way one-to-many mapping where inverse is false, cascade set to all-delete-orphan, which all looks like it should work, but it generates an exception about being unable to set the EmployeeId in EmployeeStatus. I'm led to believe that this is because NHibernate wants to do an insert with EmployeeId as NULL and then update it to the ID of the parent.
I guess I'm missing something here, so quite simply - can anyone tell me what my mapping file should look like to achieve the above?
Thanks in advance
Tony
-- edit: Heres a rough idea of the classes as requested --
public class Employee
{
private IList<EmployeeStatus> _statusList;
public Employee()
{
_statusList = new List<EmployeeStatus>();
}
public virtual int Id{ get; set; }
public virtual string Name{ get; set; }
public virtual IList<EmployeeStatus> StatusList
{
get
{
return _statusList;
}
}
}
public class EmployeeStatus
{
public virtual int Id{ get; set; }
public virtual string StatusText{ get; set; }
public EmployeeStatus()
{
}
public EmployeeStatus(string statusText)
{
StatusText = statusText;
}
}
The scenario you've described is just a basic one-to-many mapping. Here is the Fluent NHibernate mapping for this:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
WithTable("Employee");
HasMany(employee => employee.StatusList)
.Cascade.All();
}
}
You do not need to maintain a reference from EmployeeStatus back to Employee to achieve this.
Turns out that what I want to do isn't possible - you have to have a bi-directional association, and must set the child's parent reference. Not a massive problem I suppose, but didn't want to hold references in the child that I don't need within my code directly.
I may not of explained clearly, but an employee status cannot be linked to more than one employee. It's definitely 1 (employee) to many (status')
In the physical database, the status entity has an employeeID field, which isn't in the domain - IE: I hold no reference back to employee from the status entity, but the physical field should be inferred from the owner of the collection - In fact, it does do this if I set the EmployeeID field in the status table to nullable - it actually executes 2 SQL statements - an insert and then an update, the EmployeeID being set in the update.
Thanks,
Tony
Can you post the code for the classes?
Are you trying to keep a history of statuses for an Employee?
-- Edit --
Looks like you are going to need many-to-many, since the child in the relationship (EmployeeStatus) has no reference back to the parent (Employee).
-- Edit 2 --
If you want the insert to be done as 1 call to the DB, you are going to need to add an Employee property to the EmployeeStatus class, and set the Inverse=true. And I'm pretty sure that you are going to need to add some logic which sets the bi-directional relationship in the objects. I.E.
public void AddStatus(EmployeeStatus status)
{
this.StatusList.Add(status);
status.Employee = this;
}