FluentNHibernate tricky mapping - fluent-nhibernate-mapping

When I load data everything is mapped and loaded correctly, but when I try to insert object graph with Period, PaymentType and CalendarEntry it throws an exception:
{"Cannot insert the value NULL into column 'PaymentTypeId', table 'CashFlowCalculator.dbo.CalendarEntries'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}
The reason is that there is no value for the foreign key column of the object that tries to insert. I can't where is the mistake in my mappings?
Objects
public class Period
{
public virtual int Id { get; private set; }
public virtual DateTime StartDate { get; set; }
public virtual DateTime EndDate { get; set; }
public virtual double OpeningCash { get; set; }
public virtual IList<CalendarEntry> CalendarEntries { get; set; }
public Period()
{
CalendarEntries = new List<CalendarEntry>();
}
}
public class PaymentType
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual DateTime StartDate { get; set; }
public virtual DateTime EndDate { get; set; }
public virtual bool IsIncome { get; set; }
public virtual IList<CalendarEntry> CalendarEntries { get; set; }
public PaymentType()
{
CalendarEntries = new List<CalendarEntry>();
}
}
public class CalendarEntry
{
public virtual int Id { get; private set; }
public virtual double Amount { get; set; }
public virtual DateTime DueDate { get; set; }
public virtual PaymentType PaymentType { get; set; }
public virtual Period Period { get; set; }
public CalendarEntry(){ }
public virtual void AddPaymentType(PaymentType paymentType)
{
paymentType.CalendarEntries.Add(this);
PaymentType = paymentType;
}
public virtual void AddPeriod(Period period)
{
period.CalendarEntries.Add(this);
Period = period;
}
}
Mappings
public PeriodMap()
{
Table("Periods");
Id(x => x.Id);
Map(x => x.OpeningCash);
Map(x => x.StartDate);
Map(x => x.EndDate);
HasMany(x => x.CalendarEntries).LazyLoad().Inverse().Cascade.All();
}
public PaymentTypeMap()
{
Table("PaymentTypes");
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Description);
Map(x => x.StartDate);
Map(x => x.EndDate);
Map(x => x.IsIncome);
HasMany(x => x.CalendarEntries).LazyLoad().Inverse().Cascade.All();
}
public CalendarEntryMap()
{
Table("CalendarEntries");
Id(x => x.Id);
Map(x => x.Amount);
Map(x => x.DueDate);
References(x => x.PaymentType).Column("PaymentTypeId");
References(x => x.Period).Column("PeriodId");
}

If you want a "nullable" PaymentType property of your on your CalendarEntry mapping, I think you have to specify it:
public CalendarEntryMap()
{
Table("CalendarEntries");
Id(x => x.Id);
Map(x => x.Amount);
Map(x => x.DueDate);
References(x => x.PaymentType).Column("PaymentTypeId").Nullable();
References(x => x.Period).Column("PeriodId");
}
Instead, if you don't want it nullable, you have to manually set the PaymentType property of any CalendarEntry instance.

This is the code that I use to set the object graph:
Period period = new Period
{
OpeningCash = 2000,
StartDate = new DateTime(2011, 1, 1),
EndDate = new DateTime(2011, 12, 1)
};
PaymentType type = new PaymentType
{
Description = "Description",
IsIncome = false,
Name = "Name",
StartDate = new DateTime(2010, 11, 23),
EndDate = new DateTime(2012, 11, 23)
};
CalendarEntry entry = new CalendarEntry
{
Amount = 232,
DueDate = new DateTime(2011, 7, 23)
};
entry.AddPeriod(period);
entry.AddPaymentType(type);

Related

Nhibernate SubClass relationship generating wrong SQL

