Nhibernate databinding foreign key to lookupedit - nhibernate

I would like to databind the foreign key property Product.CategoryId to a Devexpess Lookupedit in Windows Forms Application.
So
lookEditCategory.DataBindings
.Add(new Binding("EditValue", Product, "CategoryId ", true,
DataSourceUpdateMode.OnPropertyChanged));
lookEditCategory.Properties.Columns.Clear();
lookEditCategory.Properties.NullText = "";
lookEditCategory.Properties.DataSource = CatCol;
lookEditCategory.Properties.ValueMember = "CategoryId";
lookEditCategory.Properties.DisplayMember = "CategoryName";
var col = new LookUpColumnInfo("CategoryName") { Caption = "Type" };
lookEditCategory.Properties.Columns.Add(col);
The problem is that Nhibernate does not expose the foreign key Product.CategoryId. Instead my entity and mapping are like this
public partial class Product
{
public virtual int ProductId { get; set; }
[NotNull]
[Length(Max=40)]
public virtual string ProductName { get; set; }
public virtual bool Discontinued { get; set; }
public virtual System.Nullable<int> SupplierId { get; set; }
[Length(Max=20)]
public virtual string QuantityPerUnit { get; set; }
public virtual System.Nullable<decimal> UnitPrice { get; set; }
public virtual System.Nullable<short> UnitsInStock { get; set; }
public virtual System.Nullable<short> UnitsOnOrder { get; set; }
public virtual System.Nullable<short> ReorderLevel { get; set; }
private IList<OrderDetail> _orderDetails = new List<OrderDetail>();
public virtual IList<OrderDetail> OrderDetails
{
get { return _orderDetails; }
set { _orderDetails = value; }
}
public virtual Category Category { get; set; }
public class ProductMap : FluentNHibernate.Mapping.ClassMap<Product>
{
public ProductMap()
{
Table("`Products`");
Id(x => x.ProductId, "`ProductID`")
.GeneratedBy
.Identity();
Map(x => x.ProductName, "`ProductName`")
;
Map(x => x.Discontinued, "`Discontinued`")
;
Map(x => x.SupplierId, "`SupplierID`")
;
Map(x => x.QuantityPerUnit, "`QuantityPerUnit`")
;
Map(x => x.UnitPrice, "`UnitPrice`")
;
Map(x => x.UnitsInStock, "`UnitsInStock`")
;
Map(x => x.UnitsOnOrder, "`UnitsOnOrder`")
;
Map(x => x.ReorderLevel, "`ReorderLevel`")
;
HasMany(x => x.OrderDetails)
.KeyColumn("`ProductID`")
.AsBag()
.Inverse()
.Cascade.None()
;
References(x => x.Category)
.Column("`CategoryID`");
}
}
}
I cannot add the property CategoryID in my Product entity and mapping because then it will be mapped twice.
Is there any solution?

Yes. Do NOT use your domain entities in the UI.
Sometimes your UI doesn't need (and shouldn't be aware of) all the properties of your domain objects.
Other times, it needs DTOs that contain data from different domain sources (for example- a list of CourseNames for the Student screen), or, like in your case- it needs the data to be represented in a slightly different way.
So the best way would be to create your DTOs with all (and only) the properties needed by the UI.
See this SO question for further details.

Related

Many to Many Nhibernate - Duplicate records and no insert

