How to map part of aspnet_Users table using Fluent NHibernate - fluent-nhibernate

I'm trying to get Fluent NHibernate 1.0 RTM to map a User entity for me so that I have access to UserId and UserName inside my ASP.NET MVC application via NHibernate.
I have:
public class User
{
public virtual Guid UserId { get; protected set; }
public virtual string UserName { get; protected set; }
}
It represents the aspnet_Users table with only the relevant columns to be mapped. This is the only entity that is not being automapped. Here is my mapping:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.UserId);
Map(x => x.UserName);
WithTable("aspnet_Users");
}
}
Everything else is getting automapped with conventions.
Here are my PrimaryKeyConvention and TableNameConvention:
public class PrimaryKeyConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.Column(instance.Property.ReflectedType.Name + "Id");
instance.UnsavedValue(System.Guid.Empty.ToString());
instance.GeneratedBy.GuidComb();
}
}
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
}
The Mapping process fails right after executing the ClassMap code (which comes before all automapping), followed by the TableNameConvention code, followed by the PrimaryKeyConvention code. The failure is in PrimaryKeyConvention because instance.Property is null. I tried to do an if(instance.Property != null) but that
terminates the mapping process early with a "the required attribute 'class' is missing" error. I also had an if (instance.EntityType != typeof(User)) in the TableNameConvention, but took out when it was making no difference.
What is going on here? First of all, why is the AutoMapping processes calling the conventions for the ClassMap? Second, why is the PrimaryKenConvention getting passed an instance.Property == null? How can I get this to work so that the mapping process moves on and maps the rest of my entities using AutoMapping + conventions?
Note, I had this all working for months under an earlier version of FNH prior to the refactor for 1.0 RTM.

I've figured out the answer to this.
public UserMap()
{
Id(x => x.UserId);
Map(x => x.UserName);
Table("aspnet_Users");
}
public class PrimaryKeyConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Id");
instance.UnsavedValue(System.Guid.Empty.ToString());
instance.GeneratedBy.GuidComb();
}
}
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
}
Note that the PrimaryKeyConvention sets the Column name using instance.EntityName rather that instance.Property. The latter was null for the UserMap, so it would crash.
This approach is better than using a conditional statement on (null != instance.Property) and keeping the instance.Property.ReflectedType.Name line, but both work. If you choose to go that route, you have to explicitly set the Column names in the UserMap:
public UserMap()
{
Id(x => x.UserId).Column("UserId")
.GeneratedBy.Assigned();
Map(x => x.UserName).Column("UserName");
Table("aspnet_Users");
}

I don't use the auto-mapping myself, but I think you need to map User by implementing IAutoMappingOverride. Something like:
public class UserMap : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.Id(x => x.UserId);
mapping.Map(x => x.UserName);
mapping.WithTable("aspnet_Users");
}
}

Related

Fluent Nhibernate Enum Mapping

