Nhibernate ManyToMany with a dynamic where clause - nhibernate

I'm having some issues mapping a complex many to many relationship in fluentnhibernate. I have a legacy db which looks something like this:
Foos: | Id | Foo |
FooBars: | FooId | BarId |
Bars: | Id | Bar | CultureId |
which I am trying to map to the following object model:
class Foo
{
property virtual int Id { get; set; }
property virtual string Foo { get; set; }
property virtual IList<Bar> Bars { get; set; }
}
class Bar
{
property virtual int Id { get; set; }
property virtual int CultureId { get; set; }
}
with the mappings:
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("foos");
Id(v => v.Id);
Map(v => v.Foo);
HasManyToMany(v => v.Bars)
.Table("FooBars")
.ParentKeyColumn("FooId")
.ChildKeyColumn("BarId")
.Cascade.All();
}
}
public class BarMapping : ClassMap<Bar>
{
public BarMapping()
{
Table("bars");
Id(v => v.Id);
Map(v => v.Bar);
Map(v => v.CultureId);
}
}
The problem is I have multiple Bar's with the same Id for different CultureIds
e.g.
I would have a table that looks like:
Id|Bar|CultureId
1, Hello, 1
1, Bonjour, 2
1, Gutentag, 3
At the moment, the Bars property for the above table will return 3 elements but the Bar property on it will return "Hello" for all three elements (presumably because they all have the same identity). So my question is, how can I either stop this happening or can anyone suggest a way of filtering rows that do not have the correct culture id (note, this is dynamic & based on the current culture)?

You can't create dynamic where clauses in your mappings. You're going to need to query this collection instead of accessing it via the parent, using a Criteria or HQL query. You could read up on filters, but they still require a query.

Related

Possible to dynamically join a related model in Entity Framework Core?

Say there's a project that involves a lot of tables, all of which reference a single table that holds a field called group ID. However, not all of the tables reference this single table directly.
For example, the db contains a garages table, and each garage is referenced by rows in a cars table, and each car is referenced by rows in a tire table. I want to organize the data into two groups, group1 and group2. Each garage has a group ID, however the cars and tires do not. Here is a diagram of the tables:
As you can see, assets in group 1 are highlighted red, and assets in group 2 are highlighted yellow. But not all rows highlighted necessarily have a group ID. You can imagine how this may go even longer, with hub caps having forgien keys for their respective tires, bolts with forgien keys to their respective hub caps, etc.
Is there a way to dynamically call an EF query that can get the group ID from the related garage, given EITHER a child car, tire, hub cap, and so on? Implementation might be something like this:
var tire = Context.Tires.where(t => t.ID == 3).FirstOrDefault<Tire>();
tire.findShortestJoinToEntity(entity => entity.GetProperty("GroupID") != null);
// Returns 2, the group of "Toyota Tires"
In more technical terms, it would need to recursively check for any forgien keys in the passed in model, then all the forgein keys of the referenced models, all their referenced models, etc. until there are either no more forgein keys or a field with the passed in name is found.
I think you can do it using reflection, it is not the ideal solution due to possible performance issues, but it does what you are looking for.
The key idea here is having all entity classes implementing a common interface IEntity so we can call a recursive method over any entity. Also assumes that we have both parentId (foreign key) and parent object in any entity, although you can tweak the method to use only the parentId, I have put the parent object only to take advantage of EF tracking mechanism linking parents and child entities
public interface IEntity
{
int Id { get; set; }
}
public class Group : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Garage : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual int GroupId { get; set; }
public virtual Group Group { get; set; }
}
public class Car : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual int GarageId { get; set; }
public virtual Garage Garage { get; set; }
}
// ... other entity classes here
public static class EntityExtensions
{
public static int FindGroupId(this IEntity entity, Context context, string propertyName = null)
{
var groupId = 0;
if (entity != null)
{
var entityType = entity.GetType();
if (entity is Group)
{
// we found it
groupId = propertyName == null
? entity.Id
: (int)entityType.GetProperty(propertyName).GetValue(entity);
}
else
{
// look into properties for parent entities
foreach (var property in entityType.GetProperties())
{
if (property.GetGetMethod().IsVirtual
&& property.PropertyType.GetInterface(nameof(IEntity)) != null)
{
// it is parent entity
var parentEntity = property.GetValue(entity) as IEntity;
if (parentEntity == null)
{
// if parent entity is null, let's get it from db
var parentIdName = $"{property.PropertyType.Name}Id";
var parentId = (int)entityType.GetProperty(parentIdName).GetValue(entity);
parentEntity = context.Find(property.PropertyType, parentId) as IEntity;
}
groupId = FindGroupId(parentEntity, context, propertyName);
}
if (groupId > 0)
{
// we found it
break;
}
}
}
}
return groupId;
}
}

