Fluent NHibernate Mapping: one-to-one (or none) - c#

I have a following database scheme setup which I can't really change.
User
----
Id (primary key)
[Some simple properties...]
UserAdditionalData
------------------
Id (primary key)
[Some simple properties...]
USERID (foreign key to User)
It's clear that the User table doesn't really have any recollection whether or not it is linked to a UserAdditionalData record, so I don't think I can call this a true one-to-one mapping here since they also don't share a mutually exclusive PK.
However, in practice, I would like to be able to work on a User object and for example check if it has a UserAdditionalData record and if so, access its properties.
I've set up my BDO as such:
public class User
{
[Some simple properties...]
public virtual UserAdditionalData UserAdditionalData { get; set; }
}
public class UserAdditionalData
{
[Some simple properties...]
public virtual User User { get; set; } /* I have this here,
but I don't really ever
have to access it in this
direction */
}
I've set up my mapping as such:
public UserMapping()
{
Table("USER");
[Some simple properties...]
HasOne(x => x.UserAdditionalData).Cascade.None();
}
public UserExtraMapping()
{
Table("USER_ADDITIONAL_DATA");
[Some simple properties...]
References(x => x.User, "USERID").Unique();
}
This all compiles, but I see that my UserExtra object (when accessed via a User object) is always null.
I've tried a lot of different ways to go about it, read a lot on implementing this as a one-to-many. However, I'm still not being able to get it to work.
Any help would be much appreciated.
Thanks!
[Small UPDATE]: I only have to query the database, not save to it if that's relevant in any way.

Based on your small update, I would go with a simplified mapping. We would profit from NHibernate real mapping power, and also optimize the User loading. All that because we do need Read-Only mapping.
Firstly, we should introduce simple int property UserId on the Additional class
// extra class is having an int property containig the foreign key
public class UserAdditionalData
{
public virtual int UserId { get; set; }
}
// that would be the mapping:
public UserExtraMapping()
{
...
Map(x => x.UserId, "USERID");
}
Now, we will use well optimized mapping for lazy loading many-to-one (I.e. in comparison with one-to-one which loads both ends always, here we will get the reference data only if really needed!)
public UserMapping()
{
...
References(x => x.UserAdditionalData)
.LazyLoad()
.PropertyRef(e => e.UserId)
.Not.Insert()
.Not.Update()
;
}
So, for readonly I would do the best to use many-to-one mapping (References())
see also:
5.1.10. many-to-one
mapping by code, ManyToOne (scroll down to Fluent NHibernate's equivalent)

Related

Mapping One-to-Zero-or-One with EF7

I am currently in the process of cleaning up a fairly large database. Part of the database has a relationship which is a one-to-zero-or-one mapping. Specifically:
User -> UserSettings
Not all users will have user settings, but a user setting cannot exist without the user. Unfortunately, the tables already exist. User has an PK ID. UserSettings has a PK ID and a column, User_Id_Fk which, at this point in time, is not a true FK (there is no relationship defined).
I'm in the process of fixing that and have done so from the DB perspective through SQL and have confirmed with tests. (Added the FK constraint. Added a unique constraint on User_Id_Fk.) This was all done on the UserSettings table. (Note: I am not using EF Migrations here. I have to manually write the SQL at this point in time.)
However, I now need to wire up an existing application to properly handle this new mapping. The application is using ASP.NET Core 1.0 and EF7. Here are (shortened) versions of the existing data models.
public class User
{
public int Id { get; set; }
public virtual UserSettings UserSettings { get; set; }
}
public class UserSettings
{
public int Id { get; set; }
[Column("User_Id_Fk")]
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
}
I have this Fluent Mapping as well:
builder.Entity<UserSettings>()
.HasOne(us => us.User)
.WithOne(u => u.User)
.IsRequired(false);
When I go to run the application and access these items in the database, I get this error followed with a cryptic set of messages that has no information relating directly back to my application.:
ArgumentNullException: Value cannot be null.
Parameter name: navigation
Microsoft.Data.Entity.Utilities.Check.NotNull[T] (Microsoft.Data.Entity.Utilities.T value, System.String parameterName) <0x10d28a650 + 0x00081> in <filename unknown>, line 0
After doing research, someone had mentioned that the ID of the UserSettings class must be the same as the foreign key, like so:
public class UserSettings
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public virtual User User { get; set; }
}
I don't really have this as an option as the DB is being used for other applications I have no control over at this point. So, am I stuck here? Will I just have to maintain a 1:many mapping (which could happen now, though it hasn't) and not have proper constraints for a 1:0..1 mapping?
Update
Looking at octavioccl's answer below, I tried it out without any success. However, I then removed User from the mapping in UserSettings (but I left UserId). Everything appeared to work as far as I can tell. I'm really confused what is going on here, however, and if this is even the right answer, or if I'm just getting lucky.
Remove the data annotations and try with these configurations:
builder.Entity<UserSettings>()
.Property(b => b.UserId)
.HasColumnName("User_Id_Fk");
builder.Entity<User>()
.HasOne(us => us.UserSettings)
.WithOne(u => u.User)
.HasForeignKey<UserSettings>(b => b.UserId);
From EF Core documentation:
When configuring the foreign key you need to specify the dependent
entity type - notice the generic parameter provided to HasForeignKey
in the listing above. In a one-to-many relationship it is clear that
the entity with the reference navigation is the dependent and the one
with the collection is the principal. But this is not so in a
one-to-one relationship - hence the need to explicitly define it.
The example that is presented in the quoted link (Blog-BlogImage) is pretty much the same of what are you trying to achieve.
If the solution that I show above doesn't work, then you should check if User_Id_Fk column allows null. If that is the case, change the FK property type to int?:
public class UserSettings
{
public int Id { get; set; }
public int? UserId { get; set; }
public virtual User User { get; set; }
}