I have some problem with enum mapping in fluent NHibernate. I know this question has been asked many times but I couldn't find any solution that worked for me. I'm newbie in NHibernate and it looks like I may have missed something simple and stupid. Here is my code.
public class DBPublication
{
public virtual int pub_id { get; set; }
public virtual PublicationStatuses status { get; set; }
...
}
public enum PublicationStatuses
{
on_moderation,
active,
...
}
public class DBPublicationMap : ClassMap<DBPublication>
{
public DBPublicationMap()
{
Table("content.publications");
Id(x => x.pub_id).GeneratedBy.Sequence("content.pub_sq");
Map(x => x.status);
...
}
}
postgres enum type
CREATE TYPE content.enum_publication_status AS ENUM('on_moderation', 'active', ...);
but when I try to save, postgres throws this
column "status" is of type content.enum_publication_status but expression is of type text
any suggestion?
Here is a working sample of configuring nhibernate to store enum field.
public class Entity
{
public virtual int id { get; set; }
public virtual SomeEnum EnumField { get; set; }
}
public enum SomeEnum
{
Value1,
Value2
}
class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Id(x => x.id).GeneratedBy.Native();
Map(x => x.EnumField);
}
}
class Program
{
static void Main(string[] args)
{
var factory = Fluently.Configure().Mappings(x => x.FluentMappings.AddFromAssemblyOf<Entity>())
.ExposeConfiguration(config => new SchemaExport(config).Create(false, true))
.Database(MsSqlConfiguration.MsSql2008.ConnectionString("Data Source=.;Initial Catalog=nhtest;Integrated Security=True"))
.BuildSessionFactory();
using (var session = factory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var entity = new Entity();
entity.EnumField = SomeEnum.Value2;
session.Save(entity);
transaction.Commit();
}
}
}
}
In such case it is stored as strings in data base. If you want it be saved as integers, you need to change mapping for Enum field property to the following:
Map(x => x.EnumField).CustomType<int>();
Why are you creating enum types in progres? isn't this a maintenance-nightmare?
What does your configuration look like? Have you already tried using the conventions discribed here?
Looks like a simple casting error, so please consider using CustomType() / CustomSQLType() inside your mapping.
Simply add this class to your project:
public class PgEnumMapper<T> : NHibernate.Type.EnumStringType<T>
{
public override NHibernate.SqlTypes.SqlType SqlType
{
get { return new NHibernate.SqlTypes.SqlType(System.Data.DbType.Object); }
}
}
Then, you can use:
Map(x => x.status).CustomType<PgEnumMapper<PublicationStatuses>>();

Fluent NHibernate without Automapping SQL Exception, Incorrect syntax near the keyword 'User'

Just starting in NHibernate and to my eye Everything seems correct but obviously is not. When I ren unit tests shown below I receive back that there is a syntax error near the keyword "User"
here is my user class:
namespace Users
{
public class User
{
public virtual string firstName { get; set; }
public virtual string lastName { get; set; }
public virtual int Id { get; set; }
}
}
and the User mapping(Also ran without square brackets around column names with identical results:
namespace Users
{
class UserMap: ClassMap<User>
{
UserMap()
{
Table("User");
Id(x => x.Id).GeneratedBy.Native().Column("[Id]").Not.Nullable();
Map(x => x.firstName).Not.Nullable().Column("[firstName]");
Map(x => x.lastName).Not.Nullable().Column("[lastName]");
}
}
}
The Config file named Framework.cs
namespace Users
{
public class Framework
{
private const string ConnectionStr = "Data Source=ERALCH-ESTEPHEN;Initial
Catalog=NHDev;Integrated Security=True;Pooling=False";
public static ISessionFactory CreateFactory()
{
return Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration
.MsSql2008
.ConnectionString(ConnectionStr))
.Mappings(x=>x.FluentMappings.AddFromAssemblyOf<User>())
.BuildSessionFactory();
}
}
}
The Data Access Layer-- simply retrieves a user by Id
namespace Users
{
public class Accesslayer
{
public static IList<User> GetUserById(int Id)
{
ISessionFactory provider = Framework.CreateFactory();
using (ISession session = provider.OpenSession())
{
return session.CreateSQLQuery(String
.Format("SELECT * FROM User WHERE Id={0}", Id)).List<User>();
}
}
}
}
and finally the unit test layer
namespace Users
{
[TestFixture]
class AccessLayerTest
{
[Test]
public void CanGetUserById()
{
Assert.AreEqual(1, Accesslayer.GetUserById(1).Count());
}
}
}
The Database is MSsql with one table "User" with columns matching the user properties. Any help would be appreciated thanks
You should be able to do this in your configuration:
var factory = Fluently.Configure()
// ...
.ExposeConfiguration(c => SchemaMetadataUpdater.QuoteTableAndColumns(c))
.BuildSessionFactory();
Which should escape your table and column names for you automagically.
Did you try putting backticks around your User table name ?
namespace Users
{
class UserMap: ClassMap<User>
{
UserMap()
{
Table("`User`");
Id(x => x.Id).GeneratedBy.Native().Column("`Id`").Not.Nullable();
Map(x => x.firstName).Not.Nullable().Column("`firstName`");
Map(x => x.lastName).Not.Nullable().Column("`lastName`");
}
}
}
See this answer for more details : NHibernate - Force escaping on Table Names
Also, you should use NHibernate querying facilities instead of SQL :
namespace Users
{
public class Accesslayer
{
public static IList<User> GetUserById(int Id)
{
ISessionFactory provider = Framework.CreateFactory();
using (ISession session = provider.OpenSession())
{
return session.Query<User>().Where(x => x.Id == Id ).List<User>();
}
}
}
}
Have a look at this tutorial : http://www.d80.co.uk/post/2011/02/20/Linq-to-NHibernate-Tutorial.aspx
You should need the [Brackets] while mapping. Heck, if they have the same name, both of these would work the same:
public class UserMap: ClassMap<User>
{
UserMap()
{
Table("User");
Id(x => x.Id).GeneratedBy.Native().Not.Nullable();
Map(x => x.firstName).Not.Nullable();
Map(x => x.lastName).Not.Nullable();
}
}
public class UserMap: ClassMap<User>
{
UserMap()
{
Table("User");
Id(x => x.Id, "Id").GeneratedBy.Native().Not.Nullable();
Map(x => x.firstName, "firstName").Not.Nullable();
Map(x => x.lastName, "lastName").Not.Nullable();
}
}
Avoid naming your tables or columns using Reserved Keywords. Hibernate forms a SQL-Statement which won't be accepted by MS SQL. Had the same problem with NHibernate + Fluent + Automapper and solved it by renaming the "user"-column to "username". Don't know how other DBs handle it.
Further comments about this here.

