Fluent NHibernate one to many am i doing it right? - c#

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

Related

How create circular reference with fluent api code first c#

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

Fluent NHibernate CompositeId trying to insert null values

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

NHibernate query for most recent records

Ok, this should be really simple. I'm new to NHibernate, but everywhere I've searched has given me conflicting answers on this, and none of them seem to work. Basically, I need to implement the following SQL query into my code. It simply returns the most recent records for a specific participant.
select *
from result
where ParticipantId = 1
and ResultDate = (select MAX(resultDate)
from Result
where ParticipantId = 1)
I'm using Fluent NHibernate with NHibernate 3.3. I can pull back the data without the "and" in the where clause using the code below, but when I try to restrict it to the most recent records, nothing's working.
var resultSet = session.Query<Result>()
.Where(r => r.Participant.ParticipantId == 1);
Also including my domain and mapping files here for completeness. Let me know if more info is needed.
public class Result
{
public virtual int ResultId { get; set; }
public virtual Participant Participant { get; set; }
public virtual ResultType ResultType { get; set; }
public virtual float? ResultValue { get; set; }
public virtual string ResultText { get; set; }
public virtual DateTime ResultDate { get; set; }
public virtual int? Systolic { get; set; }
public virtual int? Diastolic { get; set; }
public virtual DateTime? DateAdded { get; set; }
public virtual string AddedBy { get; set; }
public virtual DateTime? DateChanged { get; set; }
public virtual string ChangedBy { get; set; }
}
public ResultMap()
{
Table("Result");
LazyLoad();
Id(x => x.ResultId).GeneratedBy.Identity().Column("ResultId");
References(x => x.Participant).Column("ParticipantId");
References(x => x.ResultType).Column("ResultTypeId");
Map(x => x.ResultValue).Nullable();
Map(x => x.ResultText).Nullable().Length(100);
Map(x => x.ResultDate).Nullable();
Map(x => x.Systolic).Nullable();
Map(x => x.Diastolic).Nullable();
Map(x => x.DateAdded).Nullable();
Map(x => x.AddedBy).Nullable().Length(100);
Map(x => x.DateChanged).Nullable();
Map(x => x.ChangedBy).Nullable().Length(100);
}
Any thoughts on an easy solution to get the recent data? Bonus points for letting me know how to get the last 2 dates, as I'll need them soon for comparing improvements. Thanks in advance!
To get that same query via QueryOver...
var maxResultDate = QueryOver.Of<Result>()
.Where(x => x.Participant.ParticipantId == 1)
.Select(Projections.Max<Result>(x => x.ResultDate));
var rslt = s.QueryOver<Result>()
.Where(x => x.Participant.ParticipantId == 1)
.WithSubquery.WhereProperty(x => x.ResultDate).Eq(maxResultDate)
//or to get the "last" 2 rows...
//.OrderBy(x => x.ResultDate).Desc
//.Take(2)
.List();

simple One-To-Many fluent-nhiberante [duplicate]

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

Mapping nHibernate One Table to Class & Sub Classes

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

Categories