How to restrict selection with QueryOver in (fluent) nhibernate? - nhibernate

I want to filter objects from the db by a property that comes from another object but i get an exception:
A first chance exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in mscorlib.dll
A first chance exception of type 'NHibernate.QueryException' occurred in NHibernate.dll
A first chance exception of type 'NHibernate.QueryException' occurred in NHibernate.dll
The program '[5116] Examples.FirstProject.vshost.exe: Managed (v2.0.50727)' has exited with code -532459699 (0xe0434f4d).
This works:
var curves = session.QueryOver<Curve>().WhereRestrictionOn(p => p.Name).IsLike("%CurveName%").List();
foreach (Curve curve in curves)
{
Console.WriteLine(" ID:\t{0}\n Name:\t{1}\n Group:\t{2}\n", curve.Id, curve.Name, curve.Group.Name);
}
This not, it outputs the exception information:
var curves = session.QueryOver<Curve>().WhereRestrictionOn(p => p.Group.Name).IsLike("%GroupName%").List();
foreach (Curve curve in curves)
{
Console.WriteLine(" ID:\t{0}\n Name:\t{1}\n Group:\t{2}\n", curve.Id, curve.Name, curve.Group.Name);
}
These are my mappings:
public class CurveMap : ClassMap<Curve>
{
public CurveMap()
{
Table("CURVES");
Id(x => x.Id).Column("CURVE_ID");
Map(x => x.Name).Column("NAME");
References(x => x.Group).Column("GROUP_ID");
}
}
public class CurveGroupMap : ClassMap<CurveGroup>
{
public CurveGroupMap()
{
Table("GROUPS");
Id(x => x.Id).Column("GROUP_ID");
Map(x => x.Name).Column("NAME");
HasMany(x => x.Curves).KeyColumn("GROUP_ID").Cascade.All().Inverse();
}
}
And these are my objects
public class Curve
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual CurveGroup Group { get; set; }
}
public class CurveGroup
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Curve> Curves { get; set; }
}
Any idea, how to fix this. I am new to (fluent) nhibernate.

If you join CurveGroup and use Aliases it will work:
CurveGroup cgAlias = null;
var curves = session.QueryOver<Curve>()
.JoinAlias(e => e.Group, () => cgAlias)
.WhereRestrictionOn(() => cgAlias.Name).IsLike("%GroupName%").List();

Related

Unable to cast object of type 'NHibernate.Mapping.List' to type 'NHibernate.Mapping.IKeyValue'

Trying to map a relationship in Fluent NHibernate and I'm getting this exception.
Here are the classes
public abstract class TaskContainer : DomainObject
{
public virtual IList<Task> Tasks { get; set; }
}
public class Task : TaskContainer
{
public virtual TaskContainer TaskContainer { get; set; }
public virtual string Description { get; set; }
public static Task Get(int id)
{
return Get<Task>(id);
}
}
And the mapping file
public class TaskMap : ClassMap<Task>
{
public TaskMap()
{
Id(x => x.Id);
Map(x => x.Description);
ReferencesAny(x => x.TaskContainer)
.EntityIdentifierColumn("TaskContainer_Id")
.EntityTypeColumn("TaskContainerDiscriminator")
.IdentityType<int>()
.AddMetaValue<Task>("Task")
HasMany(x => x.Tasks)
.KeyColumn("TaskContainer_Id")
.PropertyRef("Tasks")
.AsList();
}
}
I've seen other references to this error, but they had to do with implementing List<T> instead of IList<T>, which I am not doing.
Try: .PropertyRef("TaskContainer")

Fluent NHibernate - HasOne mapped to a ReferencesAny

I have the following POCO classes:
public class Container
{
public virtual Int64 ContainerId { get; protected set; }
public virtual string Name { get; set; }
public virtual Location Location { get; set; }
}
public abstract class Location
{
public virtual Int64 LocationId { get; protected set; }
public virtual string Name { get; set; }
}
public class UniqueLocation : Location
{
public virtual Container Container { get; set; }
}
public class SharedLocation : Location
{
public SharedLocation()
{
this.Containers = new List<Container>();
}
public virtual IList<Container> Containers { get; set; }
}
and the following Fluent mapping:
public class ContainerMap: ClassMap<Container>
{
public ContainerMap()
{
Table("Containers");
Id(x => x.ContainerId);
Map(x => x.Name);
ReferencesAny(x => x.Location).IdentityType<Int64>().EntityTypeColumn("LocationType").EntityIdentifierColumn("LocationId")
.AddMetaValue<UniqueLocation>("U")
.AddMetaValue<SharedLocation>("S");
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Table("Locations");
Id(x => x.LocationId);
Map(x => x.Name);
}
}
public class UniqueLocationMap : SubclassMap<UniqueLocation>
{
public UniqueLocationMap()
{
HasOne(x => x.Container).PropertyRef(x => x.Location).ForeignKey("LocationId").Cascade.All().Constrained();
}
}
public class SharedLocationMap : SubclassMap<SharedLocation>
{
public SharedLocationMap()
{
HasMany(x => x.Containers).KeyColumn("LocationId");
}
}
The problem is HasOne() mapping generates the following exception: "broken column mapping for: Container.Location of: UniqueLocation, type Object expects 2 columns, but 1 were mapped".
How do I tell HasOne() to use/map both LocationType and LocationId?
AFAIK Where conditions are not possible on Entity references except using Formulas. The design seems a strange because it would be nasty to change a unique Location to a shared location.
what you want can be done using:
Reference(x => x.Container).Formula("(SELECT c.Id FROM Container c WHERE c.LocationId = Id AND c.LocationType = 'U')");
But i would prefere
class Location
{
...
public virtual bool IsUnique { get { return Container.Count == 1; } }
}

