Fluent NHibernate mapping on multiple non-PK fields - nhibernate

I have a situation similar to that described in Fluent NHibernate Mapping not on PK Field
However, the relationship between my tables is described by multiple non-primary key columns.
Imagine Chris Meek's situation but where a Person has a JobType and a Code that, together, should (sorry, it's a legacy database) uniquely describe a Person
Person
------
Id PK
JobType
Code
Name
Order
-----
Id PK
Person_JobType
Person_Code
OrderDetails
Serhat Özgel's answer describes using PropertyRef, but I can't find a way to do that individually for multiple columns. I've tried similar to
class PersonMap : ClassMap<Person>
{
public PersonMap()
{
HasMany(p => p.Order)
.KeyColumns.Add("Person_JobType")
.PropertyRef("JobType")
.KeyColumns.Add("Person_Code")
.PropertyRef("Code")
}
}
But this obviously doesn't work, since KeyColumns.Add() returns another OneToManyPart so PropertyRef() isn't being run against the individual column being added. The second PropertyRef() simply overwrites the first one, and I get the following error:
NHibernate.MappingException : collection foreign key mapping
has wrong number of columns: MyApp.Person.Order type: Int32
I've looked at the various overloads of KeyColumns.Add(),
public TParent Add(string name)
public TParent Add(params string[] names)
public TParent Add(string columnName, Action<ColumnPart> customColumnMapping)
public TParent Add(ColumnMapping column)
Specifically the last two, but couldn't find any way to set PropertyRef individually level for each column :(
Is there a way to do that? Am I going about this the wrong way entirely?

using hbm.xml and FluentNHibernate it is possible with a trick
class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Map(_ => JobTypeAndCode)
.Columns.Add("Person_JobType", "Person_Code")
.ReadOnly()
.LazyLoad() // optional: prevent loading the Columns twice
.Access.None();
HasMany(p => p.Orders)
.KeyColumns.Add("Person_JobType", "Person_Code")
.PropertyRef("JobTypeAndCode")
}
private object JobTypeAndCode { get; set; } // FakeProperty
}
Note: i never got this to work using NHibernate MappingByCode

Related

Fluent NHibernate one-to-many disable foreign key constraint

I have this problem where I have a one-to-many relationship and I have to be able to delete the parent entity without it's children being deleted, nor their foreign key column set to NULL. But whenever I try deleting a parent, I get the foreign key constraint violation exception.
NHibernate.Exceptions.GenericADOException: could not execute update query[SQL: delete from [Customer]] ---> System.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK7867CB245055157F"
I have a similar one-to-one relationship, where I have to be able to delete the entity on one end without the foreign key on the other end to be set to NULL and I solved this problem successfully by using NotFound.Ignore(). I have found several answers suggesting exactly this solution, but it seems to have no effect at all. I use my mapping to build the database BTW.
Here are my entities and mappings:
public class User : Entity
{
...
public virtual Customer Customer { get; set; }
...
}
public class Customer : Entity
{
...
public virtual string CustomerNumber { get; set; }
public virtual User User { get; set; }
public virtual IList<Vehicle> Vehicles { get; set; }
...
}
public class Vehicle : Entity
{
...
public virtual string SerialNumber { get; set; }
public virtual Customer Customer { get; set; }
...
}
I'm using AutoMapping and overrides:
public class UserMappingOverride : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
...
mapping.References(u => u.Customer).Column("CustomerNumber").NotFound.Ignore();
}
}
public class CustomerMappingOverride : IAutoMappingOverride<Customer>
{
public void Override(AutoMapping<Customer> mapping)
{
mapping.Id(u => u.Kundenummer).GeneratedBy.Assigned().Not.Nullable();
mapping.HasOne(u => u.User).PropertyRef(c => c.Customer);
mapping.HasMany(u => u.Vehicles).KeyColumns.Add("CustomerNumber")
.Cascade.None()
.Inverse();
}
}
public class VehicleMappingOverride : IAutoMappingOverride<Vehicle>
{
public void Override(AutoMapping<Vehicle> mapping)
{
mapping.Id(u => u.SerialNumber).GeneratedBy.Assigned().Not.Nullable();
mapping.References(u => u.Customer).Column("CustomerNumber").NotFound.Ignore();
}
}
As said, in the one-to-one relationship, in the mapping of User I use NotFound.Ignore(), which does as promised - allows me to delete a Customer without firing a constraint violation exception and still keep the values of "CustomerNumber" in the User table intact. The mapping of the relationship between the User and the Customer entities, simply does not produce a foreign key constraint in the database when the database is built from the mapping.
But the same thing doesn't work for my one-to-many relationship. Although the mapping is almost the same as my one-to-one relationship, and I use NotFound.Ignore() as suggested in similar questions here, this relationship still produces a foreign key constraint and I get a constraint violation exception when trying to delete a Customer. The only work-around is to manually delete the FK in the database or modify it by setting Enforce Foreign Key Constraint to False.
How can I get NHibernate to either not create this Foreign key constraint, or setting the Enfore Foreign Key Constraint to False, when building the database?
Best Regards
- Nikolaj
BTW: I'm not interested in comments about the overall design of the entities and relationships. They're designed like this based on constraints from the source of data, and this is the only plausible workaround. :-) I've found that a lot of answers in similar posts, focus on the design rater then of the question at hand.
you can not work around the FK in the database. My guess is that there is no FK from User to Customer. If you create the Schema from mappings then you'll need to disable the creation of the FK with mapping.References(u => u.Customer).ForeignKey("none");
Firo's answer pointed me in the right direction as how to get rid of the FK constraint. Adding .ForeignKey("none") to the Vehicle mapping didn't do it though. But adding a similar property to the Customer mapping solved my problem.
So the solution became:
mapping.HasMany(u => u.Vehicles).ForeignKeyConstraintName("none")

