I try to write a (fluent) mapping against an interface
public interface IOrderDiscount : IDomainObject<long>
where
public interface IDomainObject<IdT> : IDomainObject
{
IdT Id { get; }
}
like so (and all other thinkable varieties of access strategies)
Id(d => d.Id, "DiscountId")
.GeneratedBy.HiLo("9")
.WithUnsavedValue(0)
.Access.AsReadOnlyPropertyThroughCamelCaseField();
but all I get are variations of
Could not find field 'id' in class 'IOrderDiscount'
My base class implements this as
public virtual IdT Id { get; protected set; }
but event using a backing field does not change a thing.
So I am left to wonder, how I could get this to work...
Anyone with an idea?
Specify the custom column name via the Column method instead:
Id(d => d.Id)
.Column("DiscountId")
.GeneratedBy.HiLo("9")
.WithUnsavedValue(0)
.Access.AsReadOnlyPropertyThroughCamelCaseField();
Related
I'm discovering nhibernate right now. Thus my question is maybe very stupid :)
What I'm trying to do (I'm working with a legacy database) is to get an entity which some of its data are coming from a table value function.
My entity is the following
public class Entity
{
public virtual int Id { get; protected set; }
....
public virtual int AccessRightId { get; set; }
}
where AccessRightId comes from the table value function (fp_AccessRight('userId'))
I have the following mapping
public class EntityMap : ClassMap<Entity>
{
public EntityMap ()
{
this.Id(entity => entity .Id);
this.Join(
"fp_AccessRight('userId')",
join =>
{
join.Fetch.Join();
join.KeyColumn("EntityId");
join.Map(t => t.AccessRightId, "AccessRightType");
join.Table();
});
}
}
Unfortunately, I'm not able to substitute 'userId' by any value.
Thanks.
Is there a way to do it?
I finally solved it.
The trick was to make the join like this :
this.Join("fp_ACCOUNT_ACL(:AclFilter.userId)"
and then simpley to enable the filter
this.session.EnableFilter("AclFilter").SetParameter("userId", "bdd#5");
I found an example here.
I have a class that has an enum type indicating whether the message type is Email or Sms. The enum type is defined:
public enum ReminderType
{
Email = 1,
Sms = 2
}
The class that utilizes this type looks like:
public class Reminder : EntityBase
{
public virtual string Origin { get; set; }
public virtual string Recipient { get; set; }
public virtual ReminderType Type { get; set; }
public virtual Business Business { get; set; }
public virtual DateTime Created { get; set; }
public Reminder()
{
Created = DateTime.UtcNow;
}
}
When I try to persist an entity of type Reminder to the database however, I get the following error:
System.Data.SqlClient.SqlException (0x80131904): Conversion failed when converting the nvarchar value 'Email' to data type int.
The backing field is of type int, so I'm not sure why NHibernate is trying to map the string representation by default. I'm using Fluent NHibernate, and the relevant mapping code is:
mappings.Override<Reminder>(map =>
{
map.Map(x => x.Type).Column("Type")
});
I'm pretty sure the default behavior of NHibernate is to map enums as ints, so why is it not doing so in this case? I'm using SQL Server 2005, if that matters.
I am doing the same thing and got it working like so...
In my case EmployeeType is the enum class
Map(x => x.EmployeeType, "EmployeeType_Id").CustomType(typeof (EmployeeType));
I don't know why this person keeps posting and then deleting their comment or answer, but the link they provided () does answer my question. I opted not to go with a full blow class definition for the convention, but rather, an inline convention in the mappings code, like so:
var mappings = AutoMap.AssemblyOf<Business>()
.Where(x => x.IsSubclassOf(typeof(EntityBase)))
.IgnoreBase(typeof(EntityBase))
.Conventions.Add
(
ConventionBuilder.Id.Always(x => x.GeneratedBy.Identity()),
ConventionBuilder.HasMany.Always(x => x.Cascade.All()),
ConventionBuilder.Property.Always(x => x.Column(x.Property.Name)),
Table.Is(o => Inflector.Pluralize(o.EntityType.Name)),
PrimaryKey.Name.Is(o => "Id"),
ForeignKey.EndsWith("Id"),
DefaultLazy.Always(),
DefaultCascade.All(),
ConventionBuilder.Property.When(
c => c.Expect(x => x.Property.PropertyType.IsEnum),
x => x.CustomType(x.Property.PropertyType))
);
The last convention builder statement did the trick. I'm curious as to why Fluent NHibernate's default is to map enums as strings now. That doesn't seem to make much sense.
You should never map Enum as int in NHibernate. It becomes a reason of having a ghost updates.
The best way to it is just not setting a type property in XML mappings. To achieve that in Fluent NHibernate you can use .CustomType(string.Empty).
Some additional info you can find here.
I have a situation where my primary key is a char(2) in SqlServer 2008, and I want to reference it in a one-to-many relationship, but the ManyToOneBuilder (which is returned by ClassMap<>.References()) doesn't have a CustomSqlType() method. Specifically:
public class State
{
// state FIPS code is 2 characters
public virtual string StateCode { get; set; }
public virtual ICollection<County> { get; set; }
}
public class County
{
// state-county FIPS code is 5 characters
public virtual string StateCountyCode { get; set; }
public virtual State State { get; set; }
}
public class StateMap : ClassMap<State>
{
public StateMap()
{
Id(e => e.StateCode).CustomSqlType("char(2)").GeneratedBy.Assigned();
}
}
public class CountyMap : ClassMap<County>
{
public CountyMap()
{
Id(e => e.StateCountyCode).CustomSqlType("char(5)").GeneratedBy.Assigned();
References(e => e.State, "StateCode")
// Here's what I want to do, but can't because the method is not
// implemented on the class ManyToOneBuilder:
.CustomSqlType("char(2)");
}
}
Is there any way to accomplish this without modifying the ManyToOneBuilder? Is there a way to automatically map the FK (i.e. County.StateCode) to the correct type? It's trivial to add CustomSqlType to ManyToOneBuilder, but is that the right thing to do?
Keep your mapping definition as is, add your "State" column definition with
.CustomSqlType("char(2)")
and set for this column Insert=false and update=false.
I've the same problem and in AutoMapping I use this code:
mapping.Map(x => x.IdUniArticolo)
.CustomSqlType("varchar(50)")
.Not.Insert().Not.Update();
mapping.References(x => x.Articolo)
.Column("IdUniArticolo").PropertyRef("IdUniArticolo");
Keep in mind that if NHibernate itself doesn't support it, then Fluent NHibernate can't, and I don't NHibernate supports the scenario you have. I had a similar problem in that I had a 2 column composite key on a table and on one of the fields, I wanted to use an enumerated type which had a custom IUserType to translate it to its appropriate code value in the DB. Couldn't do it, so I was stuck keeping the property of the string type rather than the enumerated type.
I have a scenario where I have a base class as one entity, then another entity that derives from the other base class. Both have meaning in my domain and can be used separately.
public class MyBaseClass
{
int ID { get; set; }
string Name { get; set; }
}
public class MyChildClass
{
string AdditionalField { get; set; }
}
I have both mapped using Fluent nHibernate using ClassMap like this:
public class MyBaseClassMap : ClassMap<MyBaseClass>
{
Id("MyBaseClassID");
Map(x => x.Name);
}
public class MyChildClassMap : SubclassMap<MyChildClass>
{
Map(x => x.AdditionalField);
}
What is happening is when I try to fetch a copy of the base class, its using the mapping for the child class. Its as if it doesn't know the the difference between the base and child class, or its choosing the wrong mapping for it. I confirmed this by watching the SQL statement and its joining to the child table and fetching the additional column. Any way to get it to use the right map?
That's the 'nature' of NHibernate.
The behaviour you're describing, is called 'polymorphic queries'.
Since MyChildClass is a MyBaseClass, the MyChildClass instances are retrieved as well.
If you want to avoid this behaviour, you can maybe have a look at the answers in this topic. (I've never 'disabled' the polymorphic query ability).
I'm using two class NiceCustomer & RoughCustomer which implment the interface ICustomer.
The ICustomer has four properties. They are:
Property Id() As Integer
Property Name() As String
Property IsNiceCustomer() As Boolean
ReadOnly Property AddressFullText() As String
I don't know how to map the interface ICustomer, to the database.
I get an error like this in the inner exception.
An association refers to an unmapped class: ICustomer
I'm using Fluent and NHibernate.
You can map directly to interfaces in NHibernate, by plugging in an EmptyInterceptor during the configuration stage. The job of this interceptor would be to provide implementations to the interfaces you are defining in your mapping files.
public class ProxyInterceptor : EmptyInterceptor
{
public ProxyInterceptor(ITypeHandler typeHandler) {
// TypeHandler is a custom class that defines all Interface/Poco relationships
// Should be written to match your system
}
// Swaps Interfaces for Implementations
public override object Instantiate(string clazz, EntityMode entityMode, object id)
{
var handler = TypeHandler.GetByInterface(clazz);
if (handler == null || !handler.Interface.IsInterface) return base.Instantiate(clazz, entityMode, id);
var poco = handler.Poco;
if (poco == null) return base.Instantiate(clazz, entityMode, id);
// Return Poco for Interface
var instance = FormatterServices.GetUninitializedObject(poco);
SessionFactory.GetClassMetadata(clazz).SetIdentifier(instance, id, entityMode);
return instance;
}
}
After this, all relationships and mappings can be defined as interfaces.
public Parent : IParent {
public int ID { get; set; }
public string Name { get; set; }
public IChild Child { get; set; }
}
public Child : IChild {
public int ID { get; set; }
public string Name { get; set; }
}
public class ParentMap : ClassMap<IParent>
{
public ParentMap()
{
Id(x => x.ID).GeneratedBy.Identity().UnsavedValue(0);
Map(x => x.Name)
}
}
...
This type of technique is great if you want to achieve true decoupling of your ORM, placing all configuration/mappings in a seperate project and only referencing interfaces. Your domain layer is then not being polluted with ORM, and you can then replace it at a later stage if you need to.
how are you querying? If you're using HQL you need to import the interface's namespace with an HBM file with this line:
<import class="name.space.ICustomer, Customers" />
If you're using Criteria you should just be able to query for ICustomer and it'll return both customer types.
If you're mapping a class that has a customer on it either through a HasMany, HasManyToMany or References then you need to use the generic form:
References<NiceCustomer>(f=>f.Customer)
If you want it to cope with either, you'll need to make them subclasses
Subclassmap<NiceCustomer>
In which case I think you'll need the base class Customer and use that for the generic type parameter in the outer class:
References<Customer>(f=>f.Customer)
Regardless, you shouldn't change your domain model to cope with this, it should still have an ICustomer on the outer class.
I'm not sure if the 1.0RTM has the Generic form working for References but a quick scan of the changes should show the change, which I think is a two line addition.
It is not possible to map an interface in nhibernate. If your goal is to be able to query using a common type to retrieve both types of customers you can use a polymorphic query. Simply have both your classes implement the interface and map the classes normally. See this reference:
https://www.hibernate.org/hib_docs/nhibernate/html/queryhql.html (section 11.6)