Trying to run query for entities when I have Inheritance with fluent Nhibernate

I attempted to extract some common properties to a base class and map with Fluent Nhibernate. In addition, I also attempted to add a second level of inheritance.
//Base entity class
public class EntityBase : IEntityBase
{
public EntityBase()
{
CreatedDate = DateTime.Now;
}
public virtual DateTime? CreatedDate { get; set; }
public virtual int Id { get; set; }
public virtual int Version { get; set; }
}
//Base Entity Mapping
public class EntityBaseMap: ClassMap<EntityBase>
{
public EntityBaseMap()
{
UseUnionSubclassForInheritanceMapping();
Id(x => x.Id);
Version(x => x.Id);
Map(x => x.CreatedDate);
}
}
//first sub class of EntityBase
public class Actuate : EntityBase, IActuate
{
public virtual DateTime? ActivatedOn { get; set; }
}
//Actuate Mapping class
public class ActuateMap : SubclassMap<Actuate>
{
public ActuateMap()
{
Map(x => x.ActivatedOn);
}
}
//Sub class entity
public class Item : Actuate
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual decimal UnitPrice { get; set; }
public virtual ItemStatus Status { get; set; }
public virtual Store Store { get; set; }
}
//Item Mapping class
public class ItemMap : SubclassMap<Item>
{
public ItemMap()
{
Abstract();
Map(x => x.Name);
Map(x => x.Description);
Map(x => x.UnitPrice);
Map(x => x.Status);
References(x => x.Store);
}
}
The entity I have discovered has a problem (other relationship issues might exists)
//Store entity Does not inherit from EntityBase or Actuate
public class Store
{
public virtual int Id { get; set; }
public virtual int Version { get; set; }
public virtual string Name { get; set; }
public virtual IEnumerable<Item> Items { get; set; }
}
//Store mapping class
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id).GeneratedBy.Assigned();
Version(x => x.Version);
Map(x => x.Name);
HasMany(x => x.Items);
}
}
Problem
If I try to run the following query:
//store = is the Store entity I have retrieved from the database and I am trying
//trying to return the items that are associated with the store and are active
store.Items != null && store.Items.Any(item => item.Status == ItemStatus.Active);
I get the following error:
ERROR
Nhibernate.Exceptions.GenericADOException: could not initialize a collection: [SomeDomain.Store.Items#0][SQL: SELECT items0_.StoreId as StoreId1_, items0_.Id as Id1_, items0_.Id as Id10_0_, items0_.CreatedDate as CreatedD2_10_0_, items0_.ActivatedOn as Activate1_11_0_, items0_.Name as Name12_0_, items0_.Description as Descript2_12_0_, items0_.UnitPrice as UnitPrice12_0_, items0_.Status as Status12_0_, items0_.StoreId as StoreId12_0_ FROM [Item] items0_ WHERE items0_.StoreId=?]"}
Inner Exception
"Invalid object name 'Item'."
Now, if I take out the base classes and Item doesn't inherit, and the
Id, Version
columns are part of the Item entity and are mapped in the ItemMap mapping class (with the ItemMap class inheriting from ClassMap<Item> instead, everything works without issue.
NOTE
I have also attempted to add on the StoreMap class unsuccessful.
HasMany(x => x.Items).KeyColumn("Id");
Any thoughts?
if entityBase is just for inheriting common properties then you do not need to map it at all
public class EntityBaseMap<TEntity> : ClassMap<TEntity> where TEntity : EntityBase
{
public EntityBaseMap()
{
Id(x => x.Id);
Version(x => x.Version);
Map(x => x.CreatedDate);
}
}
public class ActuateMap : EntityBaseMap<Actuate> { ... }
Notes:
Versionmapping should map Version property not Id
Version should be readonly in code so nobody accidently alters it
.KeyColumn("Id") is wrong because the column is from the Items table and then it's both the autogenerated id and foreign key. That's not possible nor usefull
usually only classes which are abstract should containt Abstract() in the mapping

Multiple hasmany same keycolumn - illegal access to loading collection

it works. pull the data, but gives this error: illegal access to loading collection
public class Image : File
{
public virtual string ImagePath { get; set; }
}
public class Video : File
{
public virtual string VideoPath { get; set; }
public virtual string VideoType { get; set; }
}
public class Service : ContentBase
{
public virtual IList<Image> Images { get; set; }
public virtual IList<Video> Videos { get; set; }
}
public class ServiceMap:SubclassMap<Domain.Service>
{
public ServiceMap()
{
DiscriminatorValue("Service");
HasMany(x => x.Images).KeyColumn("ContentBase");
HasMany(x => x.Videos).KeyColumn("ContentBase");
}
}
public class ImageMap:SubclassMap<Image>
{
public ImageMap()
{
DiscriminatorValue("Image");
Map(x => x.ImagePath);
}
}
public class VideoMap:SubclassMap<Video>
{
public VideoMap()
{
DiscriminatorValue("Video");
Map(x => x.VideoPath);
}
}
it works. but it gives this error when I query. I think the same "keycolumn" gives this error to be. mapping'i How should I do?
Are you aware that joinalias wont eagerload the collections? that's what Fetch is for. Try this one instead
var service = UnitOfWork.CurrentSession.QueryOver<Service>()
.Fetch(x => x.Images).Eager
.Fetch(x => x.Videos).Eager
.Where(x => x.Id == serviceId)
.SingleOrDefault();
Update:
illegal access to loading collection could be thrown when
session is closed when accessing not initialized collection
mapping and collection type mismatch
Database doesn't match
have you inspected the sql generated to see if NH tries to access nonexistant columns or columns on the wrong table?

Fluent NHibernate compositeid to mapped class

I'm trying to figure out how to use CompositeId to map another class. Here's a test case:
The tables:
TestParent:
TestParentId (PK)
FavoriteColor
TestChild:
TestParentId (PK)
ChildName (PK)
Age
The classes in C#:
public class TestParent
{
public TestParent()
{
TestChildList = new List<TestChild>();
}
public virtual int TestParentId { get; set; }
public virtual string FavoriteColor { get; set; }
public virtual IList<TestChild> TestChildList { get; set; }
}
public class TestChild
{
public virtual TestParent Parent { get; set; }
public virtual string ChildName { get; set; }
public virtual int Age { get; set; }
public override int GetHashCode()
{
return Parent.GetHashCode() ^ ChildName.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is TestChild)
{
var toCompare = obj as TestChild;
return this.GetHashCode() != toCompare.GetHashCode();
}
return false;
}
}
The Fluent NHibernate maps:
public class TestParentMap : ClassMap<TestParent>
{
public TestParentMap()
{
Table("TestParent");
Id(x => x.TestParentId).Column("TestParentId").GeneratedBy.Native();
Map(x => x.FavoriteColor);
HasMany(x => x.TestChildList).KeyColumn("TestParentId").Inverse().Cascade.None();
}
}
public class TestChildMap : ClassMap<TestChild>
{
public TestChildMap()
{
Table("TestChild");
CompositeId()
.KeyProperty(x => x.ChildName, "ChildName")
.KeyReference(x => x.Parent, "TestParentId");
Map(x => x.Age);
References(x => x.Parent, "TestParentId"); /** breaks insert **/
}
}
When I try to add a new record, I get this error:
System.ArgumentOutOfRangeException :
Index was out of range. Must be
non-negative and less than the size of
the collection. Parameter name: index
I know this error is due to the TestParentId column being mapped in the CompositeId and References calls. However, removing the References call causes another error when querying TestChild based on the TestParentId.
Here's the code that does the queries:
var session = _sessionBuilder.GetSession();
using (var tx = session.BeginTransaction())
{
// create parent
var p = new TestParent() { FavoriteColor = "Red" };
session.Save(p);
// creat child
var c = new TestChild()
{
ChildName = "First child",
Parent = p,
Age = 4
};
session.Save(c); // breaks with References call in TestChildMap
tx.Commit();
}
// breaks without the References call in TestChildMap
var children = _sessionBuilder.GetSession().CreateCriteria<TestChild>()
.CreateAlias("Parent", "p")
.Add(Restrictions.Eq("p.TestParentId", 1))
.List<TestChild>();
Any ideas on how to create a composite key for this scenario?
I found a better solution that will allow querying and inserting. The key is updating the map for TestChild to not insert records. The new map is:
public class TestChildMap : ClassMap<TestChild>
{
public TestChildMap()
{
Table("TestChild");
CompositeId()
.KeyProperty(x => x.ChildName, "ChildName")
.KeyReference(x => x.Parent, "TestParentId");
Map(x => x.Age);
References(x => x.Parent, "TestParentId")
.Not.Insert(); // will avoid "Index was out of range" error on insert
}
}
Any reason you can't modify your query to just be
_sessionBuilder.GetSession().CreateCriteria<TestChild>()
.Add(Restrictions.Eq("Parent.TestParentId", 1))
.List<TestChild>()
Then get rid of the reference?