How to get records in first table(projects) not present in second table(finances) with foreign key reference using nhibernate - nhibernate

I'm trying to query on a simple data structure in nhibernate and MSSQL
dbo.Projects : Id(int, not null)
dbo.Finances : Id(int, not null), ProjectId(int,not null), foreign key references to dbo.projects
I want to get all the records in projects table that are not present in finances table where the finances table has a foreign key reference ProjectId.
I am migrating to (Fluent) Nhibernate 3 from EntityFramework?
//So far I have got here:
public IQueryable<ProjectModel> GetProjectsNotPresentInFinance()
{
var factory = Fluently.Configure()
.Database(MsSqlConfiguration
.MsSql2008
.ConnectionString(m_connectionString))
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<ProjectMap>()
).BuildSessionFactory();
using (var session = factory.OpenSession())
{
var allprojects = session.QueryOver<ProjectModel>();
var projectsToReturn = allprojects.List<ProjectModel>().AsQueryable();
//--- Something like : all the records not in finances table ---------
// .Where( proj => !db.Finances.Where(fin => fin.ProjectId == proj.Id).Any())
// .Select(project => new ProjectModel
// {
// Id=project.Id,
// ProjectName = project.ProjectName,
// });
return projectsToReturn;
}
}
public class FinanceModel
{
public virtual int Id { get; set; }
public virtual int ProjectId { get; set; }
}
public class ProjectModel
{
public virtual int Id { get; set; }
public virtual string ProjectName { get; set; }
}
public class ProjectMap:ClassMap<ProjectModel>
{
public ProjectMap() {
Table("Projects");
Id(x => x.Id);
Map(x => x.ProjectName);
}
}
public class FinanceMap : ClassMap<FinanceModel>
{
public FinanceMap()
{
Table("Finances");
Id(x => x.Id);
References(x => x.ProjectModel);
}
}
//-------------------------------------------------------
//This is an Equivalent working code Using EntityFramework :
public IQueryable<ProjectModel> GetProjectsNotPresentInFinance() {
IQueryable<ProjectModel> projectList = db.Projects
.Where( proj => !db.Finances.Where(fin => fin.ProjectId == proj.Id).Any())
.Select(project => new ProjectModel
{
Id=project.Id,
ProjectName = project.ProjectName,
});
return projectList;
}
//-------------------------------------------------------

On second thought, you may try this without changing anything to your mapping, using a subquery :
var notOrphanProjectIdsSubquery = QueryOver.Of<FinanceModel>()
.Select(x => x.ProjectId);
var orphanProjects = session.QueryOver<ProjectModel>()
.WithSubquery
.WhereProperty(x=>x.Id)
.NotIn(notOrphanProjectIdsSubquery)
.List();
----------------------- Initial answer
Assuming you have a mapped Finances Property in your Project class, and according to https://stackoverflow.com/a/14980450/1236044, it should be something like :
var orphanProjects = session.QueryOver<ProjectModel>()
.WhereRestrictionOn(x => x.Finances).IsEmpty()
.List();
I must confess I am not proficient with FluentNH. I guess the classes and mappings should be something like this, hoping I'm not setting you on the wrong track...
public class FinanceModel
{
public virtual int Id { get; set; }
public virtual int ProjectId { get; set; }
public virtual ProjectModel Project{get;set;}
}
public class ProjectModel
{
public virtual int Id { get; set; }
public virtual string ProjectName { get; set; }
public virtual IList<FinanceModel> Finances { get; set; }
}
public class ProjectMap:ClassMap<ProjectModel>
{
public ProjectMap() {
Table("Projects");
Id(x => x.Id);
Map(x => x.ProjectName);
HasMany(x => x.Finances);
}
}
public class FinanceMap : ClassMap<FinanceModel>
{
public FinanceMap()
{
Table("Finances");
Id(x => x.Id);
References(x => x.Project);
}
}

Related

nhibernate child collection limitation

