Overwriting bool property convention in fluent NHHibernate - fluent-nhibernate

I am experimenting with the automap feature of Fluent Nhibernate..
I saw an example which had a default string convention as follows:
namespace Vuscode.Framework.NHibernate.Conventions
{
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Instances;
public class DefaultStringPropertyConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Length(100);
instance.Nullable();
}
}
}
Here, I see nothing that checks if the instance is of string type.. it just makes the length 100 and nullable.. How does Automapper knows to apply this convention only to strings?
Furthermore, I'd like to make all my bools non nullable and set a default value of 0.. so Following the above example, I'd have this:
namespace Vuscode.Framework.NHibernate.Conventions
{
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Instances;
public class DefaultBoolPropertyConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
instance.Default("0");
}
}
}
But if I do that and have default string convention as well in my project.. then how does automapper know where which property types the convention is applied to?
I tried reading the fluent wiki on conventions but did not find anything useful there..

the stringconvention is applied to all properties but since NHibernate ignores length for non string properties and nullable is the default it just has no effect. You have two options to filter non bool properties
public void Apply(IPropertyInstance instance)
{
if (instance.Type == typeof(bool))
{
instance.Not.Nullable();
instance.Default("0");
}
}
// or
public class DefaultBoolPropertyConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(i => i.Type == typeof(bool));
}
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
instance.Default("0");
}
}

Related

SaveOrUpdate is trying to insert NULL into primary key field

I'm creating a little application using Sharp Architecture, and I've run into a bug I can't figure out. I think it has something to do with the NHibernte mappings. In my HttpPost Create() method, my SaveOrUpdate call is trying to insert null into the table's primary key field. The declaration for the primary key in my model is public virtual int Id { get; protected set; }.
I checked newSprint.Id and it's zero. I think the problem's with my NHibernate Mappings, so I've included all of that below.
Here's the automapping configuration:
public class AutomappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(System.Type type)
{
return type.GetInterfaces().Any(x =>
x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEntityWithTypedId<>));
}
public override bool ShouldMap(Member member)
{
return base.ShouldMap(member) && member.CanWrite;
}
public override bool AbstractClassIsLayerSupertype(System.Type type)
{
return type == typeof(EntityWithTypedId<>) || type == typeof(Entity);
}
public override bool IsId(Member member)
{
return member.Name == "Id";
}
}
The auto-persistence model generator:
public class AutoPersistenceModelGenerator : IAutoPersistenceModelGenerator
{
public AutoPersistenceModel Generate()
{
var mappings = AutoMap.AssemblyOf<Sprint>(new AutomappingConfiguration());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.Conventions.Setup(GetConventions());
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
return mappings;
}
private static Action<IConventionFinder> GetConventions()
{
return c =>
{
c.Add<PrimaryKeyConvention>();
c.Add<CustomForeignKeyConvention>();
c.Add<HasManyConvention>();
c.Add<TableNameConvention>();
};
}
Thanks in advance for any help anyone can offer.
Edit
I figured out that the problem was with the Table Name Convention. Removing that from the AutoMapping configuration solved the problem. I've removed the extraneous code and added the TableNameConvention mapping in the hopes that someone can explain what specifically about it caused those problems.
public class TableNameConvention : IClassConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
}
Your IdGenerator is probably not right. You need to set an appropriate generator for your PrimaryKey. For example, Identity if you're using SQL Server.
I'm not sure how you set this with your setup, not very familiar with Fluent NH. My guess is PrimaryKeyConvention?

Fluent NHibernate - automapping: allow null for single properties

