FluentNhiberate Mapping SQLCE4 VALUES(?,?,?,?) on transaction.Save() - fluent-nhibernate

I cant figure this one out...
I have tried to use FluentNhibernate with SQLCE4.. and my configs looks like this for the session:
public class FluentNHibernateFactory
{
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlCeConfiguration.Standard
.ConnectionString(ConfigurationManager.ConnectionStrings["SqlCeDatabase"].ConnectionString))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ApplicationEntity>())
.BuildSessionFactory();
}
public static ISession GetOpenSession()
{
return CreateSessionFactory().OpenSession();
}
}
and my mappings looks like this:
public class ApplicationMap : ClassMap<ApplicationEntity>
{
public ApplicationMap()
{
Table("Applications");
Id(x => x.Id).GeneratedBy.Assigned().Column("id");
Id(x => x.Alias).Column("alias");
Id(x => x.Name).Column("name");
Map(x => x.Created).Column("created");
Map(x => x.CreatedByUser).Column("createdBy");
Map(x => x.Updated).Column("updated");
Map(x => x.UpdatedByUser).Column("updatedBy");
}
}
and finally.. my repository looks like this..:
public class ApplicationRepository : IRepository<ApplicationEntity>
{
public void Add(ApplicationEntity entity)
{
using (var session = FluentNHibernateFactory.GetOpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(entity);
//TODO: Fix the add functionality
transaction.Commit();
}
}
}
public void Remove(ApplicationEntity entity)
{
throw new NotImplementedException();
}
public void Update(ApplicationEntity entity)
{
throw new NotImplementedException();
}
public IEnumerable<ApplicationEntity> GetAll()
{
using (var session = FluentNHibernateFactory.GetOpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
return session.CreateCriteria<ApplicationEntity>().List<ApplicationEntity>().AsEnumerable<ApplicationEntity>();
}
}
}
public ApplicationEntity GetById(long id)
{
throw new NotImplementedException();
}
}
But I just can't add any objects to my database.. the entity objects that gets passed to the Add method has values for all properies and they also seems to be valid properties.. but I do however have three primary keys in the table, so might that be the problem?
the table looks like this by the way:
Applications |
Id | bigint | NOT NULL | Primary key
alias | nvarchar(25) | NOT NULL | Primary key
name | nvarchar(100) | NOT NULL | Primary key
created | datetime | NOT NULL
createdBy | bigint | NOT NULL
updated | datetime | NULL
updatedBy | bigint | NULL
Do you guys see anything wrong in here?. Im new to NHibernate so I might have been doing something really strange here..
The error im getting is:
{"could not insert: [LBi.CATT.Core.Domain.Entities.ApplicationEntity#Test][SQL: INSERT INTO Applications (created, createdBy, updated, updatedBy, name) VALUES (?, ?, ?, ?, ?)]"}
and for the inner exception:
{"Data conversion failed. [ OLE DB status value (if known) = 2 ]"}
sorry for the bad formatting of the table..
Thanks in advance!

the following is not legal
Id(x => x.Id).GeneratedBy.Assigned().Column("id");
Id(x => x.Alias).Column("alias");
Id(x => x.Name).Column("name");
either use this (if Id is the primary key)
Id(x => x.Id)
.GeneratedBy.Assigned()
.UnsavedValue(0)
.Column("id");
Map(x => x.Alias).Column("alias");
Map(x => x.Name).Column("name");
or this (if Id, Alias, Name together are the primary key)
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Alias, "alias")
.KeyProperty(x => x.Name, "name");

Related

Fluent NHibernate One to Many - Transient Instance Exception

