I'm having difficulty writing a Criteria to select all entities with empty child collections or empty grand-child collections. I can do these as separate Criteria but I'm having trouble combining into a single Criteria.
class structure:
public class Component
{
public IList<Version> Versions { get; set; }
}
public class Version
{
public IList<SubscribeEvent> SubscribedEvents { get; set; }
public IList<PublishEvent> PublishedEvent { get; set; }
}
This does not work:
return session
.CreateCriteria<Component>("c")
.CreateCriteria("Versions", "v")
.Add(Restrictions.Or(Restrictions.IsEmpty("c.Versions"), Restrictions.And(Restrictions.IsEmpty("v.PublishedEvents"),
Restrictions.IsEmpty("v.SubscribedEvents"))))
.SetCacheable(true);
I've managed to work out a solution, not sure if it is the best (I should buy NH profiler)
return session
.CreateCriteria<Component>("c")
.CreateAlias("Versions", "v", JoinType.LeftOuterJoin)
.Add(Restrictions.Or(Restrictions.IsEmpty("c.Versions"),
Restrictions.And(Restrictions.IsEmpty("v.SubscribedEvents"),
Restrictions.IsEmpty("v.PublishedEvents"))))
.SetCacheable(true);
Related
There is a NODES table with dozen of 'small' columns and a LOB column in a legacy DB. A NodeEntity class is mapped to the NODES table.
For performance purposes I do not want to load LOB column every time I access the DB. I know two approaches to achieve this:
Lazy loaded properties
Separate entity class (the idea is taken from here)
Lazy loaded properties are good when you only loading data from DB. But if you have to save entities then there is a risk to lose your data if you forget to fetch lazy loaded properties beforehand.
So I chose the second approach.
I created separate small NodeEntityLite class with properties mapped to non-LOB columns of NODES table. I modified NodeEntity class so it inherits from NodeEntityLite class. I changed the mappings for my classes and used union-subclass for inheritance.
public class NodeEntityLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
}
public class NodeEntity : NodeEntityLite {
public virtual string NOTE { get; set; } // type:clob
}
FluentNHibernate mapping for NodeEntityLite class is
public void Override(AutoMapping<NodeEntityLite> mapping) {
mapping.Table("NODES");
mapping.UseUnionSubclassForInheritanceMapping();
}
FluentNHibernate mapping for NodeEntity class is
public void Override(AutoMapping<NodeEntity> mapping) {
mapping.Table("NODES");
mapping.Map(e => e.NOTE).CustomType("StringClob").CustomSqlType("NCLOB");
}
I expected that when I execute select n from NodeEntityLite n where n.Id = :p0 HQL then NHibernate generates SQL commands without NOTE column:
select nodeentity0_.ID as id1_87_,
nodeentity0_.CODE as code2_87_
from from NODES nodeentity0_
where nodeentity0_.ID=:p0;
But NHibernate generates absolutely different SQL command (NOTE column is not skipped as I expected):
select nodeentity0_.ID as id1_87_,
nodeentity0_.CODE as code2_87_,
nodeentity0_.NOTE as note14_87_,
nodeentity0_.clazz_ as clazz_
from ( select ID, CODE, NOTE, 1 as clazz_ from NODES ) nodeentity0_
where nodeentity0_.ID=:p0;
I tried to change inheritance and to use other mappings but without success.
The question is: Can I map several classes to the same table in NHibernate to get access to different columns?
If yes, please give an example.
The solution (based on the suggestions from David Osborne and mxmissile) is not to use inheritance. I use common interface implementation instead of class inheritance. The working code is below:
public interface INodeLite {
long Id { get; set; }
string Code { get; set; }
}
public class NodeEntityLite : INodeLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
}
public class NodeEntity : INodeLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
public virtual string NOTE { get; set; } // type:clob
}
...
public void Override(AutoMapping<NodeEntityLite> mapping) {
mapping.Table("NODES");
}
...
public void Override(AutoMapping<NodeEntity> mapping) {
mapping.Table("NODES");
mapping.Map(e => e.NOTE).CustomType("StringClob").CustomSqlType("NCLOB");
}
Regardless of the inheritance, NH can map different types to the same table. I have done it, albeit without inheritance.
You should be able to remove this line from the NodeEntityLite override and achieve it:
mapping.UseUnionSubclassForInheritanceMapping();
If this proves unsuccessful, you might need to tune the automapping further. It's definitely possible though.
I have two classes with a Many-to-Many relationship. When I save my context, Entity Framework is not using the existing Ids, it creates new entry in my database.
My classes are the following : Country and CountryGroup (in my database EF creates as expected CountryGroupCountries).
public class Country : EntityBase
{
public Country()
{
CountryGroups = new List<CountryGroup>();
}
public virtual List<CountryGroup> CountryGroups { get; set; }
}
public class CountryGroup : EntityBase
{
public CountryGroup()
{
Countries = new List<Country>();
}
public virtual List<Country> Countries { get; set; }
}
public abstract class EntityBase
{
public EntityBase()
{
DateCreate = DateTime.Now;
DateUpdate = DateTime.Now;
DateDelete = DateTime.Now;
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
[Required]
public virtual String Name { get; set; }
}
I use ASP MVC 4 and Entity Framework 5. When I want to save a CountryGroup, I use Countries that are already in my website. The Ids are the right one.
public virtual void Save(TEntity entity)
{
EntityRepository.Insert(entity);
Context.SaveChanges();
}
I just want EF to save my object and the relation to the countries but not. What solution do I have here ? I Feel like I have a misunderstanding of the way EF manages Many To Many.
After many research I believe that my problem lies on the model binder. It must be only creating object without getting them from the context. I overridded my Save Method to replace each Countries in the CountryGroup entity with a fresh one from the context. It is not optimal but I'm going to studie the model binding and then I will arbitrate between those solutions.
I query items with lazy collections in via nHibernate. Items are queried without fetching collections. But when i try to create HashedSet
var hashedSet = new HashedSet<Thing>(Session.Query<Thing>())
from those items all lazy stuff is fetched. Whats causing that?
[Serializable]
public class Thing {
public virtual String Name { get; set; }
public Thing() {
OtherThings = new HashedSet<OtherThing>();
}
public virtual ISet<OtherThing> OtherThings { get; set; }
}
Maybe you have overridden GetHashCode() in the entity (or any base class of it) and access the properties there?
I have a requirement to load a complex object called Node...well its not that complex...it looks like follows:-
A Node has a reference to EntityType which has a one to many with Property which in turn has a one to many with PorpertyListValue
public class Node
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual EntityType Etype
{
get;
set;
}
}
public class EntityType
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual IList<Property> Properties
{
get;
protected set;
}
public EntityType()
{
Properties = new List<Property>();
}
}
public class Property
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual EntityType EntityType
{
get;
set;
}
public virtual IList<PropertyListValue> ListValues
{
get;
protected set;
}
public virtual string DefaultValue
{
get;
set;
}
public Property()
{
ListValues = new List<PropertyListValue>();
}
}
public class PropertyListValue
{
public virtual int Id
{
get;
set;
}
public virtual Property Property
{
get;
set;
}
public virtual string Value
{
get;
set;
}
protected PropertyListValue()
{
}
}
What I a trying to do is load the Node object with all the child objects all at once. No Lazy load. The reason is I have thousands of Node objects in the database and I have to send them over the wire using WCF Service.I ran into the classes SQL N+ 1 problem. I am using Fluent Nhibernate with Automapping and NHibernate Profiler suggested me to use FetchMode.Eager to load the whole objects at once. I am using the following qyuery
Session.CreateCriteria(typeof (Node))
.SetFetchMode( "Etype", FetchMode.Join )
.SetFetchMode( "Etype.Properties", FetchMode.Join )
.SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )
OR using NHibernate LINQ
Session.Linq<NodeType>()
.Expand( "Etype")
.Expand( "Etype.Properties" )
.Expand( "Etype.Properties.ListValues" )
When I run any of the above query, they both generate one same single query with all the left outer joins, which is what I need. However, for some reason the return IList from the query is not being loaded property into the objects. Infact the returned Nodes count is equal to the number of rows of the query, so the Nodes objects are repeated.Moreover, the properties within each Node are repeated, and so do the Listvalues.
So I would like to know how to modify the above query to return all unique Nodes with the properties and list values within them.
each mapping has to have lazy loading off
in Node Map:
Map(x => x.EntityType).Not.LazyLoad();
in EnityType Map:
Map(x => x.Properties).Not.LazyLoad();
and so on...
Also, see NHibernate Eager loading multi-level child objects for one time eager loading
Added:
Additional info on Sql N+1:
http://nhprof.com/Learn/Alerts/SelectNPlusOne
I figure it out myself. The key is to use SetResultTransformer() passing an object of DistinctRootEntityResultTransformer as a parameter. So the query now looks like as follows
Session.CreateCriteria(typeof (Node))
.SetFetchMode( "Etype", FetchMode.Join )
.SetFetchMode( "Etype.Properties", FetchMode.Join )
.SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )
.SetResultTransformer(new DistinctRootEntityResultTransformer());
I found the answer to my questions through these links:
http://www.mailinglistarchive.com/html/nhusers#googlegroups.com/2010-05/msg00512.html
http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx
I ended up with something like this:
HasMany(x => x.YourList).KeyColumn("ColumnName").Inverse().Not.LazyLoad().Fetch.Join()
Just make sure to select your entity like this, to avoid duplication due to the join:
session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>();
SetResultTransformer with DistinctRootEntityResultTransformer will only work for Main object but IList collections will be multiplied.
I have an object from my domain model that has a child object. How can I use a criteria query to order based on a property of the child?
For example:
class FooType
{
public int Id { get; set; }
public string Name { get; set; }
public BarType Bar { get; set; }
}
class BarType
{
public int Id { get; set; }
public string Color { get; set; }
}
...
// WORKS GREAT
var orderedByName = _session.CreateCriteria<FooType>().AddOrder(Order.Asc("Name")).List();
// THROWS "could not resolve property: Bar.Color of: FooType"
var orderedByColor = _session.CreateCriteria<FooType>().AddOrder(Order.Asc("Bar.Color")).List();
What do I need to do to enable this scenario? I'm using NHibernate 2.1. Thanks!
You need to either add an alias or create a nested criteria for your child. Not sure how to do this in NHibernate, in Hibernate it's done via createCriteria() and createAlias() methods.
You would then use the alias as prefix in order by.
Update Hibernate code sample:
Criteria criteria = session.createCriteria(FooType.class);
criteria.createAlias("bar", "b");
criteria.addOrder(Order.asc("b.color"));
I imagine in NHibernate it would be quite similar, though with property/entity names uppercased. Here's an example from NHibernate documentation.