Fluent Nhibernate Get object with filled child collection - fluent-nhibernate

I have
public class Parents {
public Parents() {
Childs = new HashedSet<Childs>();
}
public virtual int Id {get; set;}
public virtual String Name {get; set;}
public virtual ISet<Childs> Childs {get; protected set;}
}
public class Childs {
public virtual int Id {get; set;}
public virtual String Name {get; set;}
public virtual Parents Parent {get; set;}
}
Mapping
....
.Override<Parents>(map => {
map.HasMany(x => x.Childs).KeyColumn("parent_id").Cascade.SaveUpdate().Not.LazyLoad().AsSet().Fetch().Join();
})
.Override<Childs>(map => {
map.References(x => x.Parent, "parent_id");
})
....
How can i get(select) parent with childs using Link, HQL or AutoMapper? For example I have query like this in my test case,
Parents parent = new Parents {Name = "parent test"};
Childs child = new Childs {Name = "child test", Parent = parent};
session.Save(parent);
session.Save(child);
...
var myParent = session.QueryOver<Parents>().Where(x=>x.Id==1).List()[0];
Assert.IsTrue(myParent.Childs.Count>0);
...
in log i see query: select with join, but i get only parent without childs

You need to make the "Childs" property inverse.
When it is inverse, it is not stored. In you test case, the list is empty (you don't add the children to the parent, you only set the back-reference in the children to the parent). If it is stored, what it is doing now, NH clears the list in the database and removes the children from the parent.

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

How to get parent and child table title using nested foreach loop in entity framework

I have one Parent table and its child table.I have to display title from parent and child table on page as given below in MVC project:
I know I have to use nested foreach on view to display this data like given below:
foreach(var ptitle in parents)
{
<li>#model.title</li>
foreach(var pchild in parents.childs)
{
<li>#model.childtitle</li>
}
}
I am using database first approach so what will be the linq query to get such type of result
thanks
Assume that parent is User and Product is their childs. Here is your entities.
public class User {
public int Id {get; set;}
public string Name {get; set;}
}
public class Product {
public int Id {get; set;}
public string Name {get; set;}
//user who created this product
public int UserId {get; set;}
}
You can create viewmodel and collect your data to show:
public class ProductUserViewModel {
public User User {get; set;}
public List<Product> Products {get; set;}
}
In action collect data:
public ActionResult GetAllUsersAndTheirProducts()
{
var allUsers = db.UserTable.ToList();
List<ProductUserViewModel> result = new List<ProductUserViewModel>();
foreach(var user in allUsers)
{
ProductUserViewModel model = new ProductUserViewModel();
model.User = user;
model.Products = db.ProductTable.Where(e=>e.UserId == user.Id).ToList();
result.Add(model);
}
return View(result);
}
And In view:
#model IEnumerable<ProductUserViewModel>
foreach(var item in Model)
{
<li>#item.User.Name</li>
foreach(var product in item.Products)
{
<li>#product.Name</li>
}
}
You can query the children and group them by their parent name. This assumes a ParentClient=>Client relationship.
var clientGroups = context.Clients.GroupBy(x => x.ParentClient.Name).OrderBy(x=>x.Key).ToList();
foreach (var clientGroup in clientGroups)
{
var parentName = clientGroup .Key;
foreach (var child in clientGroup)
{
var title = child.Name;
}
}

Nhibernate Has Many Insert generates extra Update (no Inverse)

I have a Parent class with two lists of Child classes
public class Parent
{
...
public virtual ICollection<Foo> Foo{ get; set; }
public virtual ICollection<Bae> Bar{ get; set; }
}
public class Foo
{
...
public virtual Parent Parent{ get; set; }
}
public class Bar
{
...
public virtual Parent Parent{ get; set; }
}
The mappings are
public ParentMap()
{
...
HasMany(m => m.Foo).AsSet().Cascade.All();
HasMany(m => m.Bar).AsSet().Cascade.All();
}
public FooMap()
{
...
References(m => m.Parent);
}
public BarMap()
{
...
References(m => m.Parent);
}
Whenever I save the parent object I get
INSERT Parent ...
INSERT Foo with Parent_id set to NULL
INSERT Bar with Parent_id set to NULL
UPDATE Foo set Parent_id to Parent id
UPDATE Bar set Parent_id to Parent id
Is there a way apart from setting the relationship to Inverse to avoid the extra update?
If you have a bidirectional assiciation you should set the HasMany side to inverse, this is the most common way.
Another possibility is setting the parent reference to Not.Insert().Not.Update(), then the parent property will be completely ignored for insert/updates.
If you don't need the reference from child to parent just remove it from the class and mapping so you only have the collection.
For the second and third way you can additionally set Not.KeyNullable() on the HasMany, this makes sure that NHibernate inserts the new row with the parent ID already set and avoids the additional update statement (this feature requires NHibernate 3.2.0).
AFAIK, not.
By setting the inverse property, you specify the owner of the association.
Why is it a problem for you to specify the inverse property ?

NHibernate projecting child entities into parent properties with Linq or QueryOver

Maybe it's simple but I'm stuck with it and I didn't find any answer on how it could be done. I have a parent entity User with a collection of child entities Operations. These two entities are just for UI so they are a kinf of views. Here is the pseudo code
public class User
{
public int Id {get; set;}
public IEnumerable<Operation> Operations {get; set;}
public int TotalSuccessfulAccesses {get; set;} // not mapped to the database
public int TotalFailedAccesses {get; set;} // not mapped to the database
}
public class Operation
{
public int Id {get; set; }
public int UserId {get; set; } // FK
public int NbSuccessfulAccesses {get; set; }
public int NbFailedAccesses {get; set; }
}
What I would like to do it's to get the User with TotalSuccesfulAccesses and TotalFailedAccesses initialized from the child collection in one round trip to the database.
For each user we should calculate Sum(Operation.NbSuccessfulAccesses) and Sum(Operation.NbFailedAccesse) and make a projection respectively to the User.TotalSuccesfulAccesses and User.TotalFailedAccesses.
I tried to play with multicriteria and several queries but I'm not satisfied with it. I would like to know if maybe there is a simple way to do it with projection or something other. Or maybe I missed something.
What would you recommend ?
Thanks in advance for you help.
I was able to get rid of the magic alias strings in the following way:
UserViewModel userView = null;
Add(Projections.Sum<User>(x => operations.NbSuccessfulAccesses).WithAlias(() => userView.TotalSuccessfulAccesses))
You probably need to separate your view models and your domain entities. I assume in your domain you have got a User class having a list of Operation and these entities are mapped accordingly.
You could then create a view model:
public class UserViewModel
{
public int UserId { get; set; }
public int TotalSuccessfulAccesses { get; set; }
public int TotalFailedAccesses {get; set;}
}
Using ICriteria you can create the following query:
var criteria = Session.CreateCriteria(typeof(User));
criteria.CreateAlias("Operations", "operations", JoinType.LeftOuterJoin);
var projList = Projections.ProjectionList();
projList.Add(Projections.GroupProperty("Id"));
projList.Add(Projections.Sum("operations.NbSuccessfulAccesses"), "TotalSuccessfulAccesses");
projList.Add(Projections.Sum("operations.NbFailedAccesses"), "TotalFailedAccesses");
criteria.SetProjection(projList);
criteria.SetResultTransformer(Transformers.AliasToBean<UserViewModel>());
var ret = criteria.List<UserViewModel>();
Create the view model according to your needs and add any properties in the projection list accordingly.
Hope that helps.
Thanks to Kay, I came up with the following translation :
Operation operations = null;
var q = GetSession().QueryOver<User>().Where(u => u.AccessKeyId == accessKeyId)
.Left.JoinQueryOver(x => x.Operations, () => operations)
.Select(Projections.ProjectionList()
.Add(Projections.Sum<User>(x => operations.NbSuccessfulAccesses), "TotalSuccessfulAccesses"))
.Add(Projections.Sum<User>(x => operations.NbFailedAccesses), "TotalFailedAccesses"))
.TransformUsing(Transformers.AliasToBean<UserViewModel>()).List< UserViewModel >();
However I would like to know if there is a mean to get rid of the magic string "TotalSuccessfulAccesses" and "TotalFailedAccesses".
if I use something like that
UserViewModel userView = null;
Add(Projections.Sum<User>(x => operations.NbSuccessfulAccesses), () => userView.TotalSuccessfulAccesses)
NHibernate yields an error :
Could not find a setter for property 'userView.TotalSuccessfulAccesses' in class 'Domain.Query.UserViewModel'
which is not true because there is a setter for TotalSuccessfulAccesses' property.
Any ideas ?
Thanks