Writing computed properties with NHibernate

I'm using NHibernate 2.1.2 + Fluent NHibernate
I have a ContactInfo class and table. The Name column is encrypted in the database (SQL Server) using EncryptByPassphrase/DecryptByPassphrase.
The following are the relevant schema/class/mapping bits:
table ContactInfo(
int Id,
varbinary(108) Name)
public class ContactInfo
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class ContactInfoMap : ClassMap<ContactInfo>
{
public ContactInfoMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Formula("Convert(nvarchar, DecryptByPassPhrase('passphrase', Name))");
}
}
Using the Formula approach as above, the values get read correctly from the database, but NHibernate doesn't try to insert/update the values when saving to the database (which makes sense).
The problem is that I would like to be able to write the Name value using the corresponding EncryptByPassPhrase function. I'm unsure if NHibernate supports this, and if it does, I haven't been able to find the correct words to search the documentation effectively for it.
So... how can I write this computed property back to the database with NHibernate?
Thanks in advance!
A property mapped to a formula is read-only.
A named query wrapped up in a ContactInfoNameUpdater service might be one way to solve the problem.

FluentNhibernate and References

I was trying to change a convention so that my IDs follow this simple rule: ProductCode, CustomerCode, OrderCode etc etc.
I've found a simple way to do that adding a convention:
public class PrimaryKeyNameConvention : IIdConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Code");
}
}
Now I've got what I wanted but it seems that FluentNhibernate refuses to apply the same rule with column referencing my primary keys.
EX: my table Customer will have a PK called CustomerCode but my table Order will have a reference column called Customer_Id.
I've tried different ways to rename the column Customer_Id in CustomerCode (table Order) but it seems that nothing works properly.
The only solution which seems to work is adding a convention like this:
public class ReferenceConvention : IReferenceConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IManyToOneInstance instance)
{
instance.Column(instance.Property.PropertyType.Name + "Code");
}
}
but now FluentNhibernate creates two columns which reference my primary key: CostumerCode and Customer_Id.
I can't figure out what I am doing wrong.
Any help would be apreciated.
Regards,
Alberto
Take a look at the ForeignKeyConvention base-class.
The ForeignKeyConvention is an amalgamation of several other conventions to provide an easy way to specify the naming scheme for all foreign-keys in your domain. This is particularly useful because not all the foreign-keys are accessible in the same way, depending on where they are; this convention negates need to know about the underlying structure.
As James suggested I've now applied these two conventions:
public class PrimaryKeyNameConvention : IIdConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Code");
}
}
public class CustomForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if (property == null)
return (type.Name + "Code"); // many-to-many, one-to-many, join
return (property.Name + "Code"); // many-to-one
}
}
and everything works fine.

