Fluent NHibernate Mapping and Retrieve Hierarchy Table - fluent-nhibernate

I have a hierarchy category table like this
Id int,
Description varchar(100),
ParentId int,
Ordinal int,
IsActive bit
I want to fetch all of the Categories from parent to child, so when I called session.get<Category>(id), it already fetched all of their children. Here is my map and class:
class Category
{
public virtual int Id {get; set;}
public virtual string Description {get; set;}
public virtual int ParentId {get; set;}
public virtual int Ordinal {get; set;}
public virtual bool IsActive {get; set;}
}
class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Table("TB_CATEGORY");
Id(f => f.Id).GeneratedBy.Native();
Map(f => f.Description);
Map(f => f.ParentId);
Map(f => f.Ordinal);
Map(f => f.IsActive);
}
}
I've searched so many articles, and am still confused when using their solutions because they don't tell me about the table structure and the mappings. Like this one from ayende blog, I think its a good solution, but I can't follow it well enough to apply this in my project.
Could somebody give me a step by step tutorial to achieve this? Are my mapping and class correct?

using the following classes
class Category
{
public virtual int Id {get; private set;}
public virtual string Description {get; set;}
public virtual Category Parent {get; set;}
public virtual bool IsActive {get; set;}
public virtual IList<Category> Children {get; private set;}
public override bool Euqals(object obj)
{
var other = obj as Category;
return other != null && (Id == 0) ? ReferenceEquals(other, this) : other.Id == Id;
}
public override int GetHashCode()
{
return Id;
}
}
class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Table("TB_CATEGORY");
Id(f => f.Id).GeneratedBy.Native();
Map(f => f.Description);
References(f => f.Parent).Column("ParentId");
HasMany(f => f.Children)
.AsList(i => i.Column("Ordinal")) // let the list have the correct order of child items
.KeyColumn("ParentId")
.Inverse(); // let the reference maintain the association
Map(f => f.IsActive);
}
}
then you can query
var categoriesWithChildrenInitialised = session.QueryOver<Category>()
.Fetch(c => c.Children).Eager
.List()

Related

Database structure with Entity Framework Core - Discrimination

I have the following problem:
I'd like to be able sell a few kinds of products in my store.
I have created following infrastructure:
public class Product
{
public int Id {get; set;}
public string Name {get; set;}
...
public int? ParentId {get; set;}
public Product Parent {get; set;}
public ICollection<Product> Children {get; set;}
}
public class AudioFile : Product
{
public string SomeValue {get; set;}
}
public class DocummentFile : Product
{
public string SomeString {get; set;}
}
In fluent mapping I am using discriminator.
So, to Select the amount of documment files without a parent, I am doing something like this:
DbContext.DocummentFiles.Count(x=> !x.ParentId.HasValue)
It works with Audio files too.
I'd like to select sample dictionary of all children for some audio file with SomeValue:
DbContext.AudioFiles
.Where(x => x.ParentId == 222)
.Select(x => x.Children
.OfType<AudioFile>()
.ToDictionary(t => t.Name, t => t.SomeValue));
Unfortunately, it does not work. I have high CPU usage by the long time, but I have not any result.

Fluent NHibernate HASMANY mapping without references