(Fluent) NHibernate table-per-hierarchy; Id is only unique along with discriminator

I have a legacy database mapping issue. The database is storing all of its lookup values (code/description) in one table, distinguished by a type code field. The tables referring to it do so with one column (the code, without the type code). The code table does not have a primary key constraint (!).
I have a class that looks like this:
public class Code
{
[StringLength(8)]
public virtual string CodeValue { get; set; }
[StringLength(2000)]
public virtual string Description { get; set; }
public virtual long? OrderBy { get; set; }
public virtual DateTime? StopDate { get; set; }
}
My initial mapping looked like this:
public class CodesMap : ClassMap<Code>
{
public CodesMap()
{
Table("CODES");
Id(x => x.CodeValue).Column("CODE_CODE").GeneratedBy.Assigned();
Map(x => x.Description).Column("DESCRIPTION");
Map(x => x.OrderBy).Column("ORDER_BY");
DiscriminateSubClassesOnColumn("TYPE_CODE", "INVALID")
.SqlType("VARCHAR2");
}
}
And then there are a bunch of sub-classes that differ only in their discriminator values.
Another mapping might reference this as:
...
References<FacilityType>(x => x.Type).Column("FACIL_TYPE_CODE").ReadOnly();
...
Now, this is all well and good, and everything works, since that reference knows the class, and therefore the discriminator value for the query, except...I only just hit the case where CODE_CODE is non-unique between two objects of different types (both subtypes of Code) in the same session. Oops.
CODE_CODE and TYPE_CODE are unique together, so the right thing ought to be to use them as a composite key. But then my References in the other class maps become impossible, because those tables only have a single column foreign key (obviously no FK constraint defined on table).
Short of adding a surrogate key on the code table, whatever shall I do?
In case, that we need to map look up values as readonly, solution would be surprisingly very easy. Instead of explicit inheritance, we will explicitly map each subclass. The discriminator will be moved to a WHERE clause:
public FacilityTypeMap()
{
Table("CODES");
// here we will use explicit runtime discriminator
// injected by NHibernate into each SELECT .. FROM clause for this type
Where(" TYPE_CODE = 'FACIL_TYPE' " )
Id(x => x.CodeValue).Column("CODE_CODE").GeneratedBy.Assigned();
Map(x => x.Description).Column("DESCRIPTION");
Map(x => x.OrderBy).Column("ORDER_BY");
// no more inheritance
// DiscriminateSubClassesOnColumn("TYPE_CODE", "INVALID")
// .SqlType("VARCHAR2");
}
This is very well working for SELECT. We just have to repeat that mapping for each Discriminator == each derived type.
see 5.1.3. class:
<class
name="ClassName" (1)
table="tableName" (2)
...
where="arbitrary sql where condition" (11)
(11) where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving objects of this class
In case, we need to use this class also for insert, we have to do few more steps. Explicitly map column 'TYPE_CODE' as e.g. Discriminator, and set it in constructor to correct value (e.g. 'FACIL_TYPE'). It could be protected property mapped as .Not.Update()
string _discriminator = "FACIL_TYPE";
public virtual string Discriminator { get { return _discriminator; } protected set {} }
...
// mapping
Map(x => x.Discriminator, "TYPE_CODE").Not.Update()

