NHibernate Mapping for string Foreign Key with specific type - nhibernate

I'm trying to map a string foreign key in NHibernate. This works but the field is a varchar(10). This is causing performance problems and the script is timing out because NH is using a nvarchar(4000). When we change the variable type in SSMS to a varchar(10) the query takes under a second.
I have the following setup in NHibernate:
public class Product
{
ProductCode:string;
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(p=>p.ProductCode);
}
}
public class Holding
{
public long HoldingID {get;set;
public Product Prod {get;set;}
public decimal SomeValue {get;set;}
}
public class HoldingMap
{
public HoldingMap()
{
Id(h=>h.HoldingID);
References(h=>h.Product, "ProductCode");
Map(h=>h.SomeValue);
}
}
I've tried changing the Id(p=>p.ProductCode); so that it is better typed:
Id(c => c.Code, "portfolio_code").CustomSqlType("varchar(10)");
but that doesn't seem to change anything. Does anyone know how to have NH generate the query with a properly typed parameter?
Thanks

#David Osbourne was correct, this did answer my underlying question: Always encrypted mapping in NHibernate but I'll summarise here.
NHibernate will always use the maximum field size for a parameter to allow for wildcards so it's a bit risky to force field length. If you do need to you can see the answer in the driver in the linked answer.
My problem was actually caused by NH using a NVARCHAR on a VARCHAR field. By changing the column to being and AnsiString I dropped the time taken from 40 seconds to sub-second.

Related

Fluid NHibernate, Custom Types and Id mapping

I have an object in C# that I want to use as a primary key in a database that auto-increments when new objects are added. The object is basically a wrapper of a ulong value that uses some bits of the value for additional hints. I want to store it as a 'pure' ulong value in a database but I would like get an automatic conversion when the value is loaded / unloaded from DB. IE, apply the 'hint' bits to the value based on the table they come from.
I went on a journey of implementing my own IUserType object based on number of examples I found online ( tons of help on this forum ).
I have an ObjectId class that acts is an object ID
class ObjectIdType: IUserType
{
private static readonly NHibernate.SqlTypes.SqlType[] SQL_TYPES = { NHibernateUtil.UInt64.SqlType };
public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get { return SQL_TYPES; }
}
public Type ReturnedType
{
get { return typeof(ObjectId); }
}
...
}
I have a mapping class that looks like this:
public class ObjectTableMap()
{
Id(x => x.Id)
.Column("instance_id")
.CustomType<ObjectIdType>()
.GeneratedBy.Native();
}
At this point I get an exception at config that Id can only be an integer. I guess that makes sense but I was half expecting that having the custom type implemented, the native ulong database type would take over and work.
I've tried to go down the path of creating a custom generator but its still a bit out of my skill level so I am stumbling though it.
My question is, is it possible for me to accomplish what I am trying to do with the mapping?
I think, it is not possible, because your mapping uses the native generator for the Id. This can only be used for integral types (and GUIDs). You can try to use assigned Ids with your custom type, so you are responsible for assigning the values to your Id property.
There is another alternative: Why not set your information bits on class level, instead depending on your table? Your entities represent the tables, so you should have the same information in your entity classes. Example:
class Entity
{
protected virtual ulong InternalId { get; set; } // Mapped as Id
public virtual ulong Id // This property is not mapped
{
get
{
var retVal = InternalId;
// Flip your hint bits here based on class information
return retVal;
}
}
}
You could also turn InternalId into a public property and make the setter protected.

Invalid index n for this SqlParameterCollection with Count=m

I'm getting this nasty error in Castle Active Record (wrapped around NHibernate) when I try to save a class:
Invalid index n for this SqlParameterCollection with Count=m
I know that this error is caused by a property being mapped multiple times in a class however I'm not sure how to get around it. I have two child classes that both map back to the class in question using the same column (IpAddressNumber). Also IpAddressNumber is the primary key of the class, which results in NHibernate trying to map the IpAddressNumber property three times (just a guess.)
Here is the class:
[ActiveRecord(Lazy=true)]
public class DeviceConfiguration : UsersDatabase<DeviceConfiguration>
{
[PrimaryKey]
public virtual long IPAddressNumber { get; set; }
[BelongsTo("IPAddressNumber", Lazy = FetchWhen.OnInvoke)]
public virtual Vehicle Vehicle { get; set; }
[BelongsTo("IPAddressNumber", Lazy = FetchWhen.OnInvoke)]
public virtual JBusConfiguration JBusConfiguration { get; set; }
}
Any help would be greatly appreciated...
I ended up having to just remove the second association altogether to get around this issue. Not a great solution but the only one I could find.

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.

Fluent NHibernate - mapping an Entity as a different type

I have a class which I would like to map as a component onto any table which contains it:
public class Time
{
public int Hours { get; set; }
public int Minutes { get; set; }
public int Seconds { get; set; }
}
I would like to store this class as a bigint in the database - the same as how TimeSpan is stored but my class has completely different behaviour so I decided to create my own.
I'm using FLH's automapper and have this class set as a component (other classes have Time as a property). I've got as far as creating an override but am not sure how to go about mapping it:
I gave it a try this way:
public class TimeMappingOverride : IAutoMappingOverride<Time>
{
public void Override(AutoMapping<Time> mapping)
{
mapping.Map(x => x.ToTimeSpan());
mapping.IgnoreProperty(x => x.Hours);
mapping.IgnoreProperty(x => x.Minutes);
mapping.IgnoreProperty(x => x.Seconds);
}
}
But got this error:
Unable to cast object of type 'System.Linq.Expressions.UnaryExpression' to type 'System.Linq.Expressions.MethodCallExpression'.
How should I go about this?
Details of components can be found here: http://wiki.fluentnhibernate.org/Fluent_mapping#Components
But first of all, you can't map a method.
Assuming you change ToTimeSpan() to a property AsTimeSpan, there are two ways to do it, only the harder of which will work for you because you are using automapping:
Create a ComponentMap<Time> -- once done, your existing mapping will just work. This is not compatible with automapping.
Declare the component mapping inline:
mapping.Component(x => x.AsTimeSpan, component => {
component.Map(Hours);
component.Map(Minutes);
component.Map(Seconds);
});
You'll have to do this every time, though.
Of course, this doesn't address "I would like to store this class as bigint…"
Are you saying you want to persist it as seconds only? If so, scratch everything at the top and again you have two options:
Implement NHibernate IUserType (ugh)
Create a private property or field that stores the value as seconds only, and wire only this up to NHibernate. The getters and setters of the pubic properties will have to convert to/from seconds.
I personally haven't worked with AutoMappings yet, but my suggestion would be to look into NHibernate's IUserType to change how a type is being persisted. I believe that's a cleaner way of defining your custom mapping of Time <-> bigint.
Reading the code above, Map(x => x.ToTimeSpan()) will not work as you cannot embed application-to-database transformation code into your mappings. Even if that would be possible, the declaration misses the transformation from the database to the application. A IUserType, on the other hand, can do custom transformations in the NullSafeGet and NullSafeSet methods.

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.