I am a beginner at using Fluent NHibernate.
I am developing a C# application that has to interact with an existing database.Let say I have 2 tables: Items and ItemsList.
Items: ID INT ItemName VARCHAR(100)
ItemsList: ID INT ChildItemID INT
I've built 2 classes and their mapping:
public class Items
{
public virtual int id {get; set;}
public virtual string itemName {get; set;}
}
public class ItemsMap : ClassMap<Items>
{
public ItemsMap()
{
Id(x => x.id).GeneratedBy.Increment();
Map(x => x.itemsName);
}
}
public class ItemsList()
{
public virtual int id {get; set;}
public virtual IList<Items> childItems {get; set;}
public ItemsList()
{
childItems = new List<Items>();
}
}
public class ItemsListMap : ClassMap<ItemsList>
{
public ItemsListMap()
{
Id(x => x.id).GeneratedBy.Increment();
HasMany(x => x.childItems).KeyColumn("childID").Cascade.All();
}
}
And finally, I insert an item in the itemsList and save it all:
try
{
using( ISession session = NH.OpenSession())
{
using(ITransaction transaction = session.BeginTransaction())
{
Items i = New Items()
i = session.get<Items>(1);
ItemsList il = new ItemsList();
il.childID.Add(i);
session.SaveOrUpdate(il);
transaction.Commit();
}
}
}
So when I commit, I have a new entry in ItemsList table, but the childID is blank.
Question:
All the examples I see has a reference to ItemsListID in Items table. But I don't want to have this reference since I want the item to be unique in the items table. How can I acheve that?
The NHibernate native way for expressing the unique reference, is:
5.1.12. one-to-one
There are two varieties of one-to-one association:
primary key associations
unique foreign key associations
Primary key associations don't need an extra table column; if two rows are related by the association then the two table rows share the same primary key value. So if you want two objects to be related by a primary key association, you must make sure that they are assigned the same identifier value!...
Other words, Tables would look like this (Table Items generates the value of ItemID, table ItemsList takes that value and stores it in the ItemID ) :
Items: ItemID INT ItemName VARCHAR(100)
ItemsList: ItemID INT
The C# would be (I changed Items into Item and ItemList into ItemMoreDetails, because it is not a list anymore)
public class Item
{
public virtual int ItemId { get; set; }
...
public virtual ItemMoreDetails ItemMoreDetails {get; set; }
public class ItemMoreDetails
{
public virtual int ItemId { get; set; }
...
public virtual Item Item {get; set;}
The mapping would be (in fluent):
// Parent side
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(x => x.id).GeneratedBy.Increment();
...
HasOne(x => x.ItemMoreDetails).Cascade.All();
// child side
public class ItemMoreDetailsMap: ClassMap<ItemMoreDetails>
{
public ItemMoreDetailsMap()
{
...
References(x => x.parent).Unique();
See the doc:
HasOne / one-to-one

nhibernate: project a Parent from a child-query

I have the following entities
public class ArticleCategory
{
public int Id {get; set;}
public string Name {get; set;}
public IList<Article> Articles {get; set;}
}
public class Article
{
public int Id {get; set;}
public string Name {get; set;}
public ArticleCategory Category {get; set;}
}
public class JobArticles
{
public int Id {get; set;}
public Job Job {get; set;}
public decimal Price {get; set;}
public Article Article {get; set;}
}
As you can see Article knows nothing about to which JobArticle it has been assigned (it's not relevant)
So what I need to do is the following.
Get every ArticleCategory for which there exist JobArticles for Job X.
The easiest way would be to Add the List of JobArticles to the Article Entity. But I'm not sure if it is the best way.
So I tried the opposite way (going from JobArticle to ArticleCategory). Something like that
IQueryOver<JobArticle, JobArticle> q = DataSession.Current.QueryOver<JobArticle>();
Article ArticleAlias = null;
ArticleCategory ArticleCategoryAlias = null;
q.JoinAlias(x => x.Article, () => ArticleAlias);
q.JoinAlias(x => ArticleAlias.Category, () => ArticleCategoryAlias);
q.Where(x => x.Job.Id == jobId);
q.SelectList(list => list
.Select(x => ArticleCategoryAlias))
Which leads to a NULL-Reference Exception because .Select(x => ArticleCategoryAlias)
I'm not really sure how to do it, hope you can help
Article ArticleAlias = null;
ArticleCategory ArticleCategoryAlias = null;
var categories = DataSession.Current.QueryOver<ArticleCategory>()
.WithSubquery.WhereProperty(x => x.Id).In(QueryOver.Of<JobArticle>()
.JoinAlias(x => x.Article, () => ArticleAlias);
.JoinAlias(x => ArticleAlias.Category, () => ArticleCategoryAlias);
.Where(x => x.Job.Id == jobId);
.Select(() => ArticleCategoryAlias.Id))
.List();

Fluent nhibernate map list of items

I have a class "Company" which has a list of "Operator"
public class Company
{
public IList<Opertator> Operators {get; set; }
public Int32 Id {get; set;}
}
public class Operator {
public Int32 Id {get; set; }
public Company Company {get; set; }
}
When I mapped as follows:
public class CompanyMapping : ClassMap<Company>
{
public ProductMapping() : base()
{
Id(x => x.Id, "CompanyId").GeneratedBy.Native();
HasMany(x => x.Operators);
}
}
public class OperatorMapping : ClassMap<Operator>
{
public OperatorMapping()
{
Id(x => x.Id);
Reference(x => x.Company);
}
}
I have a UI where user can add operators and remove operators by checking boxes.
in c# code, I query the stored company, and add to the list, or remove from the list.
then send SaveOrUpdate.
My problem is when I add one more operator to existing company, and save/Update, the NHibernate is deleting the whole list, and reinster them again.
I don't want to do that.
Can I have it, that NHibernate will detect the changed items (new items from the list, and insert them, and determine the deleted item and delete them)?
I tried change the mapping to have in the company mapping
HasMany(x => x.Operators).Inverse();
but it end up not deleting at all.
Any help?
Inverse says that the Operator is responsible for the association and you have to set it when adding an Operator to the Company
public class Company
{
public Int32 Id {get; set;}
public ICollection<Opertator> Operators {get; private set; }
public void Add(Opertator operator)
{
Operators.Add(operator);
operator.Company = this;
}
public Company()
{
Operators = new List<Operator>();
}
}
// and
HasMany(x => x.Operators).Inverse();
// use it like
company.Add(new Operator()); // instead of company.Operators.Add(new Operator());

FluentNHibernate, getting 1 column from another table

We're using FluentNHibernate and we have run into a problem where our object model requires data from two tables like so:
public class MyModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int FooId { get; set; }
public virtual string FooName { get; set; }
}
Where there is a MyModel table that has Id, Name, and FooId as a foreign key into the Foo table. The Foo tables contains Id and FooName.
This problem is very similar to another post here: Nhibernate: join tables and get single column from other table but I am trying to figure out how to do it with FluentNHibernate.
I can make the Id, Name, and FooId very easily..but mapping FooName I am having trouble with. This is my class map:
public class MyModelClassMap : ClassMap<MyModel>
{
public MyModelClassMap()
{
this.Id(a => a.Id).Column("AccountId").GeneratedBy.Identity();
this.Map(a => a.Name);
this.Map(a => a.FooId);
// my attempt to map FooName but it doesn't work
this.Join("Foo", join => join.KeyColumn("FooId").Map(a => a.FooName));
}
}
with that mapping I get this error:
The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'join' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'joined-subclass, loader, sql-insert, sql-update, sql-delete, filter, resultset, query, sql-query' in namespace 'urn:nhibernate-mapping-2.2'.
any ideas?
I think you misunderstood something here.
Instead of having Id, Name, FooId and FooName in same class you need to create two classes
public class MyModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Foo Foo { get; set; }
}
public class Foo
{
public virtual int Id { get; set; }
public virtual string FooName { get; set; }
}
And your mapping classes for these:
public class MyModelMapping : ClassMap<MyModel>
{
public MyModelMapping()
{
this.Id(x => x.Id);
this.Map(x => x.Name);
this.References(x => x.Foo);
}
}
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
this.Id(x => x.Id);
this.Map(x => x.FooName);
}
}
this should help.
But remember Convention other Configuration :)