Mapping One to One in FluentNHibernate, NHibernate tries to write child before parent

I'm trying to do a 1:1 relationship in FN but its being a bit of a pain.
I looked at http://avinashsing.sunkur.com/2011/09/29/how-to-do-a-one-to-one-mapping-in-fluent-nhibernate/ which seemed to confirm this should work but hey ho, it keeps trying to insert records into the child tables before the parent which means the child-inserts dont contain the CustomerId which is required as its a foreign key constraint.
Tables
+-----------------------+ +----------------------+
| tblCustomer | | tblCustomerPhoto |
|-----------------------| |----------------------|
| | 1:1 | |
| CustomerID (PK) |+---->| CustomerID (FK) |
| OtherJunk... | | Photo (Image) |
| | | |
| | | |
+-----------------------+ +----------------------+
Models
public class Customer
{
public virtual int CustomerID { get; private set; }
/* public virtual Other stuff */
}
public class CustomerPhoto
{
public virtual int CustomerID { get;set;}
public virtual Byte[] Photograph { get; set; }
}
Maps
public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
public CustomerPhotoMap()
{
Id(x => x.CustomerID)
.Column("CustomerID")
.GeneratedBy.Assigned();
Map(x => x.Photograph)
.CustomSqlType("Image")
.CustomType<Byte[]>()
.Nullable();
}
}
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID)
.GeneratedBy.Identity()
.Column("CustomerID");
HasOne<CustomerPhoto>(x => x.CustomerPhoto)
.LazyLoad()
.ForeignKey("CustomerID");
}
}
Test
class CustomerMappingFixture : IntegrationTestBase
{
[Test]
public void CanMapCustomer()
{
new PersistenceSpecification<Customer>(Session)
.CheckReference(x => x.CustomerPhoto, new CustomerPhoto(){ Photograph = Arrange.GetBitmapData(ImageFormat.Jpeg) })
.VerifyTheMappings();
}
}
Now the foreign key column on the CustomerPhoto was set to not-null and I was repeating this in the notation on the CustomerPhotoMapping.
On the basis of ( https://stackoverflow.com/a/2286491/529120 ) I changed that to nullable and removed the notation from the mapping.
Regardless of which, NHibernate returns
System.NullReferenceException : Object reference not set to an instance of an object.
and appears to be trying to insert a CustomerPhoto record first, passing zero as the CustomerId; then creating the Customer record, then trying to select the customer and photo using a left outer join. Which obviously wont work as at no point has it attempted to update the ID in the photo table.
Few things I noticed
Using CheckReference to verify this mapping is probably incorrect. I'm pretty sure this is only for many-to-one relationships. Therefore it makes sense that it's trying to insert the CustomerPhoto before the Customer. I would write a test using straight up NH sessions here. I found it to be more trouble than it's worth to use PersistenceSpecification for many of my mappings that were non-trivial.
The one to one mappings looked like they were a bit off (proposed solution below)
When mapping an image column to a byte array I don't think there is a need to declare a custom type. The mapping below has worked fine for me. I think the other stuff you have on that property mapping is un-needed.
I have a mapping almost identical to yours and this is what I use:
public class Customer
{
public virtual int CustomerID { get; private set; }
public virtual CustomerPhoto CustomerPhoto { get; set; }
/* public virtual Other crap */
}
public class CustomerPhoto
{
public virtual Customer Customer { get; set; }
public virtual Byte[] Photograph { get; set; }
}
public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
public CustomerPhotoMap()
{
Id(x => x.Id)
.Column("CustomerID")
.GeneratedBy.Foreign("Customer");
Map(x => x.Photograph).Length(Int32.MaxValue);
}
}
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID).GeneratedBy.Identity()
.Column("CustomerID");
HasOne(x => x.CustomerPhoto)
.Cascade.All();
}
}