I have these classes:
public class Document
{
public Document()
{
Descriptions = new List<Descriptions>();
}
public virtual int Id { get; set; }
public virtual IList<DocumentDescription> Descriptions { get; set; }
}
public class DocumentDescription
{
public virtual int DocumentId { get; set; }
public virtual int LanguageId { get; set; }
}
and mappings:
public DocumentMap()
{
Id(x => x.Id);
HasMany(x => x.Descriptions).KeyColumn("DocumentId");
}
public DocumentDescriptionMap()
{
CompositeId()
.KeyProperty(x => x.DocumentId)
.KeyProperty(x => x.LanguageId);
}
my query is:
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds)).List();
I need a query over solution to restrict DocumentDescriptions by few languages, which I will get run-time. I don't want to get all DocumentDescriptions for one Document (only few). Is it possible to set filter/limitation for a child collection?
So, I find out how to add additional join clause to my query:
DocumentDescription dd = null;
ICriterion criterion = Restrictions.On<DocumentDescription>(x => x.LanguageId).IsIn(languageIds.ToArray());
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds));
query.Left.JoinQueryOver(x => x.Descriptions, () => dd, criterion);
SQL:
SELECT * FROM tDocument
LEFT OUTER JOIN tDocumentDescription ON tDocumentDescription.DocumentId = tDocument.Id AND tDocumentDescription.LanguageId IN (#languageIds)
WHERE tDocument.Id IN (#documentIds)

Fluent NHibernate Composite Mapping

I am trying to map two tables :
the first one has an id (idA) and a field with the id (idB) of the other table.
the other one has a composite key based on the previous id (idB) and an other one (idB2)
The idea is that the second table contains the description of A split on multiple rows.
My current implementation is the following but I am unable to retrieve the partialDescription needed for concatenation.
How should I change my mapping to work? Any ideas? :)
public class A
{
long idA
lond idB
string fullDescription
}
public class B
{
long idB
long idB2
strind partialDescription
}
public class AMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.id).Column("idA").GeneratedBy.Native();
Map(x => x.idB).Column("idB")
HasMany(x => x.B).KeyColumn("idB").Inverse().Cascade.All().Not.LazyLoad();
}
}
public class BMap : ClassMap<B>
{
public BMap()
{
Table("B");
CompositeId()
.KeyReference(x => x.A, "idB")
.KeyProperty(x => x.idB2, "idB2");
Map(x => x.partialDescription, "desc").CustomType("AnsiString");
}
}
In fact, by your description, you must have this another scenario:
public class A
{
public virtual long Id { get; set; }
public virtual IList<B> PartialDescriptions { get; protected set; }
public string fullDescription
{
get
{
StringBuilder description = new StringBuilder();
foreach (var partial in PartialDescriptions)
{
description.Append(partial);
}
return description.ToString();
}
}
}
public class B
{
public virtual long Id { get; set; }
public virtual long Id2 { get; set; }
public virtual string Description { get; set; }
}
Then, try to implement your class maps like this:
public class BMap : ClassMap<B>
{
public BMap()
{
Table("B");
CompositeId()
.KeyReference(x => x.Id, "idB")
.KeyProperty(x => x.Id2, "idB2");
Map(x => x.partialDescription, "desc").CustomType("AnsiString");
}
}
public class AMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.Dd).Column("idA").GeneratedBy.Native();
HasMany(x => x.PartialDescriptions)
.KeyColumn("idB")
.Inverse()
.Cascade.All()
.Not.LazyLoad();
}
}
NOTE: I didn't tried to compile this code. I only expect that you can take the general .
I recommend you that take a look into Getting started section of Fluent NHibernate to more information.

NHibernate Criteria Queries - how use in One to One relationship