"No persister for" using Fluently mapping of Subclass in Fluent NHibernate

I'm doing a very basic thing with Fluent NHibernate. I found a lot of people with similar problems here in SO but none seemed to fix my problem.
I have 1 Class like:
public abstract class ParentClass
{
public virtual long Id { get; private set; }
public virtual DateTime CreateDate { get; set; }
public virtual int Type { get; set; }
}
And 1 Concrete classes like:
public class ChildClass : ParentClass
{
public virtual string PropertyX { get; set; }
public virtual int PropertyY{ get; set; }
}
So I made the mapping as follows:
public class ParentMap : ClassMap<ParentClass>
{
public ParentMap()
{
Id(x => x.Id);
Map(x => x.CreateDate);
DiscriminateSubClassesOnColumn("Type");
}
}
And
public class ChildMap : SubclassMap<ChildClass>
{
public ChildMap()
{
Extends<ParentClass>();
DiscriminatorValue(1);
Map(x => x.PropertyX);
Map(x => x.PropertyY);
}
}
My legacy database has 2 tables, 1 that holds all the data from the ParentClass and another one that holds the data from the Child with a foreign key in the ID.
The idea is to have different tables for each different implementation of the ParentClass but having the ParentClass table as a single repository for "Ids" and "Create Dates".
I'm creating my SessionFactory as follows:
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlCeConfiguration.Standard.ConnectionString(cstr => cstr.FromConnectionStringWithKey("TheConnectionString")))
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentClass>()
.ExportTo(#"c:\temp\Mappings"))
.BuildSessionFactory();
}
I'm doing just a basic test of storing things to the database as:
public void Store(ParentClass parent)
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(parent);
transaction.Commit();
}
}
}
But despite the method waits for a ParentClass variable, I'm passing a ChildClass instance for it (the method is actually a inheritance of an interface, that's why it expects a ParentClass).
And every time I it raises an error on "SaveOrUpdate" method saying "No persister for: ChildClass".
What am I doing wrong?
ps.: Another strange thing is that even with the "ExportTo" method on the SessionFactory creation, no mapping is being write on the folder.
Change
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentClass>()
To
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentMap>()

Fluent NHibernate: How to Map List<String>

