NHibernate SaveOrUpdateCopy won't insert entity with CompositeId - nhibernate

I have an entity with a CompositeId that won't insert new rows to the database using SaveOrUpdateCopy. The INSERT statement generated by NHibernate is populated with "?" for the value of every field. It inserts fine with SaveOrUpdate, it updates fine with either SaveOrUpdateCopy or SaveOrUpdate, and any entity without a CompositeId inserts/updates fine with SaveOrUpdateCopy. I don't want to create an if/then looking for entities with CompositeId to decide if it should use SaveOrUpdate or SaveOrUpdateCopy. Is there some trick to getting SaveOrUpdateCopy to work with entities with CompositeId?
Here's the code (names changed to protect the innocent):
public class MyEntity
{
public virtual Int32 FirstProperty { get; set; }
public virtual string SecondProperty { get; set; }
public virtual string DataText { get; set; }
public override int GetHashCode( )
{
int hashCode = 0;
hashCode = hashCode ^ FirstProperty.GetHashCode() ^
SecondProperty.GetHashCode();
return hashCode;
}
public override bool Equals( object obj )
{
MyEntity toCompare = obj as MyEntity;
if( toCompare == null )
{
return false;
}
return ( GetHashCode() != toCompare.GetHashCode() );
}
}
public MyEntityMap()
{
CompositeId()
.KeyProperty(x => x.FirstProperty, "first_property")
.KeyProperty(x => x.SecondProperty, "second_property");
Map(x => x.DataText, "data_text")
.Nullable();
Table("dbo.my_entity");
}
Database call:
public MyEntity GetMyEntity(long firstProperty, string secondProperty)
{
using (var session = sessionFactory.OpenSession())
{
var result = from entity in
session.Linq()
where entity.FirstProperty == firstProperty
&& entity.SecondProperty== secondProperty
select entity;
return result.Count() > 0 ? result.First() : null;
}
}
Database save:
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
try
{
session.SaveOrUpdateCopy(entity);
transaction.Commit();
}
catch(Exception ex)
{
transaction.Rollback();
throw;
}
}
}

Add a version property to the composite key class, see this article for an in-depth explanation.

Hi I use compositeId with FluentnHibernate but my implementation of the Equals and GetHashCode is different. This one class that have 7 fields as Key:
public class PresupuestoGastoPromocion
{
public PresupuestoGastoPromocion() { }
public virtual int Año { get; set; }
public virtual int Mes { get; set; }
public virtual int PartidaId { get; set; }
public virtual string Equipo { get; set; }
public virtual string CodFamilia { get; set; }
public virtual string GDP { get; set; }
public virtual int TipoPresupuestoId { get; set; }
public virtual float Monto { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
var t = obj as PresupuestoGastoPromocion;
if (t == null)
return false;
if (CodFamilia == t.CodFamilia && Año == t.Año && Mes == t.Mes && TipoPresupuestoId == t.TipoPresupuestoId && Equipo == t.Equipo && PartidaId == t.PartidaId && GDP == t.GDP)
return true;
return false;
}
public override int GetHashCode()
{
return (CodFamilia + "|" + Año + "|" + Mes + "|" + TipoPresupuestoId + "|" + Equipo + "|" + PartidaId + "|" + GDP).GetHashCode();
}
}
public class PresupuestoGastoPromocionMap : ClassMap<PresupuestoGastoPromocion>
{
public PresupuestoGastoPromocionMap()
{
Table("PresupuestoGastoPromocion");
CompositeId()
.KeyProperty(x => x.Año)
.KeyProperty(x => x.Mes)
.KeyProperty(x => x.TipoPresupuestoId)
.KeyProperty(x => x.CodFamilia, "Cod_Familia")
.KeyProperty(x => x.Equipo)
.KeyProperty(x => x.GDP)
.KeyProperty(x => x.PartidaId);
Map(x => x.Monto).Column("Monto");
}
}
I hope this will help you.

Related

NHibernate update all child rows when adding a new child