I have simple 3 POCO classes:
public class User
{
//PK
public virtual int UserId { get; set; }
//ONE to ONE
public virtual Profil Profil{ get; set; }
//ONE to MANY
public virtual IList<PhotoAlbum> Albums { get; set; }
}
public class Profil
{
//PK
public virtual int ProfilId { get; set; }
public virtual int Age { get; set; }
public virtual int Sex { get; set; }
}
public class PhotoAlbum
{
//PK
public virtual int PhotoAlbumId { get; set; }
public virtual string Name { get; set; }
public virtual int NumberOfPhoto { get; set; }
}
I created these mapping classes:
public class UserMap : ClassMap<User>
{
public UserMap()
{
//PK
Id(p => p.UserId)
.GeneratedBy.Identity();
//FK
References(p => p.Profil)
.Column("ProfilId")
.Cascade.All();
//ONE TO MANY
HasMany(p => p.Albums)
.Cascade.All();
Table("Users");
}
}
public class ProfilMap: ClassMap<Profil>
{
public ProfilMap()
{
Id(p => p.ProfilId)
.GeneratedBy.Identity();
Map(p => p.Age)
.Not.Nullable();
Map(p => p.Sex)
Table("Profiles");
}
}
public class PhotoAlbumMap : ClassMap<PhotoAlbum>
{
public PhotoAlbumMap()
{
Id(p => p.PhotoAlbumId)
.GeneratedBy.Identity();
Map(p => p.Name)
.Not.Nullable();
Map(p => p.NumberOfPhoto)
.Not.Nullable();
Table("PhotoAlbums");
}
}
Then I created simple NHibernate repository class with this method:
public IList<T> GetItemsByCriterions(params ICriterion[] criterions)
{
ICriteria criteria = AddCriterions(_session.CreateCriteria(typeof(T)),
criterions);
IList<T> result = criteria.List<T>();
return result ?? new List<T>(0);
}
For test I created repository for some entity, for example User:
_userRepo = new NHibRepository<User>(NHibeHelper.OpenSession());
and I would like have possibility make query in this style:
var users = _userRepo.GetItemsByCriterions(new ICriterion[]
{
Restrictions.Gt("Profile.Age",10)
});
this attempt finished with error:
could not resolve property: Profile of: Repository.User
User has property Profile type of Profile and this property has properties ProfileId, Age
and sex.
** #1 EDITED:**
# I tried this:
var users = _userRepo.GetItemsByCriterions(new ICriterion[]
{
Restrictions.Where<User>(u=>u.Profil.Sex==0)
});
finished with error:
could not resolve property: Profil.Sex of: Repository.User
#2 EDITED
I tried use Nathan’s advice:
var result = _userRepo.Session.CreateCriteria<User>()
.CreateAlias("Profile", "profile", JoinType.InnerJoin)
.Add(Restrictions.Eq("profile.Sex", 0));
IList<User> users=null;
if (result != null)
users = result.List<User>();
If I tried convert result to List I again get this error: could not resolve property: Profile of: Repository.User
Looking at your example, User has a Profil property not a Profile property.
If it is supposed to be Profil then I would change the Restrictions.Gt(Profile.Age,10) to Restrictions.Gt(Profil.Age,10) otherwise change the name of the property and mapping to match the query.
Edit:
You are trying to query the User Object. you need to include the CreateAlias let nhibernate know that you want to link to a different object.
Try This.
var users = session.CreateCriteria<User>()
.CreateAlias("Profile", "profile", JoinType.InnerJoin)
.Add(Restrictions.Eq("profile.Age", 10));

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?

NHibernate: Best way to deal with intermediary table using Fluent NHibernate?

How would you map the following in Fluent NHibernate?
See "18.3. Customer/Order/Product"
http://www.hibernate.org/hib_docs/nhibernate/html/example-mappings.html
The following solution uses the same approach as the solution in the example, and the generated XML is as good as the same. I have omitted specifying column names and such things for brevity.
Domain:
public class Customer
{
private ISet<Order> orders = new HashedSet<Order>();
public long Id { get; set; }
public string Name { get; set; }
public ISet<Order> Orders
{
get { return orders; }
private set { orders = value; }
}
}
public class Order
{
public long Id { get; set; }
public DateTime Date { get; set; }
public Customer Customer { get; set; }
public IList<LineItem> LineItems { get; private set; }
}
public class LineItem
{
public int Quantity { get; set; }
public Product Product { get; set; }
}
public class Product
{
public long Id { get; set; }
public string SerialNumber { get; set; }
}
Mapping:
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id)
.GeneratedBy.Native();
Map(x => x.Name);
HasMany<Order>(x => x.Orders)
.IsInverse()
.AsSet();
}
}
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
Id(x => x.Id)
.GeneratedBy.Native();
Map(x => x.Date);
References<Customer>(x => x.Customer);
HasMany<LineItem>(x => x.LineItems)
.Component(c =>
{
c.Map(x => x.Quantity);
c.References<Product>(x => x.Product);
}).AsList();
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id)
.GeneratedBy.Native();
Map(x => x.SerialNumber);
}
}
To see the generated XML mapping, you can use this code:
Configuration config = new Configuration().Configure();
PersistenceModel model = new PersistenceModel();
model.addMappingsFromAssembly(typeof(CustomerMap).Assembly);
model.Configure(config);
model.WriteMappingsTo("your folder name here");
I hope it helps.
/Erik