I need to create a many to many circular relation with two objects in fluent api. And I don't know how to use fluent api to achieve that goal.
The relation should be one to many (required) and one to many (optionnel)
I tried this scenario and it's not working.
Here my class model:
[Table("Question")]
public class Question
{
public int ID { get; set; }
public int IDQuestion { get; set; }
public int IDForm { get; set; }
public virtual AnswerSelectionType AssociateAnswerSelectionType { get; set; }
public virtual ICollection<AnswerSelectionType> AnswerSelectionTypes { get; set; }
}
[Table("AnswerSelectionType")]
public class AnswerSelectionType
{
public int ID { get; set; }
public int IDQuestionForm { get; set; }
public int? IDAssociateQuestion { get; set; }
public int? DataValue { get; set; }
public virtual Question AssociateQuestion { get; set; }
public virtual Question QuestionForm { get; set; }
}
After that, here the fluent API
modelBuilder.Entity<AnswerSelectionType>()
.HasRequired(choixRep => choixRep.QuestionForm)
.WithMany(questionTe => questionTe.AnswerSelectionTypes)
.HasForeignKey(fk => fk.IDQuestionForm);
modelBuilder.Entity<AnswerSelectionType>()
.HasOptional(choixRep => choixRep.AssociateQuestion)
.WithMany(many => many.AnswerSelectionTypes)
.HasForeignKey(fk => fk.IDAssociateQuestion);
The error I got is: The navigation property declared on type has been configured with conflicting multiplicities.
Could you help me to find why I got the error?
Thank you very much!!
AnswerSelectionType would need to have a collection of the entity on the many side. I don't see any collection in AnswerSelectionType.
But I'm not sure what you mean by circular. What is the structure you have in mind?
Question --> 1:n --> AnswerSelectionType --> 1:n --> Question
In this case a property in AnswerSelectionType would help.
ICollection<Question> Questions { get; set; }
And then
.WithMany(many => many.Questions)
(Note, remove "NHEntity" from the text below)
Try this:
public class AnswerSelectionTypeNHEntityMapping : ClassMap<AnswerSelectionTypeNHEntity>
{
public AnswerSelectionTypeNHEntityMapping()
{
Schema("dbo");
Table("AnswerSelectionTypeNHEntity");
Id(x => x.AnswerSelectionTypeNHEntityKey).GeneratedBy.Increment();
Map(x => x.IDQuestionForm);
Map(x => x.IDAssociateQuestion).Not.Nullable();
Map(x => x.DataValue).Not.Nullable();
References<QuestionNHEntity>(x => x.QuestionForm)
.Class(typeof(QuestionNHEntity))
/*.Not.Nullable() */
.Nullable()
.Column("ParentQuestionFormKey")
.Index("IX_AnswerSelectionType_ParentQuestionFormKey")
.Cascade.SaveUpdate()
;
References<QuestionNHEntity>(x => x.AssociateQuestion )
.Class(typeof(QuestionNHEntity))
/*.Not.Nullable() */
.Nullable()
.Column("OneToOneAssociateQuestionKey")
.Index("IX_AnswerSelectionType_OneToOneAssociateQuestionKeyy")
.Cascade.SaveUpdate()
;
}
}
public class QuestionNHEntityMapping : ClassMap<QuestionNHEntity>
{
public QuestionNHEntityMapping()
{
Schema("dbo");
Table("QuestionNHEntity");
Id(x => x.QuestionNHEntityKey).GeneratedBy.Assigned();
Map(x => x.IDQuestion).Not.Nullable().Index("IX_Question_IDQuestion"); ;
Map(x => x.IDForm).Not.Nullable();
HasMany<AnswerSelectionTypeNHEntity>(x => x.AnswerSelectionTypes)
.Inverse()
.KeyColumns.Add("MyAnswerSelectionTypesColumnName")
;
References<AnswerSelectionTypeNHEntity>(x => x.AssociateAnswerSelectionType)
.Class(typeof(AnswerSelectionTypeNHEntity))
/*.Not.Nullable() */
.Nullable()
.Column("OneToOneAnswerSelectionTypeNHEntityKey")
.Index("IX_AnswerSelectionType_OneToOneAnswerSelectionTypeNHEntityKey")
.Cascade.SaveUpdate()
;
}
}
Related
Have Books class.
public class Books
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Category { get; set; }
public virtual string ISBN { get; set; }
public virtual string Description { get; set; }
public virtual string Image { get; set; }
public virtual int CategoryId { get; set; }
public Categories Categories { get; set; }
public virtual IList<Comments> Comments { get; set; }
public Books()
{
Comments = new List<Comments>();
}
}
Also Comments Class.
public class Comments
{
public virtual int Id { get; set; }
public virtual string CommentText { get; set; }
public virtual DateTime Date { get; set; }
public virtual int IdBook { get; set; }
public Books Books { get; set; }
}
My map code for Books class :
public class BooksMap : ClassMap <Books>
{
public BooksMap()
{
Id(x => x.Id);
Map(x => x.Title);
Map(x => x.ISBN);
Map(x => x.Description);
Map(x => x.Category);
Map(x => x.Image);
Map(x => x.CategoryId);
//reference to categories
References(x => x.Categories).Column("Id").Not.Nullable();
//inverse reference (hasmany comments, rating)
HasMany(x => x.Comments).Cascade.All().Inverse();
}
}
Also Comments map >
public class CommentsMap:ClassMap<Comments>
{
public CommentsMap()
{
Id(x => x.Id);
Map(x => x.CommentText);
Map(x => x.Date);
Map(x => x.IdBook);
References(x => x.Books).Column("Id").Not.Nullable();
}
}
My question is : am I doing it right? - and how to make queries with this mapings for example criteria language?
It would be nice to know what is NOT working (instead of asking "is this ok?")
There is some summary, what could be the way to properly map one-to-many and many-to-one:
Minimal and correct way to map one-to-many with NHibernate
Anyhow, all properties must be virtual
public class Books
{
public virtual int Id { get; set; }
...
public virtual int CategoryId { get; set; }
// all must be virtual
//public Categories Categories { get; set; }
public virtual Categories Categories { get; set; }
public virtual IList<Comments> Comments { get; set; }
As we can also see, there is CategoryId and Category - doubled mapping of one column (once as Reference once as ValueType). It means, that one of these must be readonly:
public BooksMap()
{
Id(x => x.Id);
...
// this should be readonly
// Map(x => x.CategoryId);
Map(x => x.CategoryId)
.Not.Update()
.Not.Insert();
//reference to categories
References(x => x.Categories)...
Reference should be mapped to column representing the Categroy_ID not the Id (which is already used for property x.Id)
// this does not seem to be ok
References(x => x.Categories).Column("Id").Not.Nullable();
// we would need different column
References(x => x.Categories)
.Column("Category_ID")
.Not.Nullable();
I would also expect, that in the Comments table is some column called "Book_ID". This is column, which we will use for HasMany() mapping.
HasMany(x => x.Comments)
.KeyColumn("Book_ID")
.Cascade.AllDeleteOrphan()
.Inverse();
The same column "Book_ID" must be used on the other side
public CommentsMap()
{
Id(x => x.Id);
...
References(x => x.Books)
.Column("Book_ID")
.Not.Nullable();
}
So, this mapping should be ready to use now. To get some ideas about querying, check the doc with lot of examples:
Chapter 15. Criteria Queries
Chapter 16. QueryOver Queries
I am working to map an existing database using Fluent NHibernate and have encountered a problem when it comes to complex many-to-many relationships (additional columns).
I know that many-to-many relationships with additional columns have to be mapped as HasMany rather than HasManyToMany as they are not pure many-to-many relationships. The linking table has to be mapped as a class within itself, which I have done in the example below.
When loading this data from an existing database it loads fine. The project I am working on takes this data and inserts it into an empty database, which is where the problem occurs. I think that when inserting into the new database the CompositeId is trying to insert NULL values for ItemID and ItemGroupID which is not allowed in the database. Changing the database structure is not a viable option at this point, is there a way around this issue?
Thanks, example code below.
Entity Classes
public class Item
{
public virtual long ItemID { get; set; }
public virtual string Name { get; set; }
}
public class ItemGroup
{
public virtual long ItemGroupID { get; set; }
public virtual string Name { get; set; }
public virtual IList<ItemInGroup> ItemsInGroup { get; set; }
}
public class ItemInGroup
{
public virtual Item Item { get; set; }
public virtual ItemGroup ItemGroup { get; set; }
public virtual int? DisplayOrder { get; set; }
}
Mapping Classes
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Table("Items");
Id(x => x.ItemID).GeneratedBy.Identity();
Map(x => x.Name);
}
}
public class ItemGroupMap : ClassMap<ItemGroup>
{
public ItemGroupMap()
{
Table("ItemGroups");
Id(x => x.ItemGroupID).GeneratedBy.Identity();
Map(x => x.Name);
HasMany(x => x.ItemsInGroup).KeyColumn("ItemGroupID").Cascade.All();
}
}
public class ItemInGroupMap : ClassMap<ItemInGroup>
{
public ItemInGroupMap()
{
Table("ItemsInGroups");
CompositeId().KeyReference(x => x.Item, "ItemID")
.KeyReference(x => x.ItemGroup, "ItemGroupID");
Map(x => x.DisplayOrder);
}
}
assuming DisplayOrder is the only extra column in the link table why not use the List index of IList as order?
public class ItemGroup
{
public virtual long ItemGroupID { get; set; }
public virtual string Name { get; set; }
public virtual IList<Item> Items { get; private set; }
}
public class ItemGroupMap : ClassMap<ItemGroup>
{
public ItemGroupMap()
{
Table("ItemGroups");
Id(x => x.ItemGroupID).GeneratedBy.Identity();
Map(x => x.Name);
HasManyToMany(x => x.ItemsInGroup)
.Table("ItemsInGroups")
.ParentKeyColumn("ItemGroupID")
.ChildKeyColumn("ItemID")
.AsList("DisplayOrder")
.Cascade.All();
}
}
This question already has answers here:
Fluent nHibernate: one-to-many relationship Issue
(2 answers)
Closed 9 years ago.
I have simple one-to-many relationship
public class Product
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Category { get; set; }
public virtual bool Discontinued { get; set; }
public virtual IList<ProductCampaignTargeting> Targetings { get; set; }
}
public class ProductCampaignTargeting
{
public virtual Guid ID { get; set; }
public virtual int TargetType { get; set; }
public virtual string TargetValue { get; set; }
public virtual Product Product { get; set; }
}
with Mapping:
class ProductCampaignTargetingMap : ClassMap<ProductCampaignTargeting>
{
public ProductCampaignTargetingMap()
{
Table("campaign_targetings");
Id(x => x.ID).GeneratedBy.Guid();
Map(x => x.TargetType).Column("target_type");
Map(x => x.TargetValue).Column("target_value");
References(x => x.Product).Column("campaign_id_fk");
}
}
class ProductMap: ClassMap<Product>
{
public ProductMap()
{
Table("Product");
Id(x => x.Id).Column("id").GeneratedBy.Guid();
Map(x => x.Name).Column("Name");
Map(x => x.Category);
// check why inverse doens't work
HasMany(x => x.Targetings).KeyColumn("campaign_id_fk").Cascade.AllDeleteOrphan().AsBag();
}
}
and it is working - but the child (many) table is updated with two commands - insert and then update
When i want to change it to one command I use the Inverse() option - but then The Foreign key is populated as null, what am i missing here?
The foreign key is null when you use Inverse because you have to maintain both sides of the relationship when adding a ProductCampaignTargeting to the collection:
target.Product = product;
product.Targetings.Add(target);
This is one of the reasons that many NHibernate developers recommend exposing the collection as IEnumerable or read-only and encapsulating the Add and Remove methods:
void AddTarget(ProductCampaignTargeting target)
{
target.Product = this;
Targetings.Add(target);
}
Trying to map a Order Class with two identical sub classes and Getting the following error
Could not find a getter for property 'RatingCriteria1' in class 'ORM.Entities.Enquiry.EnquiryOrder'
Basically I have one table in database with alot of columns. 12 columns are rating related
BuyerRatingCriteria1,BuyerRatingCriteria2, BuyerRatingCriteria3, BuyerRatingCriteria4, BuyerRatingCriteria5, BuyerRatingCriteriaComments
And
SupplierRatingCriteria1, SupplierRatingCriteria2, SupplierRatingCriteria3, SupplierRatingCriteria4, SupplierRatingCriteria5, SupplierRatingCriteriaComments
I want to simplify these into two subclasses on the so that i have
Order.SupplierRating.Criteria1
and
Order.BuyerRating.Criteria1
for example
public class EnquiryOrder : Entity
{
public virtual int Id { get; set; }
public virtual DateTime DateOrdered { get; set; }
public virtual string PONumber { get; set; }
public virtual string SONumber { get; set; }
public virtual Rating SupplierRatings { get; set; }
public virtual Rating BuyerRatings { get; set; }
}
public class Rating : Entity
{
public virtual int RatingCriteria1 { get; set; }
public virtual int RatingCriteria2 { get; set; }
public virtual int RatingCriteria3 { get; set; }
public virtual int RatingCriteria4 { get; set; }
public virtual int RatingCriteria5 { get; set; }
public virtual string RatingCriteriaComment { get; set; }
}
Mapping I thought would be as easy as this
public EnquiryOrderMapping()
{
Id(x => x.Id).Column("EnquiryOrderId");
Map(x => x.DateOrdered);
Map(x => x.PONumber).Nullable();
Map(x => x.SONumber).Nullable();
Map(x => x.BuyerRatings.RatingCriteria1).Column("EnquiryBuyerRatingsRatingCriteria1").Nullable();
Map(x => x.BuyerRatings.RatingCriteria2).Column("EnquiryBuyerRatingsRatingCriteria2").Nullable();
Map(x => x.BuyerRatings.RatingCriteria3).Column("EnquiryBuyerRatingsRatingCriteria3").Nullable();
Map(x => x.BuyerRatings.RatingCriteria4).Column("EnquiryBuyerRatingsRatingCriteria4").Nullable();
Map(x => x.BuyerRatings.RatingCriteria5).Column("EnquiryBuyerRatingsRatingCriteria5").Nullable();
Map(x => x.BuyerRatings.RatingCriteriaComment).Column("EnquiryBuyerRatingsRatingCriteriaComment").Nullable();
}
No I have left out mapping the supplier side here as I was trying to simplify it best I could to be able to solve it.
So I don't understand why its having a issue... I've told it which column to look at (full name) not "RatingCriteria1"
as well as when I do have a variable RatingCriteria1 it has a getter and setter!!!
very odd... am i missing something you have to do when mapping sub classes like this?
You need to map Ratings as a component, by doing:
public EnquiryOrderMapping()
{
Id(x => x.Id).Column("EnquiryOrderId");
Map(x => x.DateOrdered);
Map(x => x.PONumber).Nullable();
Map(x => x.SONumber).Nullable();
Component(x => x.BuyerRatings,
y =>
{
y.Map(r => r.RatingCriteria1, "EnquiryBuyerRatingsRatingCriteria1").Nullable();
y.Map(r => r.RatingCriteria2, "EnquiryBuyerRatingsRatingCriteria2").Nullable();
y.Map(r => r.RatingCriteria3, "EnquiryBuyerRatingsRatingCriteria3").Nullable();
// etc..
});
// etc..
}
I am using IQueryable<> to build up batching queries.
I have used views successfully to fetch information so the IQueryable<> can find it, but in this instance I can't work out how to map a view, as it depends on properties rather than the entity's ID.
Say I have this entity and mapping:
public class Calculation
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual TransactionTotal TransactionTotal { get; set; }
}
public class CalculationMap : ClassMap<Calculation>
{
public CalculationMap()
{
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.Organisation).Not.Nullable().UniqueKey("OC");
References(x => x.Charge).Not.Nullable().UniqueKey("OC");
}
This is the class I need to get in there: I'm using a view to give me the total amount per Organisation and Charge:
public class TransactionTotal
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual decimal Amount { get; set; }
}
public class TransactionTotalMap : ClassMap<TransactionTotal>
{
public TransactionTotalMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Table("TransactionTotalsView");
References(x => x.Charge).Not.Nullable();
References(x => x.Organisation).Not.Nullable();
Map(x => x.Amount).Precision(15).Scale(2).Not.Nullable();
}
}
Other places I have used views I have successfully used mappings like HasOne(x => x.TransactionTotal); but in this instance I need to tell Nhibernate to use the Organisation and Charge properties as the key.
Is this even possible? If so, how do I map the TransactionTotal class to the Calculation class?
Edit: I have used CompositeId in TransactionTotalMap as suggested by #David:
CompositeId().KeyProperty(x => x.Organisation.Id).KeyProperty(x => x.Charge.Id);
I'm still stuck on what to put in the CalculationMap though.
use the CompositeId() method in your mapping