query many-to-many nhibernate when only one side is mapped - nhibernate

I have the following entities
public class Client
{
public virtual int Id{get;set;}
public virtual IList<Telephone> Telephones { get; private set; }
}
public class User
{
public virtual int Id{get;set;}
public virtual IList<Telephone> Telephones { get; private set; }
}
public class Telephone
{
public virtual int Id{get;set;}
public virtual string Number { get; set; }
public virtual string Extension { get; set; }
public virtual TelephoneType TelephoneType { get; set; }
}
Client as mapping like this
HasManyToMany<Telephone>(x => x.Telephones)
.Table("tblClientTel")
.ParentKeyColumn("ClientId")
.ChildKeyColumn("TelId")
.LazyLoad()
.Cascade.SaveUpdate();
User as mapping like this
HasManyToMany<Telephone>(x => x.Telephones)
.Table("tblUserTel")
.ParentKeyColumn("UserId")
.ChildKeyColumn("TelId")
.LazyLoad()
.Cascade.SaveUpdate();
And Telephone like this
public TelephoneMap()
{
Table("tblTel");
Id(x => x.Id, "Id");
LazyLoad();
References<TelephoneType>(x => x.TelephoneType, "TypeId")
.Not.Nullable()
.Cascade.None()
.Not.LazyLoad();
Map(x => x.Number, "Number")
.Not.Nullable()
.Length(15);
Map(x => x.Extension, "Extension")
.Nullable()
.Length(10);
}
How can i query for all the telephone entities of a list of client ?
I've tried this
ICriteria criteria = base.CreateCriteria<Client>(null);
return base.CreateCriteria
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("Telephones"))
)
.Add(Expression.In("Id", clientIds))
.SetFetchMode("Telephones", FetchMode.Eager)
.List<Telephone>();
But it returns the client Id

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.

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.

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

Fluent NHibernate Mapping Error

I am getting the following error using Fluent:
12:16:47,879 ERROR [ 7]
Configuration [(null)]- An association
from the table Address refers to an
unmapped class: System.Int32
NHibernate.MappingException: An
association from the table Address
refers to an unmapped class:
System.Int32
public class Address {
public Address() {
}
public virtual int AddressId {
get;
set;
}
public virtual string AddressLine1 {
get;
set;
}
public virtual string AddressLine2 {
get;
set;
}
public virtual string AddressLine3 {
get;
set;
}
public virtual string BuildingNumber {
get;
set;
}
public virtual string City {
get;
set;
}
public virtual string County {
get;
set;
}
public virtual System.DateTime MovedIn {
get;
set;
}
public virtual System.DateTime MovedOut {
get;
set;
}
public virtual int PersonId {
get;
set;
}
public virtual string PostCode {
get;
set;
}
}
public class AddressMap : ClassMap<Address> {
public AddressMap() {
Table("Address");
LazyLoad();
Id(x => x.AddressId).GeneratedBy.HiLo("1000");
Map(x => x.AddressLine1).Length(100).Not.Nullable();
Map(x => x.AddressLine2).Length(100).Not.Nullable();
Map(x => x.AddressLine3).Length(100).Not.Nullable();
Map(x => x.BuildingNumber).Length(250).Not.Nullable();
Map(x => x.City).Length(250).Not.Nullable();
Map(x => x.County).Length(250).Not.Nullable();
Map(x => x.MovedIn).Not.Nullable();
Map(x => x.MovedOut).Not.Nullable();
References(x => x.PersonId).Column("PersonId").Not.Nullable();
Map(x => x.PostCode).Length(15).Not.Nullable();
}
}
[TestFixture]
public class TestBase
{
protected SessionSource SessionSource { get; set; }
protected ISession Session { get; private set; }
[SetUp]
public void SetupContext()
{
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(
"Data Source=localhost;Initial Catalog=ted;User ID=sa;Password=xxxx;"));
SessionSource = new SessionSource(cfg.BuildConfiguration()//**Error Here**
.Properties, new TestModel());
Session = SessionSource.CreateSession();
SessionSource.BuildSchema(Session);
}
[TearDown]
public void TearDownContext()
{
Session.Close();
Session.Dispose();
}
}
I get an error on my initial configuration, I have been over it a few times and I am really unsure what exactly I am doing wrong? Can anyone see anything Obvious? I can confirm there is only 2 int's in the database for this table. AddressId - Non identity PK and PersonId non identity FK
You should have a Person property of type Person, not an id.
Suggested read: https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started

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.