NHibernate joining two table into one entity with a composite key

I have the following data structure :
+---------+
|Resume |
+---------+
|Id (PK) |
|IsActive |
|... |
|.. |
|. |
+---------+
+--------------------+
|Resume_Translation |
+--------------------+
|ResumeId (PK, FK) |
|Language (PK) |
|Title |
|Description |
|... |
|.. |
|. |
+--------------------+
So I could have such a data with two joined tables :
+----------------------------------------------------------+
|Id | IsActive | ResumeId | Language | Title | Description |
+----------------------------------------------------------+
|1 | true | 1 | 'fr' | 'One' | 'One desc' |
|1 | true | 1 | 'pl' | 'Raz' | 'Raz Opis' |
|2 | true | 2 | 'fr' | 'B' | 'bla bla' |
|3 | true | 3 | 'fr' | 'C' | 'C bla bla' |
+----------------------------------------------------------+
From my domain point of view I care only about Resume entity. I don't want to have Resume entity with its collection of Resume_Translations because I would only have one Resume entity with a current translation.
public class Resume
{
public virtual int Id{ get; protected internal set; }
public virtual string Language { get; protected internal set; }
public virtual string Title { get; protected internal set; }
public virtual string Description { get; protected internal set; }
public virtual bool IsActive { get; protected internal set; }
}
My current mapping with Fluent NHibernate is as follows :
public class ResumeMap : ClassMap<Resume>
{
public ResumeMap()
{
Table("Resume");
Id(x => x.Id);
Map(x => x.IsActive);
// other properties
Join("Resume_Translation", m =>
{
m.Fetch.Join();
m.Map(x => x.Language).Length(5);
m.Map(x => x.Title).Length(100);
m.Map(x => x.Description).Length(200);
});
}
}
I can get what I want from the repository without problem just passing in the WHERE predicate the Id of Resume and the Language I want to.
However I have some problems with Inserting and Updating the values.
My question is: How I would define a mapping that NHibernate Inserts a new record only in Resume_Translation table instead of Updating the record for the current entity ?
So what I want to achieve is if I have in the database the following record :
|2 | true | 2 | 'fr' | 'B' | 'bla bla' |
Join is good for one to one relationship between tables so if I get this into my entity and I change the language and translation, nhibernate is performing an update and I can understand it. If I try to add a new entity with the same Id by different language and translation, nhibernate yields an error that a key already exists and I understand it also.
So, certainly I'm going down the wrong path, but If some one could point me to the correct solution on how I could achieve a mapping that I want I would greatly appreciate.
Another question, how do you deal with a entities and theirs translations from the business point of view ?
Thanks, in advance for your help.
Thomas
Stefan is on the right track. I've tweaked his suggestion to have a bi-directional association which would make updating a lot easier. One catch with this approach is that you need to manually assign the Resume property of the ResumeTranslation instance when inserting so that NHibernate will properly assign the Resume table key to the ResumeTranslation row. So, given the associations you are mapping, this is how it would look in Fluent NH:
public class ResumeTranslation
{
public virtual string Title { get; protected internal set; }
public virtual string Description { get; protected internal set; }
//Needed for bi-directional association:
public virtual Resume Resume { get; set; }
}
public class ResumeTranslationMap : ClassMap<ResumeTranslation>
{
public ResumeTranslationMap()
{
Table("ResumeTranslation");
CompositeId()
.KeyReference(kp => kp.Resume, "ResumeId")
.KeyProperty(kp => kp.Language, "Language");
Map(x => x.Title);
Map(x => x.Description);
}
}
public class ResumeMap : ClassMap<Resume>
{
public ResumeMap()
{
Table("Resume");
Id(x => x.Id);
Map(x => x.IsActive);
// other properties
HasMany(c => c.Translations)
.Inverse()
.KeyColumn("id") //May not be required but here for reference
.Cascade.All();
}
}
Seems like a one to many relationship to me. I would personally have a collection of ResumeTranslation objects within my Resume object. I would then map this as a standard one to many.
You could then add another property ActiveResumeTranslation to your Resume entity that is representative of your current translation.
What about using a dictionary, using the language as a key?
public class ResumeTranslation
{
public virtual string Title { get; protected internal set; }
public virtual string Description { get; protected internal set; }
}
public class Resume
{
public virtual int Id{ get; protected internal set; }
// language is the key to the translation
// you may even want to hide the dictionary from the public interface of
// this class and only provide access to a "current" language.
public virtual IDictionary<string, ResumeTranslation> Translations { get; private set; }
public virtual bool IsActive { get; protected internal set; }
}
And map it accordingly as a map with a composite-element (sorry, I'm not using fluent, so don't ask me how it would look like). It would exactly match your database model.