I'm struggling with a problem related to parent-child mapping.
I think I did everything correctly, but every time I add a child and save the parent, nhibernate execute an insert and then an update for all the rows in the child list.
here is my classes and mapping
public class Cliente : EntityBase
{
public virtual string Cognome { get; set; }
public virtual string Nome { get; set; }
public virtual int AttivitaId { get; set; }
public virtual DateTime? DittaAssunzioneData { get; set; }
public virtual decimal Stipendio { get; set; }
public virtual IList<ClienteChiusura> Chiusure { get; protected set; }
public virtual void AddTelefonata(Telefonata telefonata)
{
if (Telefonate == null)
Telefonate = new List<Telefonata>();
//NHibernate impedence
telefonata.Cliente = this;
Telefonate.Add(telefonata);
}
public virtual void RemoveTelefonata(int telefonataId)
{
if (Telefonate == null)
return;
var telefonata = Telefonate.SingleOrDefault(x => x.Id == telefonataId);
if (telefonata != null)
{
Telefonate.Remove(telefonata);
telefonata.Cliente = null;
}
}
}
public class Telefonata : EntityBase
{
public virtual Cliente Cliente { get; set; }
public virtual string Descrizione { get; set; }
public virtual DateTime? Data { get; set; }
public virtual bool Ingresso { get; set; }
public virtual string Utente { get; set; }
public virtual DateTime? UtenteData { get; set; }
public virtual TelefonataTipo TelefonataTipo { get; set; }
}
public class TelefonataTipo : EntityBase
{
public virtual string Nome { get; set; }
}
public abstract class EntityBase
{
public virtual int Id { get; private set; }
//ctor
protected EntityBase()
{ }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
return (obj != null && obj.GetType() == GetType() && ((EntityBase)obj).Id == Id);
}
public static bool operator ==(EntityBase entity1, EntityBase entity2)
{
//cast come object altrimenti ho un loop ricorsivo
if ((object)entity1 == null && (object)entity2 == null)
return true;
if ((object)entity1 == null || (object)entity2 == null)
return false;
if (entity1.GetType() != entity2.GetType())
return false;
if (entity1.Id != entity2.Id)
return false;
return true;
}
public static bool operator !=(EntityBase entity1, EntityBase entity2)
{
return (!(entity1 == entity2));
}
}
And the mapping is as follow
public class ClienteMapping : MappingBase<Evoltel.PuntoQuinto.Domain.Entities.Cliente.Cliente>
{
public ClienteMapping()
: base("CLIENTI")
{
Id(x => x.Id, c => { c.Column("CLIENTE_ID"); c.Generator(Generators.Native, g => g.Params(new { sequence = "GEN_CLIENTI_ID" })); });
Property(x => x.Cognome, c => c.Column("COGNOME"));
Property(x => x.Nome, c => c.Column("NOME"));
Property(x => x.AttivitaId, c => c.Column("ATTIVITA_ID"));
Property(x => x.DittaAssunzioneData, c => c.Column("D_ASSUNZIONE_DATA"));
Property(x => x.Stipendio, c => c.Column("D_STIPENDIO"));
Bag(x => x.Telefonate, map =>
{
map.Inverse(true);
map.Table("TELEFONATE");
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
map.Key(k => k.Column("CLIENTE_ID"));
map.Lazy(CollectionLazy.NoLazy);
}, r => r.OneToMany());
}
public class TelefonataMapping : MappingBase<Evoltel.PuntoQuinto.Domain.Entities.Cliente.Telefonata>
{
public TelefonataMapping()
: base("TELEFONATE")
{
Id(x => x.Id, c => { c.Column("TELEFONATA_ID"); c.Generator(Generators.Native, g => g.Params(new { sequence = "GEN_TELEFONATE_ID" })); });
ManyToOne(x => x.Cliente, c => c.Column("CLIENTE_ID"));
Property(x => x.Descrizione, c => c.Column("DESC_LUNGA"));
Property(x => x.Data, c => c.Column("TELEFONATA_DATA"));
Property(x => x.Ingresso, c => { c.Column("INGRESSO"); c.Type<TrueFalseType>(); });
Property(x => x.Utente, c => c.Column("UTENTE_NOME"));
Property(x => x.UtenteData, c => c.Column("UTENTE_DATA"));
ManyToOne(x => x.TelefonataTipo, c => { c.Column("TELEFONATA_TIPO_ID"); c.NotFound(NotFoundMode.Ignore); });
}
}
public abstract class MappingBase<T> : ClassMapping<T> where T : EntityBase
{
protected MappingBase(string tableName)
{
Table(tableName);
Lazy(false);
DynamicUpdate(true);
}
}
If I do not use Cascade.All the new Child is not saved (and for what I understood of NHibernate it's correct)
What is that I'm not seeing?

Fluent NHibernate automapping table-per-abstract-hierarchy / table-per-concrete-subclass

I have classes
public abstract class Content : IContent
{
public virtual Guid Id { get; protected set; }
public virtual IPage Parent { get; set; }
public virtual DateTime Created { get; set; }
/* ... */
}
public abstract class Page : Content, IPage
{
public virtual string Slug { get; set; }
public virtual string Path { get; set; }
public virtual string Title { get; set; }
/* ... */
}
public class Foo : Page, ITaggable
{
// this is unique property
// map to joined table
public virtual string Bar { get; set; }
// this is a unique collection
public virtual ISet<Page> Related { get; set; }
// this is "shared" property (from ITaggable)
// map to shared table
public virtual ISet<Tag> Tags { get; set; }
}
And as a result I'd like to have the following tables. I've tried implementing tons of different IConventions, but even the hierarchy mappings (table-per-abstract-hierarchy / table-per-concrete-subclass) seem to fail.
Content
Id
Type (discriminator)
ParentId
Created
Slug
Path
Title
Content_Tags (Tags from ITaggable)
ContentId
TagId
Content$Foo
Bar
Content$Foo_Related
ParentFooId
ChildPageId
I already have ugly, working fluent mappings, but I would like to get rid of some ugliness
public class ContentMapping : ClassMap<Content>
{
public ContentMapping()
{
Table("Content");
Id(x => x.Id).GeneratedBy.GuidComb();
References<Page>(x => x.Parent, "ParentId");
Map(x => x.Created);
DiscriminateSubClassesOnColumn("Type");
}
}
public class PageMapping : SubclassMap<Page>
{
public PageMapping()
{
Map(x => x.Slug);
Map(x => x.Path);
Map(x => x.Title);
}
}
public class ConcreteContentMapping<T> : SubclassMap<T> where T : Content, new()
{
public ConcreteContentMapping() : this(true) { }
protected ConcreteContentMapping(bool mapJoinTable)
{
DiscriminatorValue(typeof(T).FullName);
MapCommonProperties();
if(mapJoinTable)
{
MapJoinTableWithProperties(CreateDefaultJoinTableName(), GetPropertiesNotFrom(GetContentTypesAndInterfaces().ToArray()));
}
}
private void MapCommonProperties()
{
if (typeof(ITagContext).IsAssignableFrom(typeof(T)))
{
Map(x => ((ITagContext)x).TagDirectory);
}
if (typeof(ITaggable).IsAssignableFrom(typeof(T)))
{
HasManyToMany(x => ((ITaggable)x).Tags).Table("Content_Tags").ParentKeyColumn("ContentId").ChildKeyColumn("TagId").Cascade.SaveUpdate();
}
}
/* ... */
// something I would like to get rid of with automappings...
protected void MapCollectionProperty(JoinPart<T> table, PropertyInfo p)
{
var tableName = ((IJoinMappingProvider)table).GetJoinMapping().TableName + "_" + p.Name;
var elementType = p.PropertyType.GetGenericArguments()[0];
var method = table.GetType().GetMethods().Where(m => m.Name == "HasManyToMany")
.Select(m => new { M = m, P = m.GetParameters() })
.Where(x => x.P[0].ParameterType.GetGenericArguments()[0].GetGenericArguments()[1] == typeof(object))
.FirstOrDefault().M.MakeGenericMethod(elementType);
dynamic m2m = method.Invoke(table, new object[] { MakePropertyAccessExpression(p)});
m2m.Table(tableName).ParentKeyColumn("Parent" + typeof(T).Name + "Id").ChildKeyColumn("Child" + elementType.Name + "Id");
}
protected Expression<Func<T, object>> MakePropertyAccessExpression(PropertyInfo property)
{
var param = Expression.Parameter(property.DeclaringType, "x");
var ma = Expression.MakeMemberAccess(param, property);
return Expression.Lambda<Func<T, object>>(ma, param);
}
}
How do I get the same result with automappings?

NHibernate Error Message: Invalid index 3 for this SqlParameterCollection with Count=3

I have a test database design like this:
The following is the pseudo-code:
//BhillHeader
public class BillHeader
{
public BillHeader()
{
BillDetails = new List<BillDetail>();
}
public virtual int BillNo { get; set; }
public virtual IList<BillDetail> BillDetails { get; set; }
public virtual decimal Amount { get; set; }
public virtual void AddDetail(BillDetail billdet)
{
BillDetails.Add(billdet);
}
}
//BillHeader Map
public class BillHeaderMap : ClassMap<BillHeader>
{
public BillHeaderMap()
{
Table("BillHeader");
LazyLoad();
Id(x => x.BillNo).GeneratedBy.Identity().Column("BillNo");
Map(x => x.Amount).Column("Amount").Not.Nullable();
HasMany(x => x.BillDetails).KeyColumn("BillNo").Cascade.All().Inverse();
}
}
//BillDetail
public class BillDetail
{
public BillDetail() { }
public virtual int BillID { get; set; }
public virtual int SeqNo { get; set; }
public virtual BillHeader BillHeader { get; set; }
public virtual decimal Amt { get; set; }
public override bool Equals(object obj)
{
var other = obj as BillDetail;
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.BillID == other.BillID &&
this.SeqNo == other.SeqNo;
}
public override int GetHashCode()
{
unchecked {
int hash = GetType().GetHashCode();
hash = (hash * 31) ^ SeqNo.GetHashCode();
hash = (hash * 31) ^ BillID.GetHashCode();
return hash;
}
}
}
//BillDetail Map
public class BillDetailMap : ClassMap<BillDetail>
{
public BillDetailMap()
{
Table("BillDetail");
LazyLoad();
CompositeId().KeyProperty(x => x.BillID, "BillNo").KeyProperty(x => x.SeqNo, "SeqNo");
References(x => x.BillHeader).Column("BillNo");
Map(x => x.Amt).Column("Amt").Not.Nullable();
}
}
//-----------------------------------------------------------------------------------------------------------------------------
//Program
public createBillNo()
{
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession()) {
using (var sqlTrans = session.BeginTransaction()) {
BillHeader billNo1 = new BillHeader() { Amount = 2500.00M};
BillDetail bh11 = new BillDetail() { SeqNo = 1, Amt = 200.00M };
BillDetail bh12 = new BillDetail() { SeqNo = 2, Amt = 300.00M };
BillDetail bh13 = new BillDetail() { SeqNo = 3, Amt = 500.00M };
AddBillDetailsToBillHeader(billNo1, bh11, bh12, bh13);
session.SaveOrUpdate(billNo1);
sqlTrans.Commit();
}
}
}
private void AddBillDetailsToBillHeader(BillHeader billHeader, params BillDetail[] billDetails)
{
foreach (var billdet in billDetails) {
billHeader.AddDetail(billdet);
billdet.BillHeader = billHeader;
}
}
When I run this I'm getting the following exception:
Invalid index 3 for this SqlParameterCollection with Count=3
Please help me to resolve this issue.
most probably because column "BillNo" is mapped twice, it tries to add 2 parameter for 1 column, hence the outOfRange error. move the reference into the compositekey
CompositeId()
.KeyReference(x => x.BillHeader, "BillNo")
.KeyProperty(x => x.SeqNo, "SeqNo");
// References(x => x.).Column("BillNo"); <-- Remove

Fluent Nhibernate: Trying to create entity with composite key that is also the keys for two references

The references are unidirectional. The table (StoreProduct) for this entity is actually a join table that has these fields:
Store_id
Product_id
ExtraBit
So I went with an entity having a compoundID (store_id and product_id) and the ExtraBit is just a string:
public class StoreProduct
{
protected StoreProduct():this(null,null,null){ }
public StoreProduct(Store c_Store, Product c_Product, String c_ExtraBit)
{
Store = c_Store;
Product = c_Product;
ExtraBit = c_ExtraBit;
}
public virtual int Product_id { get; set; }
public virtual int Store_id { get; set; }
public virtual Store Store { get; set; }
public virtual Product Product { get; set; }
public virtual String ExtraBit { get; set; }
public override int GetHashCode()
{
return Store.GetHashCode() + Product.GetHashCode();
}
public override bool Equals(object obj)
{
StoreProduct obj_StoreProduct;
obj_StoreProduct = obj as StoreProduct;
if (obj_StoreProduct == null)
{
return false;
}
if (obj_StoreProduct.Product != this.Product && obj_StoreProduct.Store != this.Store)
{
return false;
}
return true;
}
}
And the mapping:
public class Order_DetailMap : ClassMap<StoreProduct>
{
public Order_DetailMap()
{
Table("StoreProduct");
LazyLoad();
CompositeId().KeyProperty(x => x.Store_id).KeyProperty(x => x.Product_id);
References(x => x.Store).ForeignKey("Store_id").Cascade.All();
References(x => x.Product).ForeignKey("Product_id").Cascade.All();
Map(x => x.ExtraBit);
}
}
It doesn't work though, when I tried saving the StoreProduct and its newly created Store and product. Can anyone help? Here is some output:
Unhandled Exception: System.ArgumentOutOfRangeException: Index was out of range.
Must be non-negative and less than the size of the collection.
Parameter name: index
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Data.SQLite.SQLiteParameterCollection.GetParameter(Int32 index)
at System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item
(Int32 index)
at NHibernate.Type.Int32Type.Set(IDbCommand rs, Object value, Int32 index) in
d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\Int32Type.cs:line 60
at NHibernate.Type.NullableType.NullSafeSet(IDbCommand cmd, Object value, Int
32 index) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\NullableType.cs:line
180
at NHibernate.Type.NullableType.NullSafeSet(IDbCommand st, Object value, Int3
2 index, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHiberna
te\Type\NullableType.cs:line 139
at NHibernate.Type.ComponentType.NullSafeSet(IDbCommand st, Object value, Int
32 begin, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibern
ate\Type\ComponentType.cs:line 221
at NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, O
bject[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColu
mns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index
) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPe
rsister.cs:line 2418
Edit: Thanks to the help bellow I seem to have a decent solution:
Store Mapping and Class:
namespace compoundIDtest.Domain.Mappings
{
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id).Column("Store_id");
Map(x => x.Name);
HasMany(x => x.Staff)
.Inverse()
.Cascade.All();
HasManyToMany(x => x.Products)
.Cascade.All()
.Table("StoreProduct");
}
}
}
namespace compoundIDtest.Domain.Entities
{
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
public virtual IList<StoreProduct> StoreProducts { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
public override bool Equals(object obj)
{
Store obj_Store;
obj_Store = obj as Store;
if (obj_Store == null)
{
return false;
}
if (obj_Store.Name != this.Name)
{
return false;
}
return true;
}
}
}
Product Mapping And Class
namespace compoundIDtest.Domain.Mappings
{
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id).Column("Product_id");
Map(x => x.Name);
Map(x => x.Price);
HasManyToMany(x => x.StoresStockedIn)
.Cascade.All()
.Inverse()
.Table("StoreProduct");
}
}
}
namespace compoundIDtest.Domain.Entities
{
public class Product
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList<Store> StoresStockedIn { get; set; }
public virtual IList<StoreProduct> StoreProducts { get; set; }
public Product()
{
StoresStockedIn = new List<Store>();
StoreProducts = new List<StoreProduct>();
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
public override bool Equals(object obj)
{
Product obj_Product;
obj_Product = obj as Product;
if (obj_Product == null)
{
return false;
}
if (obj_Product.Name != this.Name)
{
return false;
}
return true;
}
}
}
And StoreProduct
namespace compoundIDtest.Domain.Mappings
{
public class Order_DetailMap : ClassMap<StoreProduct>
{
public Order_DetailMap()
{
Table("StoreProduct");
LazyLoad();
CompositeId().KeyReference(x => x.Store, "Store_id").KeyReference(x => x.Product, "Product_id");
References(x => x.Store, "Store_id").Not.Update().Not.Insert().Cascade.All();
References(x => x.Product, "Product_id").Not.Update().Not.Insert().Cascade.All();
Map(x => x.ExtraBit);
}
}
}
namespace compoundIDtest.Domain.Entities
{
public class StoreProduct
{
public StoreProduct(){}
public virtual Store Store { get; set; }
public virtual Product Product { get; set; }
public virtual String ExtraBit { get; set; }
public override int GetHashCode()
{
if (this.ExtraBit != null)
{
return Store.GetHashCode() + Product.GetHashCode() + ExtraBit.GetHashCode();
}
return Store.GetHashCode() + Product.GetHashCode();
}
public override bool Equals(object obj)
{
StoreProduct obj_StoreProduct;
obj_StoreProduct = obj as StoreProduct;
if (obj_StoreProduct == null)
{
return false;
}
if (obj_StoreProduct.Product != this.Product && obj_StoreProduct.Store != this.Store && obj_StoreProduct.ExtraBit != this.ExtraBit)
{
return false;
}
return true;
}
}
}
And here is code for an app to test the above:
using System;
using System.IO;
using compoundIDtest.Domain.Entities;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using FluentNHibernate.Conventions;
namespace compoundIDtest
{
class Program
{
private const string DbFile = "firstProgram.db";
static void Main()
{
// create our NHibernate session factory
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
// populate the database
using (var transaction = session.BeginTransaction())
{
// create a couple of Stores each with some Products and Employees
var barginBasin = new Store { Name = "Bargin Basin" };
var superMart = new Store { Name = "SuperMart" };
var CornerShop = new Store { Name = "Corner Shop" };
var potatoes = new Product { Name = "Potatoes", Price = 3.60 };
var fish = new Product { Name = "Fish", Price = 4.49 };
var milk = new Product { Name = "Milk", Price = 0.79 };
var bread = new Product { Name = "Bread", Price = 1.29 };
var cheese = new Product { Name = "Cheese", Price = 2.10 };
var waffles = new Product { Name = "Waffles", Price = 2.41 };
var poison = new Product { Name = "Poison", Price = 1.50 };
var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };
var sue = new Employee { FirstName = "Sue", LastName = "Walkters" };
var bill = new Employee { FirstName = "Bill", LastName = "Taft" };
var joan = new Employee { FirstName = "Joan", LastName = "Pope" };
var storeproduct = new StoreProduct { Store = CornerShop, Product = poison, ExtraBit = "Extra Bit"};
//session.SaveOrUpdate(CornerShop);
//session.SaveOrUpdate(poison);
session.Save(storeproduct);
// add products to the stores, there's some crossover in the products in each
// store, because the store-product relationship is many-to-many
AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
AddProductsToStore(superMart, bread, cheese, waffles);
// add employees to the stores, this relationship is a one-to-many, so one
// employee can only work at one store at a time
AddEmployeesToStore(barginBasin, daisy, jack, sue);
AddEmployeesToStore(superMart, bill, joan);
// save both stores, this saves everything else via cascading
session.SaveOrUpdate(barginBasin);
session.SaveOrUpdate(superMart);
//session.SaveOrUpdate(CornerShop);
//session.SaveOrUpdate(poison);
//session.SaveOrUpdate(storeproduct);
transaction.Commit();
}
}
using (var session = sessionFactory.OpenSession())
{
// retreive all stores and display them
using (var transaction = session.BeginTransaction())
{
var products = session.CreateCriteria(typeof(Product))
.List<Product>();
foreach (var product in products)
{
product.Price = 100;
session.SaveOrUpdate(product);
}
var storeproducts = session.CreateCriteria(typeof(StoreProduct)).List<StoreProduct>();
foreach (StoreProduct storeproduct in storeproducts)
{
if (storeproduct.Store.Name == "SuperMart")
{
storeproduct.ExtraBit = "Thank you, come again";
}
session.SaveOrUpdate(storeproduct);
}
transaction.Commit();
}
}
Console.ReadKey();
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard
.UsingFile(DbFile))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<Program>())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private static void BuildSchema(Configuration config)
{
// delete the existing db on each run
if (File.Exists(DbFile))
File.Delete(DbFile);
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}
private static void WriteStorePretty(Store store)
{
Console.WriteLine(store.Name);
Console.WriteLine(" Products:");
foreach (var product in store.Products)
{
Console.WriteLine(" " + product.Name);
}
Console.WriteLine(" Staff:");
foreach (var employee in store.Staff)
{
Console.WriteLine(" " + employee.FirstName + " " + employee.LastName);
}
Console.WriteLine();
}
public static void AddProductsToStore(Store store, params Product[] products)
{
foreach (var product in products)
{
store.AddProduct(product);
}
}
public static void AddEmployeesToStore(Store store, params Employee[] employees)
{
foreach (var employee in employees)
{
store.AddEmployee(employee);
}
}
}
}
I had a mapping pretty much identical to this and the way I ended up mapping it was like this:
public class Order_DetailMap : ClassMap<StoreProduct>
{
public Order_DetailMap()
{
Table("StoreProduct");
CompositeId()
.KeyReference(x => x.Store, "Store_id")
.KeyReference(x => x.Product, "Product_id");
Map(x => x.ExtraBit);
}
}
Inside of my Store and Product classes I have add and remove methods that make the creation of this middle class almost invisible. Example below:
public class Store
{
public IList<StoreProduct> StoreProducts { get; set; }
//Other properties and Constructors
public virtual void AddProduct(Product productToAdd, string extraBit)
{
StoreProduct newStoreProduct = new StoreProduct(this, productToAdd, extraBit);
storeProducts.Add(newStoreProduct);
}
}
In addition to the above I had HasMany's to a StoreProduct collection in my Store and Product classes that are set to Cascade.AllDeleteOrphan()
I was never able to be able to map the StoreProduct such that when it was saved by itself it would create a new Store and a new Product. I had to eventually map it like the above. So your Store or Product will need to exist before you actually create the relationship (StoreProduct) between them depending on which side you are creating your new StoreProduct from.
Edit:
You may also be able to map it like this to achieve what you are wanting:
public class Order_DetailMap : ClassMap<StoreProduct>
{
public Order_DetailMap()
{
Table("StoreProduct");
CompositeId()
.KeyReference(x => x.Store, "Store_id")
.KeyReference(x => x.Product, "Product_id");
References(x => x.Store, "Store_id")
.Not.Update()
.Not.Insert()
.Cascade.All();
References(x => x.Product, "Product_id")
.Not.Update()
.Not.Insert()
.Cascade.All();
Map(x => x.ExtraBit);
}
}