I have three tables in a many to many relationship with the nhibernate maps below. My objects are also below. A portfolio item can have many tags. The problem I am having is
1) update save another tag even when the name is the same as last time. So duplicate records get inserted into tag when the tag is the same. So for example if the tag for one portfolio object was abc the next portfolio item that adds the tag should reference this record rather than reinserting abc. I think this is because of the id column in the tag map. Nhibernate needs an id though.
2) Create does not add records in the join table. Records in the join table are only added on updates.
Domain Objects
public class Portfolio {
public Portfolio() {
PortfolioImage = new List<Portfolioimage>();
Tag = new List<Tag>();
}
public virtual int PortfolioId { get; set; }
public virtual string AliasTitle { get; set; }
public virtual string MetaDescription { get; set; }
public virtual string Title { get; set; }
public virtual string Client { get; set; }
public virtual string Summary { get; set; }
public virtual string Url { get; set; }
public virtual string MainImage { get; set; }
public virtual string TitleAlt { get; set; }
public virtual string Description { get; set; }
public virtual IList<Portfolioimage> PortfolioImage { get; set; }
public virtual IList<Tag> Tag { get; set; }
}
public class Portfoliotag {
public virtual int Id { get; set; }
public virtual Portfolio Portfolio { get; set; }
public virtual Tag Tag { get; set; }
}
public class Tag {
public Tag() {
Portfolio = new List<Portfolio>();
}
public virtual int TagId { get; set; }
public virtual string TagVal { get; set; }
public virtual IList<Portfolio> Portfolio { get; set; }
}
Maps
public class PortfolioMap : ClassMap<Portfolio> {
public PortfolioMap() {
Table("Portfolio");
LazyLoad();
Id(x => x.PortfolioId).GeneratedBy.Identity().Column("PortfolioId");
Map(x => x.AliasTitle).Column("AliasTitle").Not.Nullable();
Map(x => x.MetaDescription).Column("MetaDescription").Not.Nullable();
Map(x => x.Title).Column("Title").Not.Nullable();
Map(x => x.Client).Column("Client").Not.Nullable();
Map(x => x.Summary).Column("Summary").Not.Nullable();
Map(x => x.Url).Column("Url");
Map(x => x.MainImage).Column("MainImage");
Map(x => x.TitleAlt).Column("TitleAlt");
Map(x => x.Description).Column("Description").Not.Nullable();
HasMany(x => x.PortfolioImage).KeyColumn("PortfolioId").Inverse();
HasManyToMany(x => x.Tag).Table("PortfolioTag").ParentKeyColumn("PortfolioId").ChildKeyColumn("TagId").LazyLoad().Cascade.All().Fetch.Join();
}
}
public class PortfoliotagMap : ClassMap<Portfoliotag> {
public PortfoliotagMap() {
Table("PortfolioTag");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
References(x => x.Portfolio).Not.Nullable().Cascade.SaveUpdate().Column("PortfolioId");
References(x => x.Tag).Not.Nullable().Cascade.SaveUpdate().Column("TagId");
}
}
public class TagMap : ClassMap<Tag> {
public TagMap() {
Table("Tag");
LazyLoad();
Id(x => x.TagId).GeneratedBy.Identity().Column("TagId");
Map(x => x.TagVal).Column("Tag").Not.Nullable();
//HasMany(x => x.PortfolioTag).KeyColumn("TagId");
// HasMany(x => x.PortfolioTag).Cascade.AllDeleteOrphan().Inverse().Fetch.Join().KeyColumn("TagId");
HasManyToMany(x => x.Portfolio).Table("PortfolioTag").ParentKeyColumn("PortfolioId").ChildKeyColumn("TagId").LazyLoad().Inverse().Cascade.AllDeleteOrphan();
}
}
given the classes
public class Portfolio
{
public Portfolio()
{
Tag = new List<Tag>();
}
public virtual int PortfolioId { get; protected set; }
public virtual IList<Tag> Tag { get; protected set; }
}
public class Tag
{
public virtual int TagId { get; set; }
public virtual string Name { get; set; }
}
the this mapping should suffice
public class PortfolioMap : ClassMap<Portfolio>
{
public PortfolioMap()
{
Id(x => x.PortfolioId).GeneratedBy.Identity().Column("PortfolioId");
HasManyToMany(x => x.Tag)
.Table("PortfolioTag")
.ParentKeyColumn("PortfolioId")
.ChildKeyColumn("TagId")
.Cascade.All()
.Fetch.Join();
}
}
assigning existing tags has to be handled in code which can cache or query involved tags more efficiently than automatic querying by any framework.
Update:
example usage which works for me
public void Save(int portfolioId, IEnumerable<string> tagnames)
{
using (var tx = session.BeginTransaction())
{
var tags = Session.QueryOver<Tag>().WhereProperty(x => x.Name).In(tagnames).List();
var portfolio = session.Get<Portfolio>(portfolioId);
portfolio.Tags.Clear();
portfolio.Tags.AddRange(tags);
tx.Commit();
}
}

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.

Fluent Nhibernate - search for an item based on value of a many to many relationship

