Nhibernate SubClass relationship generating wrong SQL - nhibernate

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.

Related

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

two class reference eachother , how to do Not.LazyLoad in fluent nhibernate

i have two class
public class ProInfo
{
public ProInfo()
{
ProPrices = new List<ProPrice>();
}
public virtual Guid ProID { get; set; }
public virtual string ProCate { get; set; }
public virtual string Name { get; set; }
public virtual string Unit { get; set; }
public virtual string PicName { get; set; }
public virtual string Memo { get; set; }
public virtual bool DeleteFlag { get; set; }
public virtual DateTime LastUpDateTime { get; set; }
public virtual IList<ProPrice> ProPrices { get; set; }
}
public class ProPrice
{
public virtual int Id { get; set; }
public virtual AreaInfo AreaInfo { get; set; }
public virtual ProInfo ProInfo { get; set; }
public virtual Decimal Price { get; set; }
}
mapping codes are :
public ProInfoMap()
{
Id(x => x.ProID);
Map(x => x.DeleteFlag);
Map(x => x.Name);
Map(x => x.PicName);
Map(x => x.ProCate);
Map(x => x.Unit);
Map(x => x.LastUpDateTime);
HasMany<ProPrice>(x => x.ProPrices);
}
public ProPriceMap()
{
Id(x => x.Id);
Map(x => x.Price);
References<ProInfo> (x => x.ProInfo);
References<AreaInfo>(x => x.AreaInfo);
}
what i want is to disable the proprices's lazyload(), so i can get all the prices for the product from database. but, when i change the onetomany to this: HasMany<ProPrice>(x => x.ProPrices).Not.LazyLoad(), it cause an Infinite loop. what do i miss?
I don't know, where exactly the loop comes from, but your bidirectional association may cause this. You should declare one side as Inverse(). This can only be done in ProInfoMap, because it is a one-to-many relationship with a bidirectional association:
HasMany<ProPrice>(x => x.ProPrices).Inverse().Not.LazyLoad();
Try that. It may remove the infinite loop.

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;}

NHibernate / Fluent NHibernate Mapping

Is it possible to map the following situation?
A product class (currently a table)
An account class (currently a table)
An accountproduct class (currently a join table but with additional information related to a specific product and account)
What I'd ideally like is accountproduct to extend product and to be available from account as a property Products.
The product class would exist seperately and provide it's own peristance.
How about the following:
public class AccountProduct
{
public virtual int Id { get; set; }
public virtual DateTime Date { get; set; }
public virtual string Comments { get; set; }
public virtual Account Account { get; set; }
public virtual Product Product { get; set; }
public class AccountProductMap : ClassMap<AccountProduct>
{
public AccountProductMap()
{
Id(x => x.Id);
Map(x => x.Date);
Map(x => x.Comments);
References(x => x.Account);
References(x => x.Product);
}
}
}
public class Product
{
public virtual int Id { get; set; }
public virtual int Name { get; set; }
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
}
}
public class Account
{
public virtual int Id { get; set; }
public virtual int Name { get; set; }
public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
}
}

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.