I'm using Table Per Type(TPT) to do inheritance in Nhibernate v3.3.1.4000. I figured out that any SQL for SubClass relationships one-to-many are wrong.
Class:
public class Repasse : Colaboracao
{
public virtual string Descricao { get; set; }
public virtual Decimal ValorRepasse { get; set; }
public virtual DateTime DataInicio { get; set; }
public virtual DateTime DataTermino { get; set; }
[XmlIgnore]
public virtual Usuario UsuarioExecucao { get; set; }
[XmlIgnore]
public virtual Usuario UsuarioMonitoramento { get; set; }
[XmlIgnore]
public virtual IList<RepasseRecebido> RepassesRecebidos { get; set; }
[XmlIgnore]
public virtual IList<RepasseProduto> Produtos { get; set; }
public Repasse()
{
RepassesRecebidos = new List<RepasseRecebido>();
Produtos = new List<RepasseProduto>();
UsuarioExecucao = new Usuario();
UsuarioMonitoramento = new Usuario();
}
}
public class RepasseRecebido : Entidade
{
public virtual string NumNotaAtorizacao { get; set; }
public virtual Decimal ValorTaxaCambio { get; set; }
public virtual DateTime DataRecebimento { get; set; }
public virtual Decimal ValorRecebido { get; set; }
public virtual string Observacao { get; set; }
[XmlIgnore]
public virtual Repasse Repasse { get; set; }
[XmlIgnore]
public virtual List<Download> Downloads { get; set; }
public RepasseRecebido()
{
Repasse = new Repasse();
Downloads = new List<Download>();
}
}
public class RepasseProduto : Entidade
{
public virtual int QtdProduto { get; set; }
public virtual DateTime DataInicio { get; set; }
public virtual DateTime DataTermino { get; set; }
public virtual int QtdBeneficiado { get; set; }
public virtual PublicoBeneficiado PublicoBeneficiado { get; set; }
[XmlIgnore]
public virtual Produto Produto { get; set; }
[XmlIgnore]
public virtual Repasse Repasse { get; set; }
[XmlIgnore]
public virtual IList<RepasseProdutoAtividade> Atividades { get; set; }
public RepasseProduto()
{
Produto = new Produto();
Repasse = new Repasse();
Atividades = new List<RepasseProdutoAtividade>();
}
}
public class RepasseProdutoAtividade : Entidade
{
public virtual DateTime DataInicio { get; set; }
public virtual DateTime DataTermino { get; set; }
public virtual string Descricao { get; set; }
[XmlIgnore]
public virtual RepasseProduto RepasseProduto { get; set; }
public RepasseProdutoAtividade()
{
RepasseProduto = new RepasseProduto();
}
}
Map:
public class RepasseMap : SubclassMap<Repasse>
{
public RepasseMap()
{
Schema(ConfigInstances.ObterSchema("Sigma"));
Table("tblRepasse");
LazyLoad();
KeyColumn("cmpIdRepasse");
Map(x => x.Descricao).Column("cmpDcDescricao");
Map(x => x.ValorRepasse).Column("cmpVlValorRepasse").Not.Nullable();
Map(x => x.DataInicio).Column("cmpDtDataInicio").Not.Nullable();
Map(x => x.DataTermino).Column("cmpDtDataTermino").Not.Nullable();
References(x => x.UsuarioExecucao).Column("cmpIdUsuarioExecucao");
References(x => x.UsuarioMonitoramento).Column("cmpIdUsuarioMonitoramento");
HasMany(x => x.RepassesRecebidos).AsBag().LazyLoad();
HasMany(x => x.Produtos).KeyColumn("cmpIdRepasseProduto").AsBag().LazyLoad();
}
public class RepasseRecebidoMap : ClassMap<RepasseRecebido>
{
public RepasseRecebidoMap()
{
Schema(ConfigInstances.ObterSchema("Sigma"));
Table("tblRecursoRepasse");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("cmpIdRecursoRepasse");
Map(x => x.NumNotaAtorizacao).Column("cmpNuAutorizacaoNota").Not.Nullable();
Map(x => x.DataRecebimento).Column("cmpDtDataRecebimento").Not.Nullable();
Map(x => x.ValorRecebido).Column("cmpVlValorRecebido").Not.Nullable();
Map(x => x.ValorTaxaCambio).Column("cmpVlTaxaCambio").Not.Nullable();
Map(x => x.Observacao).Column("cmpDcObservacao");
References(x => x.Repasse).Column("cmpIdRepasse");
HasManyToMany(x => x.Downloads)
.Table("tblRecursoRepasseDownload")
.ParentKeyColumn("cmpIdRecursoRepasse")
.ChildKeyColumn("cmpIdDownload")
.AsBag().LazyLoad().Cascade.SaveUpdate();
}
}
public class RepasseProdutoMap : ClassMap<RepasseProduto>
{
public RepasseProdutoMap()
{
Schema(ConfigInstances.ObterSchema("Sigma"));
Table("tblRepasseProduto");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("cmpIdRepasseProduto");
Map(x => x.DataInicio).Column("cmpDtDatainicio").Not.Nullable();
Map(x => x.DataTermino).Column("cmpDtDatatermino").Not.Nullable();
Map(x => x.QtdProduto).Column("cmpInQuantidadeproduto").Not.Nullable();
Map(x => x.QtdBeneficiado).Column("cmpIdQuantidadeBeneficiado").Not.Nullable();
Map(x => x.PublicoBeneficiado).Column("cmpIdPublicoBeneficiado").CustomType(typeof(PublicoBeneficiado));
References(x => x.Produto).Column("cmpIdProduto");
References(x => x.Repasse).Column("cmpIdRepasse");
HasMany(x => x.Atividades).AsBag().LazyLoad();
}
}
public class RepasseProdutoAtividadeMap : ClassMap<RepasseProdutoAtividade>
{
public RepasseProdutoAtividadeMap()
{
Schema(ConfigInstances.ObterSchema("Sigma"));
Table("tblRepasseProdutoAtividade");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("cmpIdRepasseProdutoAtividade");
Map(x => x.DataInicio).Column("cmpDtInicio").Not.Nullable();
Map(x => x.DataTermino).Column("cmpDtTermino").Not.Nullable();
Map(x => x.Descricao).Column("cmpDcAtividade").Not.Nullable();
References(x => x.RepasseProduto).Column("cmpIdRepasseProduto");
}
}
}
When I query RepasseProduto then SQL is generated with an issue, the query doesn't findout how to get the Foreign Key name for the one-to-many relationship, as you can see below:
select repasse0_.cmpIdRepasse as cmpIdCol1_5_
repasse0_1_.cmpDtDataCriacao as cmpDtDat2_5_
repasse0_1_.cmpStSituacao as cmpStSit3_5_
repasse0_1_.cmpDcCancelamento as cmpDcCan4_5_
repasse0_1_.cmpIdRecurso as cmpIdRec5_5_
repasse0_.cmpDcDescricao as cmpDcDes2_7_
repasse0_.cmpVlValorRepasse as cmpVlVal3_7_
repasse0_.cmpDtDataInicio as cmpDtDat4_7_
repasse0_.cmpDtDataTermino as cmpDtDat5_7_
repasse0_.cmpIdUsuarioExecucao as cmpIdUsu6_7_
repasse0_.cmpIdUsuarioMonitoramento as cmpIdUsu7_7_
from mre_sigma_dsv.dbo.tblRepasse repasse0_
inner join mre_sigma_dsv.dbo.tblColaboracao repasse0_1_
on repasse0_.cmpIdRepasse=repasse0_1_.cmpIdColaboracao
where (select cast(count(*) as INT)
from mre_sigma_dsv.dbo.tblRepasseProduto produtos1_
where repasse0_.cmpIdRepasse=produtos1_.Repasse_id)>#p0
As you can see the last inner select doesn't find out the name of the foreign key from tblRepasseProduto to tblRepasse, then uses Repasse_id that doesn't represent a valid table field.
What should I do? Am I missing something when mapping my sub-class relation?
Really not sure from where the ghost column comes, but there is other issue in the mapping above. In relations one-to-many vs many-to-one, i.e. in fluent HasMany vs References, there must be/is exactly one column representing that. So we need to use the same (one) column for that and should change the mapping this way.
The RepasseMap : SubclassMap<Repasse>:
public RepasseMap()
{ ...
// was:
// HasMany(x => x.RepassesRecebidos).AsBag().LazyLoad();
// HasMany(x => x.Produtos).KeyColumn("cmpIdRepasseProduto").AsBag().LazyLoad();
// should be
HasMany(x => x.RepassesRecebidos)
.KeyColumn("cmpIdRepasse").AsBag().LazyLoad();
HasMany(x => x.Produtos)
.KeyColumn("cmpIdRepasse").AsBag().LazyLoad();
}
The RepasseRecebidoMap : ClassMap<RepasseRecebido>:
public RepasseRecebidoMap()
{ ...
// the same column representing this relationship in table "tblRecursoRepasse"
References(x => x.Repasse).Column("cmpIdRepasse");
The RepasseProdutoMap : ClassMap<RepasseProduto>:
public RepasseProdutoMap()
{ ...
// the same column representing this relationship in table "tblRepasseProduto"
References(x => x.Repasse).Column("cmpIdRepasse");
If this does not help, could you please send the query you've used and which generated that strange relationship.
Also, in the Mapping snippet above you are nesting the mapping classes ... this is not the right/required way.

FluentNhibernate .CheckReference is failing

public class WorldEntity
{
public WorldEntity()
{
Scenes = new List<SceneEntity>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Picture { get; set; }
public virtual IList<SceneEntity> Scenes { get; set; }
}
public class WorldMap : ClassMap<WorldEntity>
{
public WorldMap()
{
Table("Worlds");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name);
Map(x => x.Picture);
HasMany(x => x.Scenes).KeyColumn("Id");
}
}
public class SceneEntity
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual string Image { get; set; }
//public virtual int WorldId { get; set; }
public virtual WorldEntity World { get; set; }
public virtual short NoExits { get; set; }
public virtual string AnimatedIntroPath { get; set; }
}
public class SceneMap: ClassMap<SceneEntity>
{
public SceneMap()
{
Table("Scenes");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name);
Map(x => x.Image);
Map(x => x.NoExits);
Map(x => x.AnimatedIntroPath);
//Map(x => x.WorldId).Not.Nullable();
References(wrd => wrd.World, "WorldId");
}
}
[Test]
new PersistenceSpecification<SceneEntity>(Session)
.CheckProperty(x => x.Name, "Scene Name")
.CheckProperty(x => x.Image, "path to image")
//.CheckProperty(x=>x.WorldId,aa.Id)
.CheckReference(x => x.World,aa )
.VerifyTheMappings();
after running the test I am getting this error:
System.ApplicationException : For property 'World' expected same element, but got different element of type 'TwitQuestNet.DataDefinitions.OrmConfig.Entities.WorldEntity'.
Tip: override ToString() on the type to find out the difference.
at FluentNHibernate.Testing.Values.Property2.CheckValue(Object target)
at System.Collections.Generic.List1.ForEach(Action1 action)
at FluentNHibernate.Testing.PersistenceSpecification1.VerifyTheMappings(T first)
at TwitQuestNet.Test.EntityMapTests.SceneTest.scene_map_succsess() in SceneTest.cs: line 23
what I am doing wrong here ? as I am stuck for nearly a day :(
Sorted. I had to add EqualityComparer() the the test will pass

creating in query in nhibernate using linq

i want to create something like
SELECT * FROM dbo.localconveyance_details WHERE
voucherNo IN (SELECT voucherNo FROM dbo.localconveyance_master WHERE emp_code = '48'
using linq in fluent nhibernate
tried something like this
IList<LocalConveyanceDetails> detailslist = session.Query<LocalConveyanceDetails>()
.Where(x => x.LocalConveyanceMaster.emp_code == e_id).ToList();
but its not working ... can someone tell what would be the actual query ?
Update :
the Entities which i have used are :
public class LocalConveyanceMaster
{
public virtual String voucherNo { get; set; }
public virtual DateTime voucher_date { get; set; }
public virtual String emp_code { get; set; }
public virtual String emp_name { get; set; }
public virtual String project_id { get; set; }
public virtual DateTime submitDate { get; set; }
public virtual String to_be_approved_by { get; set; }
public virtual String created_by { get; set; }
public virtual Decimal conveyance_total { get; set; }
public virtual Decimal approved_amount { get; set; }
public virtual String approved { get; set; }
public virtual ProjectMaster Project { get; set; }
public virtual ICollection<LocalConveyanceDetails> LocalConveyanceDetails { get; set; }
}
public class LocalConveyanceDetails
{
public virtual String LcDetailsId { get; set; }
public virtual String voucherNo { get; set; }
public virtual String serialNo { get; set; }
public virtual String From_Project_Id { get; set; }
public virtual String To_Project_Id { get; set; }
public virtual DateTime particularsDate { get; set; }
public virtual Decimal particularsAmount { get; set; }
public virtual String particulars { get; set; }
public virtual LocalConveyanceMaster LocalConveyanceMaster { get; set; }
}
and the mappings are :
public LocalConveyanceMap()
{
Table("localconveyance_master");
Id(x => x.voucherNo).Column("voucherNo");
Map(x => x.voucher_date);
Map(x => x.emp_code);
Map(x => x.emp_name);
Map(x => x.project_id);
Map(x => x.submitDate);
Map(x => x.to_be_approved_by);
Map(x => x.created_by);
Map(x => x.conveyance_total);
Map(x => x.approved_amount);
Map(x => x.approved);
References(x => x.Project)
.Column("project_id")
.ForeignKey("project_id");
HasMany(x => x.LocalConveyanceDetails)
.KeyColumn("voucherno").AsSet();
}
public LocalConveyanceDetailsMap()
{
Table("Localconveyance_details");
Id(x => x.LcDetailsId).Column("LcDetailsId");
Map(x => x.voucherNo);
Map(x => x.serialNo);
Map(x => x.From_Project_Id);
Map(x => x.To_Project_Id);
Map(x => x.particularsDate);
Map(x => x.particularsAmount);
Map(x => x.particulars);
References(x => x.LocalConveyanceMaster)
.PropertyRef(x => x.voucherNo).Column("voucherno")
.ForeignKey("voucherno");
}
the error which i am getting is :
Exception : {"Error performing LoadByUniqueKey[SQL: SQL not available]"}
InnerException : {"The given key was not present in the dictionary."}
that's occurred when the one or more member of class is kind of other class [relationship].
i have a same problem and i remove that members and add new member for each of them in this form :
public virtual int? [related class]id {get;set;}
exmaple :
public virtual int? PersonId {get;set;}

How to enable LazyLoad in Fluent NHibernate?

I'm testing Fluent NHibernate with NorthWind database. Now, I've created Employee and EmployeeMap class. Source code is like below.
class Employee
public class Employee
{
public virtual int EmployeeID { get; private set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual string Title { get; set; }
public virtual string TitleOfCourtesy { get; set; }
public virtual DateTime? BirthDate { get; set; }
public virtual DateTime? HireDate { get; set; }
public virtual string Address { get; set; }
public virtual string City { get; set; }
public virtual string Region { get; set; }
public virtual string PostalCode { get; set; }
public virtual string Country { get; set; }
public virtual string HomePhone { get; set; }
public virtual string Extension { get; set; }
public virtual string Notes { get; set; }
public virtual Employee ReportsTo { get; set; }
public virtual string PhotoPath { get; set; }
public virtual IList<Territory> Territories{ get; set; }
public Employee()
{
Territories = new List<Territory>();
}
public virtual void AddTerritory(Territory territory)
{
territory.Employees.Add(this);
this.Territories.Add(territory);
}
}
class EmployeeMap
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employees");
Id(x => x.EmployeeID);
Map(x => x.LastName);
Map(x => x.FirstName);
Map(x => x.Title);
Map(x => x.TitleOfCourtesy);
Map(x => x.BirthDate);
Map(x => x.HireDate);
Map(x => x.Address);
Map(x => x.City);
Map(x => x.Region);
Map(x => x.PostalCode);
Map(x => x.Country);
Map(x => x.HomePhone);
Map(x => x.Extension);
Map(x => x.Notes);
Map(x => x.PhotoPath);
References(x => x.ReportsTo).Column("ReportsTo").LazyLoad();
HasManyToMany(x => x.Territories)
.Cascade.All()
.Table("EmployeeTerritories")
.ParentKeyColumn("EmployeeID")
.ChildKeyColumn("TerritoryID");
}
}
Then I try to load all employees from database, but all employees have reference object on ReportsTo property.
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (session.BeginTransaction())
{
Console.WriteLine("All employees");
var emp_ = session.CreateCriteria(typeof(Employee));
var employees = emp_.List<Employee>();
foreach (var employee in employees)
{
Console.WriteLine(employee.FirstName); // every employee has reference object on ReportsTo property here.
}
Console.Write("--------");
}
}
I want to know, what wrong with my code and how to fixed it?
Lazy Load is enabled by default. The reference in ReportsTo is a proxy that will only be loaded from the DB if any property other than the ID is used.
Lazy loading is not enabled by default.
public EmployeeMap()
{
Table("Employees");
LazyLoad();
// etc.
}
That being said, testing with a foreach statement can be mischievous, because even if you have enabled lazy loading, the second you query for "employee.FirstName", NHibernate will hit the database and return the results. You're better off catching NHibernate's generated SQL or just using the NHibernate Profiler.