Hopefully the title of this question makes sense, if not, here is my elaboration.
With two entities, Brand and Affiliate and a many-to-may relationship between them i would like to be able to use a query to find the Affiliates where the BrandName is a variable value.
Here is the Affiliate class and Affiliate MapClass (simplified of course)
public class Affiliate
{
public virtual int Id { get; private set; }
public virtual DateTime DateReceived { get; set; }
public virtual IList<Brand> Brands { get; set; }
public Affiliate()
{
Brands = new List<Brand>();
}
}
public class AffiliateApplicationRecordMap : ClassMap<Affiliate>
{
public AffiliateApplicationRecordMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateReceived, "TimeStampCreated");
HasManyToMany(x => x.Brands)
.Cascade.All()
.ParentKeyColumn("AffiliateID")
.ChildKeyColumn("BrandID")
.Table("AffiliateBrand");
}
}
There is a mapping table called AffiliateBrand which provides the many to many mapping.
Here is the Brand class and ClassMap
public class Brand
{
public virtual int ID { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Affiliate> Affiliates{ get; set; }
public Brand()
{
Affiliates = new List<Affiliate>();
}
public virtual void AddAffiliateApplication(Affiliate affiliate)
{
affiliate.Brands.Add(this);
Brands.Add(affiliate);
}
}
public class BrandMap : ClassMap<Brand>
{
public BrandMap()
{
Id(x => x.ID).GeneratedBy.Identity();
Map(x => x.Name);
HasManyToMany(x => x.Affiliates)
.Cascade.All()
.Inverse()
.ParentKeyColumn("BrandID")
.ChildKeyColumn("PartnerID")
.Table("AffiliateBrand");
}
}
Now i'm tyring to write this query with NHibernate:
var result = session
.CreateCriteria(typeof(Partner))
.AddOrder(Order.Asc("DateReceived"))
.Add(Restrictions.Eq("Brands.Name", brandName))
.SetMaxResults(10)
.List<Partner>();
Now clearly this isn't working and i didn't really think it would. What i'm trying to do is get all Affiliates back where the Brand has a specific name. How do i write this query?
You need to add a join to your criteria using CreateAlias
var result = session
.CreateCriteria(typeof(Partner))
.AddOrder(Order.Asc("DateReceived"))
.CreateAlias("Brands", "brand")
.Add(Restrictions.Eq("brand.Name", brandName))
.SetMaxResults(10)
.List<Partner>();

FluentNhibernate HasManytoMany Relation - It doesnt add into the link table

Using fluentnhibernate i am having a problem with the link table insertion.
Here is my entities
public partial class Item
{
public virtual int Id
{
get;
set;
}
public virtual string Description
{
get;
set;
}
public virtual IList<Category> Categories
{
get;
set;
}
}
public partial class Category
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual string Description
{
get;
set;
}
public virtual IList<Item> Items
{
get;
set;
}
}
Here is my mappings.
public class ItemMapping : ClassMap<Item>
{
public ItemMapping()
{
Table("Item");
Schema("dbo");
Id(x => x.Id);
Map(x => x.Description);
HasManyToMany(x => x.Categories)
.ChildKeyColumn("Item_id")
.ParentKeyColumn("Category_id")
.Table("CategoriesToItems")
.AsSet();
}
}
public class CategoryMapping : ClassMap<Category>
{
public CategoryMapping()
{
Table("Category");
Schema("dbo");
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.Name);
HasManyToMany(x => x.Items)
.ChildKeyColumn("Category_id")
.ParentKeyColumn("Item_id")
.Table("CategoriesToItems")
.AsSet();
}
}
Here is how i add it to collection in my mvc page
var category = CategoryTask.Query(x => x.Id == post.Category).FirstOrDefault();
var item = new Item
{
Categories = new List<Category> { category },
Tags = tags
};
ItemTasks.Save(item);
My question is why it doesnt add the relations in my link table "CategoriesToItems". The table is already in the database with Category_Id (FK, int, not null) and Item_Id (FK, int, not null).
Where is the problem? why it doesnt add it to relation table?
It's hard to say what's really wrong when we can't see what your ItemTasks.Save does under the covers. Are you wrapping your save in a transaction? If not, you should be.
You should call Session.Flush() just before the transaction.Commit() as well.
I am not certain if the problem has been solved, but it looks similar to my problem (fluentnhibernate hasmanytomany same identifier exception).
Also, it looks like your parent and child key columns are backward.