how to set key column im many to many relation with fluent nhibernate

I am using flunet nhibernate and in a many to many relation I need to have key column for the table between these two entities
HasManyToMany(p => p.Signers)
//.Cascade.AllDeleteOrphan()
.Table("PersonnelDocumentSigner")
.Schema("personnel");
public partial class PersonnelDocument
{
private IList<Position> _signers;
virtual public IList<Position> Signers
{
get
{
if (_signers == null)
_signers = new List<Position>();
return _signers;
}
set
{
_signers = value;
}
}
}
the created table just consist of these two columns:PersonnelDocumentId,PositionId
but I need a column Id for this connector table "PersonnelDocumentSigner"
how exactly I can assign it?
link-tables like your PersonnelDocumentSigner normally dont need an Id column because PersonnelDocumentId and PositionId together are the Id/Primary key. If you still want to have additional Data in the linking Table you should Create a new Entity.
class PersonnelDocumentSigner
{
public virtual PersonnelDocument Document { get; set; }
public virtual Position Signer { get; set; }
public virtual int Id { get; set; }
// additional Properties
}
class PersonnelDocumentSignerMap : ClassMap<PersonnelDocumentSigner>
{
Table("PersonnelDocumentSigner");
CompositeId()
.KeyReference(pds => pds.Document)
.KeyReference(pds => pds.Signer);
Map(pds => pds.Id);
// additional Mapps
}

How do I use Fluent NHibernate to configure the following joins?

This is based on a legacy system.
I have the following tables:
CREATE TABLE a
id int
CREATE TABLE b
a_id int,
c_id int
relationshipid int -- must be IN (1, 2, 3)
CREATE TABLE c
id int
I want the following domain models
public class A
{
public int Id { get; set; }
public C entityc { get ; set; }
}
public class C
{
public int Id { get; set; }
}
Table b is set up so that for a particular defined relationshipid there is (well, should only be) one pair of ids. For other relationships, that one to one mapping through B doesn't hold true. Relationshipid can be one of a small number of values.
How do I get entity C into class A from the relationship where the relationshipid is 1 using fluent NHIbernate?
As a side question, is there a name for what I am trying to do here? The original approach was trying use a HasOne with a Join table and Filter the results, but obviously that failed miserably.
EDIT: Clarified RelationshipID and purpose.
I think the easiest way to map this would be to make your table b an entity and have references to both A and C within that entity and RelationshipId as the id. So your mappings would look something like this:
public class A
{
public int Id { get; set; }
public IList<B> bEntities { get; set; }
}
public class ClassAMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.Id);
HasMany(x => x.bEntities)
.KeyColumns.Add("a_id");
}
}
public class B
{
public virtual int RelationshipId { get; set; }
public virtual A InstanceA { get; set; }
public virtual C InstanceC { get; set; }
}
public class ClassBMap : ClassMap<B>
{
public BMap()
{
Table("B");
Id(x => x.RelationshipId , "relationshipid");
References(x => x.InstanceA);
References(x => x.InstanceC);
}
}
Edit:
If your wanting to filter these results for the collection of B entities in your A entity to only ones matching RelationshipId = 1 then you should take a look at this post:
Fluent NHibernate and filtering one-to-many relationship on query requiring multiple joins?
You could also do something like this in your class A:
public class A
{
public int Id { get; set; }
public IList<B> bEntities { get; set; }
public C InstanceC
{
get { return bEntities.First<B>(x => x.RelationshipId == 1).InstanceC; }
}
}