NHibernate: Map same class to multiple tables depending on parent

I have a model where multiple classes have a list of value types:
class Foo { public List<ValType> Vals; }
class Bar { public List<ValType> Vals; }
Foo and Bar are unrelated apart from that they both contain these vals. The rules for adding, removing, etc. the ValTypes are different for each class. I'd like to keep this design in my code.
There are times when I want to copy some Vals from a Foo to a Bar, for example. In the database, each ValType has its own table, to keep it small, light (it just has the parent ID + 2 fields), and allow integrity checks. I know NHibernate says I should keep my objects as granular as the database, but that just makes my code uglier.
The best I've thought of so far is to make separate subclasses of ValType, one for each parent. Then I can map those at that level. Then, I'll hook up add and remove logic to auto-convert between the right subclasses, and actually store them in a private list that has the right subclass type. But this seemed a bit convoluted.
How can I map this in NHibernate (Fluent NHibernate if possible)?
Please let me know if this is a duplicate -- I'm not quite sure how to search this.
At database level a solution would be to have:
Val(Id)
Bar(Id)
BarToVal(IdBar, IdVal)
FooToVal(IdFoo, IdVal)
I am not very sure how would these be mapped. Maybe something like:
// BarMap:
HasManyToMany(x => x.Vals).WithTableName("BarToVal");
// FooMap:
HasManyToMany(x => x.Vals).WithTableName("FooToVal");
Hope it's making sense...
You can find an example on the Google Code page for Fluent NHibernate.
Model
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
}
Schema
table Customer (
Id int primary key
Name varchar(100)
)
table CustomerAddress (
CustomerID int,
Address varchar(100)
)
Mapping
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id);
Map(x => x.Name);
WithTable("CustomerAddress", m =>
{
m.Map(x => x.Address);
});
}
}
In this example, an entity is split across two tables in the database. These tables are joined by a one-to-one on their keys. Using the WithTable feature, you can tell NHibernate to treat these two tables as one entity.

NHibernate - Do I have to have a class to interface with a table?

I have a class called Entry. This class as a collection of strings called TopicsOfInterest. In my database, TopicsOfInterest is represented by a separate table since it is there is a one-to-many relationship between entries and their topics of interest. I'd like to use nhibernate to populate this collection, but since the table stores very little (only an entry id and a string), I was hoping I could somehow bypass the creation of a class to represent it and all that goes with (mappings, configuration, etc..)
Is this possible, and if so, how? I'm using Fluent Nhibernate, so something specific to that would be even more helpful.
public class Entry
{
private readonly IList<string> topicsOfInterest;
public Entry()
{
topicsOfInterest = new List<string>();
}
public virtual int Id { get; set; }
public virtual IEnumerable<string> TopicsOfInterest
{
get { return topicsOfInterest; }
}
}
public class EntryMapping : ClassMap<Entry>
{
public EntryMapping()
{
Id(entry => entry.Id);
HasMany(entry => entry.TopicsOfInterest)
.Table("TableName")
.AsList()
.Element("ColumnName")
.Cascade.All()
.Access.CamelCaseField();
}
}
I had a similar requirement to map a collection of floats.
I'm using Automapping to generate my entire relational model - you imply that you already have some tables, so this may not apply, unless you choose to switch to an Automapping approach.
Turns out that NHibernate will NOT Automap collections of basic types - you need an override.
See my answer to my own question How do you automap List or float[] with Fluent NHibernate?.
I've provided a lot of sample code - you should be able to substitute "string" for "float", and get it working. Note the gotchas in the explanatory text.