NHibernate Mapping Exception: An association from the table dbo.AccountGroup refers to an unmapped class: System.String

I am getting this error:
An association from the table dbo.AccountGroup refers to an unmapped class: System.String
This is my entity:
public class AccountGroup
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual string Parent { get; set; }
public virtual string Description { get; set; }
public virtual IList<Account> Accounts { get; set; }
public AccountGroup()
{
this.Accounts = new List<Account>();
}
}
public class Account
{
public virtual int Id { get; private set; }
public virtual string Code { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual int Category { get; set; }
public virtual AccountGroup Group { get; set; }
public virtual IList<LedgerEntry> LedgerEntries { get; set; }
public Account()
{
this.LedgerEntries = new List<LedgerEntry>();
}
}
This is my mapping:
public AccountGroupMap()
{
Table("dbo.AccountGroup");
Id(x => x.Id)
.Column("Id");
Map(x => x.Name);
References(x => x.Parent)
.Column("Parent");
Map(x => x.Description);
HasMany(x => x.Accounts)
.KeyColumn("GroupId")
.Inverse()
.Cascade.All();
}
}
public AccountMap()
{
Table("dbo.Account");
Id(x => x.Id)
.Column("Id");
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.Description);
Map(x => x.Category);
References(x => x.Group)
.Column("AccountGroupId");
HasMany(x => x.LedgerEntries)
.KeyColumn("AccountId")
.Inverse()
.Cascade.All();
}
Here are my tables:
CREATE TABLE AccountGroup
(
Id int PRIMARY KEY,
Name varchar(20),
Parent int,
Description varchar(20)
)
CREATE TABLE Account
(
Id int PRIMARY KEY,
Code varchar(30),
Name varchar(20),
Description varchar(20),
Category int,
AccountGroupId int,
FOREIGN KEY (AccountGroupId) REFERENCES AccountGroup (Id)
)
You have
References(x => x.Parent)
.Column("Parent");
When Parent is defined as
public virtual string Parent { get; set; }
You can't reference a string (unless it's a collection element)