How to create NHibernate HasManyToMany relation

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...

Force Mapping of only logical Foreign Key in Code First

I am using Entity Framework and Code First to map to a legacy database (I know that if I have the database already built then I should the "Database First" approach, but some how, I fill better putting the annotations by hand than handling control over the designer), in which, there is a table that have a key that point to another table (it is actually a synonym to a table in another db) that works like a foreign key but it not actually defined in the table schema.
The question is: can I force this relation to exist in my POCO classes? can I map this using the Fluent API? is this possible at all?
As usual, thanks in advance!
Sure, you can manually define a foreign key and navigation properties either by fluent mapping or data annotations.
Let's say I have a Parent table with a field CatCode and a synonym CategoryEx that points to a table Category in a second database. Both have corresponding classes and DbSets in one context. The mappings (derived from EntityTypeConfiguration) could look like (the constructors):
public ParentMap()
{
this.HasKey(t => t.Id);
this.HasRequired(p => p.Category).WithMany().HasForeignKey(p => p.CatCode);
...
}
public CategoryMap()
{
this.HasKey(t => t.CatCode);
this.ToTable("CategoryEx");
...
}
where Parent looks like:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public string CatCode { get; set; }
...
public Category Category { get; set; }
}

NHibernate recreates every associated item

I have a very simple object models.
public class Contact
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Device Device { get; set; }
public virtual IList<string> Numbers { get; set; }
public Contact()
{
Numbers = new System.Collections.Generic.List<string>(3);
}
}
As you can see, the class Contact has an association with Numbers, which is a list of strings.
Here's the mapping:
Id(x => x.Id).GeneratedBy.Assigned();
Map(x => x.Name);
References(x => x.Device, "DeviceId");
Table("Contacts");
HasMany(x => x.Numbers)
.Table("ContactNumbers")
.Element("Number")
.KeyColumn("ContactId")
.LazyLoad()
.Cascade.All()
.Not
.Inverse();
Note that I can't and don't want the collection to be inverse=true, because it's just a collection of string. This means that Contact is responsible for updating Numbers entries.
Now my problem is that, whenever I try to add a new number to an existing Contact, it deletes all associated numbers and recreates them individually. Isn't NHibernate smart enough to detect changes and update only changed items?
I think there should be a simple solution for my problem but don't know what.
Any help would be appreciated.
This is actually documented in NHibernate's documentation.
Bags are the worst case. Since a bag permits duplicate element values
and has no index column, no primary key may be defined. NHibernate has
no way of distinguishing between duplicate rows. NHibernate resolves
this problem by completely removing (in a single DELETE) and
recreating the collection whenever it changes. This might be very
inefficient.
Try using an <idbag> mapping instead, and create a surrogate primary key for that table. Unfortunately, looks like <idbag> is not yet supported in FluentNHibernate.
Also, take a look at other collection mapping options.

Categories

Resources