I know this question has been raised in similar form multiple times, but none of the threads could give me the concrete answer to my question.
I use Fluent NHibernate and Fluent`s auto-mapping to map my domain entities. Right now, I use this convention class to set all properties NOT NULL:
public class NotNullColumnConvention : IPropertyConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
instance.Not.Nullable();
}
}
The big question is:
What do I need to do, to allow single properties of my entity classes to be NULL?
Here is one of my entity classes:
public class Employee : Entity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
I´d be really pleased, if someone can finally help me out! All possible search string I have entered into Google return pages, marked as already visited...
Thanks,
Arne
EDIT: Changed title ... Want to allow NULL for single properties
Create an attribute :
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CanBeNullAttribute : Attribute
{
}
And a convention :
public class CanBeNullPropertyConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(
x => !this.IsNullableProperty(x)
|| x.Property.MemberInfo.GetCustomAttributes(typeof(CanBeNullAttribute), true).Length > 0);
}
public void Apply(IPropertyInstance instance)
{
instance.Nullable();
}
private bool IsNullableProperty(IExposedThroughPropertyInspector target)
{
var type = target.Property.PropertyType;
return type.Equals(typeof(string)) || (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
}
}
Drop the attribute on top of your properties.

fluent nhibernate convention : setting polymorphism mode

Is it possible to create a simple convention to modify the polymorphism mode of a class, if there is a joined-subclass ?
Doing this :
public class EntityMap : ClassMap<EntityBase>
{
public EntityMap()
{
Polymorphism.Explicit();
}
}
but inside a convention. Using IClassConvention doesn't work, as the Polymorphism property is read only :
public class TestConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
// read only property !
instance.Polymorphism = Polymorphism.Explicit;
}
}
Try
instance.Polymorphism.Explicit();

Fluent Nhibernate Automap convention for not-null field

Could some one help, how would I instruct automap to have not-null for
a column?
public class Paper : Entity
{
public Paper() { }
[DomainSignature]
[NotNull, NotEmpty]
public virtual string ReferenceNumber { get; set; }
[NotNull]
public virtual Int32 SessionWeek { get; set; }
}
But I am getting the following:
<column name="SessionWeek"/>
I know it can be done using fluent-map. but i would like to know it in
auto-mapping way.
Thank you. Also, for reference properties ReferenceConvention need to be done. This is the code that works:
public class ColumnNullConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Property.MemberInfo.IsDefined(typeof(NotNullAttribute), false))
instance.Not.Nullable();
}
} public class ReferenceConvention : IReferenceConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IManyToOneInstance instance)
{
instance.Column(instance.Property.Name + "Fk");
if (instance.Property.MemberInfo.IsDefined(typeof(NotNullAttribute), false))
instance.Not.Nullable();
}
}
Here is the way I do it, basically taken from the link you see in the code. There are some other useful conventions there as well
HTH,
Berryl
/// <summary>
/// If nullability for the column has not been specified explicitly to allow NULL, then set to “NOT NULL”.
/// </summary>
/// <remarks>see http://marcinobel.com/index.php/fluent-nhibernate-conventions-examples/</remarks>
public class ColumnNullabilityConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Nullable, Is.Not.Set);
}
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
}
}
If you are mostly happy with Automapping results but occasionally need to override it for say a couple of properties in a class I find implementing a IAutoMappingOverride for that class the easiest way to achieve that:
public class UserMappingOverride : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.Map(x => x.UserName).Column("User").Length(100).Not.Nullable();
}
}
And then use them like this:
AutoMap.AssemblyOf<User>().UseOverridesFromAssemblyOf<UserMappingOverride>();
Similar to ClassMaps - but you don't need to describe every field in the class.
This approach is very similar to the Entity Framework's Code First Fluent API way.
public class Paper Map : IAutoMappingOverride<Paper >
{
public void Override(AutoMapping<Paper> mapping)
{
mapping.Map(x => x.ReferenceNumber).Not.Nullable();
}
}
Int32 is not nullable type by default. Int32? is nullable, so you make it non-nullable just by specifying it as Int32.
You can use conventions to do this automatically. I am not sure which convention to use, but have a look at FluentNHibernate.Conventions.Instances to find the right one. It'll look like this.
public class ColumnConvention : IColumnConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.ColumnInstance instance)
{
if (instance.EntityType.IsDefined(typeof(NotNullAttribute), false))
instance.NotNull = true;
}
public void Apply(FluentNHibernate.Conventions.Instances.IColumnInstance instance)
{
return;
}
}
Just add this convention to your automapping.
I find more often than not, my columns are not null, so I prefer make this convention and only specify columns as nullable:
/// <summary>
/// Indicates that a column should allow nulls
/// </summary>
[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class NullableAttribute : Attribute
{
}
public class ColumnIsNotNullByDefaultConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
}
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(c => !c.Property.MemberInfo.IsDefined(typeof(NullableAttribute), false));
}
}

How do you map an enum as an int value with fluent NHibernate?

Question says it all really, the default is for it to map as a string but I need it to map as an int.
I'm currently using PersistenceModel for setting my conventions if that makes any difference.
Update
Found that getting onto the latest version of the code from the trunk resolved my woes.
The way to define this convention changed sometimes ago, it's now :
public class EnumConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
public void Apply(IPropertyInstance target)
{
target.CustomType(target.Property.PropertyType);
}
}
So, as mentioned, getting the latest version of Fluent NHibernate off the trunk got me to where I needed to be. An example mapping for an enum with the latest code is:
Map(quote => quote.Status).CustomTypeIs(typeof(QuoteStatus));
The custom type forces it to be handled as an instance of the enum rather than using the GenericEnumMapper<TEnum>.
I'm actually considering submitting a patch to be able to change between a enum mapper that persists a string and one that persists an int as that seems like something you should be able to set as a convention.
This popped up on my recent activity and things have changed in the newer versions of Fluent NHibernate to make this easier.
To make all enums be mapped as integers you can now create a convention like so:
public class EnumConvention : IUserTypeConvention
{
public bool Accept(IProperty target)
{
return target.PropertyType.IsEnum;
}
public void Apply(IProperty target)
{
target.CustomTypeIs(target.PropertyType);
}
public bool Accept(Type type)
{
return type.IsEnum;
}
}
Then your mapping only has to be:
Map(quote => quote.Status);
You add the convention to your Fluent NHibernate mapping like so;
Fluently.Configure(nHibConfig)
.Mappings(mappingConfiguration =>
{
mappingConfiguration.FluentMappings
.ConventionDiscovery.AddFromAssemblyOf<EnumConvention>();
})
./* other configuration */
Don't forget about nullable enums (like ExampleEnum? ExampleProperty)! They need to be checked separately. This is how it's done with the new FNH style configuration:
public class EnumConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum ||
(x.Property.PropertyType.IsGenericType &&
x.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
x.Property.PropertyType.GetGenericArguments()[0].IsEnum)
);
}
public void Apply(IPropertyInstance target)
{
target.CustomType(target.Property.PropertyType);
}
}
this is how I've mapped a enum property with an int value:
Map(x => x.Status).CustomType(typeof(Int32));
works for me!
For those using Fluent NHibernate with Automapping (and potentially an IoC container):
The IUserTypeConvention is as #Julien's answer above: https://stackoverflow.com/a/1706462/878612
public class EnumConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
public void Apply(IPropertyInstance target)
{
target.CustomType(target.Property.PropertyType);
}
}
The Fluent NHibernate Automapping configuration could be configured like this:
protected virtual ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(SetupDatabase)
.Mappings(mappingConfiguration =>
{
mappingConfiguration.AutoMappings
.Add(CreateAutomappings);
}
).BuildSessionFactory();
}
protected virtual IPersistenceConfigurer SetupDatabase()
{
return MsSqlConfiguration.MsSql2008.UseOuterJoin()
.ConnectionString(x =>
x.FromConnectionStringWithKey("AppDatabase")) // In Web.config
.ShowSql();
}
protected static AutoPersistenceModel CreateAutomappings()
{
return AutoMap.AssemblyOf<ClassInAnAssemblyToBeMapped>(
new EntityAutomapConfiguration())
.Conventions.Setup(c =>
{
// Other IUserTypeConvention classes here
c.Add<EnumConvention>();
});
}
*Then the CreateSessionFactory can be utilized in an IoC such as Castle Windsor (using a PersistenceFacility and installer) easily. *
Kernel.Register(
Component.For<ISessionFactory>()
.UsingFactoryMethod(() => CreateSessionFactory()),
Component.For<ISession>()
.UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
.LifestylePerWebRequest()
);
You could create an NHibernate IUserType, and specify it using CustomTypeIs<T>() on the property map.
You should keep the values as int / tinyint in your DB Table. For mapping your enum you need to specify mapping correctly. Please see below mapping and enum sample,
Mapping Class
public class TransactionMap : ClassMap Transaction
{
public TransactionMap()
{
//Other mappings
.....
//Mapping for enum
Map(x => x.Status, "Status").CustomType();
Table("Transaction");
}
}
Enum
public enum TransactionStatus
{
Waiting = 1,
Processed = 2,
RolledBack = 3,
Blocked = 4,
Refunded = 5,
AlreadyProcessed = 6,
}