I know there are lots of questions about how to map List of strings using fluent nHibernate. I tried all the options I got. But still no luck.
My situation is as follows.
class BaseClass
{
public string Name {get;set;}
}
class FirstChild : BaseClass
{
public string Parameter{get;set;}
public IList<string> OtherParameter {get;set;}
}
The mapping file I am using is as follows:
public class BaseClassMap: ClassMap<BaseClass>
{
public BaseClassMap()
{
Table("BaseClass");
Map(x => x.Name);
DiscriminateSubClassesOnColumn<string>("Class");
}
}
public class FirstChildMap : SubclassMap<FirstChild>
{
public FirstChildMap ()
{
Map(x => x.Parameter);
HasMany(x => x.OtherParameter)
.Element("OtherParameter ")
.Table("OtherParameterTable").Cascade.AllDeleteOrphan();
}
}
After that when I try to save an object of type FirstChild, it saves the value of Name and Parameter but nothing goes into OtherParameterTable.
Can someone tell me where I am going wrong?

nhibernate mapping: A collection with cascade="all-delete-orphan" was no longer referenced

I am having some probs with my fluent mappings. I have an entity with a child collection of entities i.e Event and EventItems for example.
If I set my cascade mapping of the collection to AllDeleteOrphan I get the following error when saving a new entity to the DB:
NHibernate.HibernateException : A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: Core.Event.EventItems
If I set the cascade to All it works fine? Below are my classes and mapping files:
public class EventMap : ClassMap<Event>
{
public EventMap()
{
Id(x => x.Id, "Id")
.UnsavedValue("00000000-0000-0000-0000-000000000000")
.GeneratedBy.GuidComb();
Map(x => x.Name);
HasMany(x => x.EventItems)
.Inverse()
.KeyColumn("EventId")
.AsBag()
.Cascade.AllDeleteOrphan();
}
}
public class EventItemMap : SubclassMap<EventItem>
{
public EventItemMap()
{
Id(x => x.Id, "Id")
.UnsavedValue("00000000-0000-0000-0000-000000000000")
.GeneratedBy.GuidComb();
References(x => x.Event, "EventId");
}
}
public class Event : EntityBase
{
private IList<EventItem> _EventItems;
protected Event()
{
InitMembers();
}
public Event(string name)
: this()
{
Name = name;
}
private void InitMembers()
{
_EventItems = new List<EventItem>();
}
public virtual EventItem CreateEventItem(string name)
{
EventItem eventItem = new EventItem(this, name);
_EventItems.Add(eventItem);
return eventItem;
}
public virtual string Name { get; private set; }
public virtual IList<EventItem> EventItems
{
get
{
return _EventItems.ToList<EventItem>().AsReadOnly();
}
protected set
{
_EventItems = value;
}
}
}
public class EventItem : EntityBase
{
protected EventItem()
{
}
public EventItem(Event #event, string name):base(name)
{
Event = #event;
}
public virtual Event Event { get; private set; }
}
Pretty stumped here. Any tips greatly appreciated.
Chev
You need to map _EventItems using an access strategy so that NHibernate access the private member instead of the property. You're getting this error because the collection reference is changed when the list is copied to a new List in _EventItems.ToList<EventItem>(). Try this:
public class EventMap : ClassMap<Event>
{
public EventMap()
{
Id(x => x.Id, "Id")
.UnsavedValue("00000000-0000-0000-0000-000000000000")
.GeneratedBy.GuidComb();
Map(x => x.Name);
HasMany(x => x.EventItems)
.Access.PascalCaseField(Prefix.Underscore)
.Inverse()
.KeyColumn("EventId")
.AsBag()
.Cascade.AllDeleteOrphan();
}
}
}
I don't think the accepted answer is an elegant approach. The possible problem here is that Chev is reading Events from the database and then assigning a new EventItem list to the EventItems property. NHibernate throws this exception when you just ignore the previous children list and assign a new children list.
What you need to do here is,
If you want to discard the old EventItems, do this instead:
events.EventItems.Clear();
events.EventItems.Add(new EventItem { blah blah });
Check this SO post: NHibernate: Delete a child record from the parent collection
The comments to the accepted answer has similar issue.
You may want to try removing AsReadOnly for your EventItems to check if that's the cause.