no persister for: Fluent nHibernate Exception

i m getting the exception "No persister for: MVCTemplate.Common.Entities.User" . I Google this issue and apply all the solution i found. but all are useless for me.
Does anyone know what i m doing wrong ?
my User Class code is
public class User
{
public virtual Guid UserID { get; private set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual string FullName { get; set; }
public virtual string Email { get; set; }
public virtual TimeSpan LastLogin { get; set; }
public virtual bool IsActive { get; set; }
public virtual DateTime CreationDate { get; set; }
public virtual IList<UserInRole> UserInRoles { get; set; }
}
User Mapping :
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("tblUsers");
Id(user => user.UserID).GeneratedBy.GuidComb();
Map(user => user.UserName).Not.Nullable();
Map(user => user.Password).Not.Nullable();
Map(user => user.FullName).Not.Nullable();
Map(user => user.Email).Not.Nullable();
Map(user => user.LastLogin).Not.Nullable();
Map(user => user.IsActive).Nullable();
Map(user => user.CreationDate).Not.Nullable();
HasMany(user => user.UserInRoles);
}
}
FNH Configuration :
return Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("FNHConnection"))
)
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<User>())
.BuildSessionFactory();
Thanks
Double check that your mapping class is public.
Check that you have something like this in your fluent config....
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>())
The following will cause this error in simple terms:
private static void MainFN()
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
Data[] balance = new Data[12];
for (int i = 0; i < 12; i++)
{
balance[i] = new Data();
balance[i].Test1 = "Example Data " + (i + 1).ToString();
balance[i].Test2 = i + 11;
balance[i].Test3 = (i % 2 == 0);
session.SaveOrUpdate(balance[i]); //Should be like this
}
//session.SaveOrUpdate(balance); //This will give the error