I am attempting to do a simple one to many mapping in fluent NHibernate, however i receive the following exception:
"NHibernate.TransientObjectException : object references an unsaved transient instance - save the transient instance before flushing or set cascade action for the property to something that would make it autosave. Type: Voter.Domain.Entities.VoteOption, Entity: Voter.Domain.Entities.VoteOption"
I have tried numerous using Cascade().All() - but this makes no difference.
Please help me to get this cascade working! Much time already wasted...
I have the following entities:
public class Vote
{
public Vote()
{
VoteOptions = new List<VoteOption>();
}
public virtual int Id { get; protected set; }
public virtual Guid VoteReference { get; set; }
public virtual string Title { get; set; }
public virtual string Description { get; set; }
public virtual DateTime ValidFrom { get; set; }
public virtual DateTime ValidTo { get; set; }
public virtual IList<VoteOption> VoteOptions { get; set; }
public virtual void AddOption(VoteOption voteOption)
{
VoteOptions.Add(voteOption);
}
public virtual void AddOptions(List<VoteOption> options)
{
foreach (var option in options.Where(option => VoteOptionAlreadyExists(option) == false))
{
VoteOptions.Add(option);
}
}
private bool VoteOptionAlreadyExists(VoteOption voteOption)
{
return VoteOptions.Any(x => x.Description == voteOption.Description);
}
}
public class VoteOption
{
public virtual int Id { get; protected set; }
public virtual string LongDescription { get; set; }
public virtual string Description { get; set; }
public virtual Vote Vote { get; set; }
}
And the following mappings:
public VoteMap()
{
Table("Vote");
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
Map(x => x.VoteReference).Column("VoteReference");
Map(x => x.Title).Column("Title").Not.Nullable();
Map(x => x.Description).Column("Description").Not.Nullable();
Map(x => x.ValidFrom).Column("ValidFrom").Not.Nullable();
Map(x => x.ValidTo).Column("ValidTo").Not.Nullable();
HasMany(x => x.VoteOptions).KeyColumn("Vote_Id").Cascade.All();
}
public class VoteOptionMap : ClassMap<VoteOption>
{
public VoteOptionMap()
{
Table("VoteOption");
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
Map(x => x.Description).Column("Description").Not.Nullable();
Map(x => x.LongDescription).Column("LongDescription").Not.Nullable();
References(x => x.Vote).Column("Vote_Id").Cascade.All();
}
}
And the following SQL Server database tables:
CREATE TABLE dbo.Vote
(
Id INT IDENTITY(1,1) PRIMARY KEY,
VoteReference UNIQUEIDENTIFIER NULL,
Title VARCHAR(500) NOT NULL,
[Description] VARCHAR(1000) NOT NULL,
ValidFrom DATETIME NOT NULL,
ValidTo DATETIME NOT NULL
)
CREATE TABLE dbo.VoteOption
(
Id INT IDENTITY(1,1) PRIMARY KEY,
Vote_Id INT NOT NULL,
[Description] VARCHAR(500) NOT NULL,
LongDescription VARCHAR(5000) NOT NULL
)
Implementation code is:
public void Save()
{
var vote = new Vote
{
VoteReference = new Guid(),
Title = "Events Vote",
Description = "Which event would you like to see next?",
ValidFrom = DateTime.Now.AddDays(-2),
ValidTo = DateTime.Now.AddDays(3)
};
var options = new List<VoteOption>
{
new VoteOption {Description = "What you want?", LongDescription = "Tell me all about it..."},
new VoteOption {Description = "Another option?", LongDescription = "Tell me some more..."}
};
vote.AddOptions(options);
using (var session = sessionFactory().OpenSession())
{
using (var transaction = session.BeginTransaction())
{
//This works - but undermines cascade!
//foreach (var voteOption in vote.VoteOptions)
//{
// session.Save(voteOption);
//}
session.Save(vote);
transaction.Commit();
}
}
}
private ISessionFactory sessionFactory()
{
var config = new Configuration().Configure();
return Fluently.Configure(config)
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Vote>()))
.BuildSessionFactory();
}
I would say, that setting as shown above (the fluent mapping) is ok. Other words, the code I see right now, seems to be having different issue, then the Exception at the top.
The HasMany cascade setting is OK, but I would suggest to mark it as inverse (see here for more info ... NHibernate will not try to insert or update the properties defined by this join...)
HasMany(x => x.VoteOptions)
.KeyColumn("Vote_Id")
.Inverse()
.Cascade.All();
Also, the Reference should be in most case without Cascade: References(x => x.Vote).Column("Vote_Id");
Having this, and running your code we should be recieving at the moment the SqlException: *Cannot insert the value NULL into column 'Vote_Id'*
Because of the TABLE dbo.VoteOption defintion:
...
Vote_Id INT NOT NULL, // must be filled even on a first INSERT
So, the most important change should be in the place, where we add the voteOption into Vote collection (VoteOptions). We always should/must be providing the reference back, ie. voteOption.Vote = this;
public virtual void AddOption(VoteOption voteOption)
{
VoteOptions.Add(voteOption);
voteOption.Vote = this; // here we should/MUST reference back
}
public virtual void AddOptions(List<VoteOption> options)
{
foreach (var option in options.Where(option => VoteOptionAlreadyExists(option) == false))
{
VoteOptions.Add(option);
option.Vote = this; // here we should/MUST reference back
}
}
After these adjustments, it should be working ok
The cascade option can be set globally using Fluent NHibernate Automapping conventions. The issue that #Radim Köhler pointed out also needs to be corrected when adding items to the List.
Using global conventions:
Add a convention, it can be system wide, or more restricted.
DefaultCascade.All()
Code example:
var cfg = new StoreConfiguration();
var sessionFactory = Fluently.Configure()
.Database(/* database config */)
.Mappings(m =>
m.AutoMappings.Add(
AutoMap.AssemblyOf<Product>(cfg)
.Conventions.Setup(c =>
{
c.Add(DefaultCascade.All());
}
)
.BuildSessionFactory();
Now it will automap the cascade when saving.
More info
Wiki for Automapping
Table.Is(x => x.EntityType.Name + "Table")
PrimaryKey.Name.Is(x => "ID")
AutoImport.Never()
DefaultAccess.Field()
DefaultCascade.All()
DefaultLazy.Always()
DynamicInsert.AlwaysTrue()
DynamicUpdate.AlwaysTrue()
OptimisticLock.Is(x => x.Dirty())
Cache.Is(x => x.AsReadOnly())
ForeignKey.EndsWith("ID")
See more about Fluent NHibernate automapping conventions

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.

fluent nhibernate, unable to resolve property: <property>

I have a one-to-one relationship in my db and I seem to be having issues with my fluent nhibernate mapping of this relationship.
When I attempt to insert a new TableA entity I receive the following error: "unable to resolve property: TableA". The error is thrown on this line in the repository: int id = (int)_session.Save(item);
Repository code:
public T Save(T item)
{
try
{
_session.BeginTransaction();
int id = (int)_session.Save(item);
_session.Transaction.Commit();
return _session.Get<T>(id);
}
catch (Exception)
{
_session.Transaction.Rollback();
throw;
}
}
Table definitions:
Table A
Id (int, pk, identity)
Field1 (nvarchar)
Field2 (date)
...
Table B
TableAId (int, pk) <-- no fk constraint on these tables
Field1 (nvarchar)
Field2 (nvarchar)
Field3 (bit)
Classes:
public class TableA
{
public virtual int Id {get;set;}
public virtual string Field1 {get;set;}
public virtual DateTime Field2 {get;set;}
public virtual TableB TableB {get;set;}
}
public class TableB
{
public virtual int TableAId {get;set;}
public virtual string Field1 {get;set;}
public virtual string Field2 {get;set;}
}
Mapping:
public class TableAMap : ClassMap<TableA>
{
public TableAMap(){
Id(x => x.Id);
Map(x => x.Field1);
Map(x => x.Field2);
HasOne(x => x.TableB)
.Cascade.SaveUpdate()
.Fetch.Join();
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap()
{
Id(x => x.Id, "TableAId").GeneratedBy.Foreign("TableA");
Map(x => x.Field1);
Map(x => x.Field2);
Map(x => x.Field3);
}
}
I used these as a reference:
http://avinashsing.sunkur.com/2011/09/29/how-to-do-a-one-to-one-mapping-in-fluent-nhibernate/
One-to-one Mapping issue with NHibernate/Fluent: Foreign Key not updateing
IndexOutOfRangeException Deep in the bowels of NHibernate
I've been looking at this for so long now I fear I'm missing something simple (and stupid).
Update:
Tried this:
public class TableA
{
public virtual int Id {get;set;}
public virtual string Field1 {get;set;}
public virtual DateTime Field2 {get;set;}
public virtual TableB TableB {get;set;}
public virtual int TableBId
{
get{return TableB.Id;}
set{}
}
}
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
Id(x => x.Id);
Map(x => x.Field1);
Map(x => x.Field2);
HasOne<TableB>(x => x.TableB)
.Cascade.SaveUpdate()
.Fetch.Join()
.ForeignKey("TableBId");
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap()
{
Id(x => x.Id, "TableAId");
Map(x => x.Field1);
Map(x => x.Field2);
Map(x => x.Field3);
}
}
I didn't get the "unable to resolve property: TableA" error this time, but I received a different error. It seems like the Id of the TableA record did not cascade to the TableB record.
Did some more reading.
Found this post: fluent NHibernate one-to-one relationship?
Made some more changes:
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
Id(x => x.Id);
Map(x => x.Field1);
Map(x => x.Field2);
HasOne<TableB>(x => x.TableB)
.Cascade.All()
.Constrained()
.Fetch.Join()
.ForeignKey("TableBId");
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap()
{
Id(x => x.Id, "TableAId").UnsavedValue(0).GeneratedBy.Foreign("TableA");
Map(x => x.Field1);
Map(x => x.Field2);
Map(x => x.Field3);
HasOne<TableA>(x => x.TableA);
}
}
New error:
"attempted to assign id from null one-to-one property: TableA"
Even though this relationship is a one-to-one, and should be a one-to-one, I ended up modifying my database defintion to make this relationship a many-to-one and I mapped it as a many-to-one. Now it works. I'm not happy about it though, I think there should be a way to successfully map a one-to-one.
I was wasting too much time trying to get the one-to-one mapping to work and for the sake of my timeline I just needed to move on.

Why does NHibernate's LINQ provider generate invalid T-SQL for a GroupBy()?

I'm trying to do what seems like a simple query for the number of Products produced by each Manufacturer, but NHibernate isn't generating T-SQL that MS SQL Server finds valid.
session.Query<Product>()
.GroupBy(p => p.Manufacturer)
.Select(grp => new {Mftr = grp.Key.Name, ProductCount = grp.Count()})
.ToList();
This seems dead simple, but the SQL statement that NHibernate generates doesn't include all the necessary column names, so it fails when running against a SQL Server 2008 or SQL Server CE database. If I point the same code to an in-memory SQLite database, it works fine.
More information is below, and I also created a small console application that demonstrates my problem. How do I fix this problem?
Generated SQL
select manufactur1_.Id,
cast(count(*) as INT),
manufactur1_.Id,
manufactur1_.Name
from "Product" product0_
left outer join "Manufacturer" manufactur1_
on product0_.Manufacturer_id=manufactur1_.Id
group by manufactur1_.Id -- Where's manufactur1_.Name?
Entities
public class Product {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
}
public class Manufacturer {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
FNH Mappings
public class ProductMap : ClassMap<Product> {
public ProductMap() {
Id(x => x.Id).GeneratedBy.HiLo("1");
Map(x => x.Name);
References(x => x.Manufacturer).Cascade.SaveUpdate().Not.Nullable();
}
}
public class ManufacturerMap : ClassMap<Manufacturer> {
public ManufacturerMap() {
Id(x => x.Id) .GeneratedBy.HiLo("1");
Map(x => x.Name);
}
}
Here's a QueryOver version...
//alias variables
Manufacturer m = null;
ManufacturerProducts dto = null;
var result = Session.QueryOver<Product>
.Left.JoinAlias(x => x.Manufacturer, () => m)
.SelectList(list => list
.SelectGroup(() => m.Id).WithAlias(() => dto.Id)
.SelectGroup(() => m.Name).WithAlias(() => dto.Name)
.SelectCount(x => x.Id).WithAlias(() => dto.ProductCount))
.TransformUsing(Transformers.AliasToBean<ManufacturerProducts>())
.List<ManufacturerProducts>();

fluent-nhibernate: not getting the records

I have an entity like
public class SKU
{
//public int Id { get; set; }
public string FactoruCode { get; set; }
public string Ptoduct { get; set; }
}
and mapping defined as
public class SKUMap : ClassMap<SKU>
{
public SKUMap()
{
Table("MST_PRODUCT");
Not.LazyLoad();
Id(x => x.Ptoduct).GeneratedBy.Assigned();
Map(x => x.Ptoduct, "PRODUCT_NAME");
Map(x => x.FactoruCode, "FACTORY_CODE");
}
}
and retrieving the records like
class Program
{
static void Main()
{
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (session.BeginTransaction())
{
var skus = session.CreateCriteria(typeof(SKU)).List<SKU>();
foreach (var sku in skus)
{
Console.WriteLine(sku.Ptoduct);
}
}
}
}
private static ISessionFactory CreateSessionFactory()
{
var cfg = OracleClientConfiguration.Oracle10
.ConnectionString(c =>
c.Is(
#"DATA SOURCE=SERVER_NAME;PERSIST SECURITYINFO=True;USER ID=USER_ID;Password=PWD"));
return Fluently.Configure()
.Database(cfg).Mappings(m => m.FluentMappings.AddFromAssemblyOf<Program>())
.ExposeConfiguration(BuildSchema).BuildSessionFactory();
}
private static void BuildSchema(NHibernate.Cfg.Configuration config)
{
new SchemaExport(config).Create(false, true);
}
}
but the table has more columns than specified for entity. This code executes well, but I'm not able to get the list of SKUs (table has more than 8000 rows).
Please help me to understand the problem.
Your SKU map is wrong. Why have you defined PRODUCT_NAME as an Id column? You need to fix it by setting the Id to an Id column (which you have commented out):
Id(x => x.Id, "NAME_OF_YOUR_ID_COLUMN_HERE").GeneratedBy.Assigned();
Map(x => x.Ptoduct, "PRODUCT_NAME");
If PRODUCT_NAME is indeed the Id, you need to set it like this:
Id(x => x.Ptoduct, "PRODUCT_NAME").GeneratedBy.Assigned();
and remove the other line:
Map(x => x.Ptoduct, "PRODUCT_NAME");
Also, if your database has more fields or tables then you are mapping, it can give you many errors. To resolve them, you need to set use_proxy_validator to false in your configuration.
EDIT:
NHibernate requires an Id column to work properly. I don't even know that if it does work without actually having a column declared as an Id column. Even if you declare Ptoduct as an Id column, you will not be able to properly query the database as querying for any of all objects with the same Ptoduct will return the topmost object.