Fluent NHibernate - Query using Criteria with filter on connecting table - fluent-nhibernate

I have following tables
User - Primary column - Id and other detail columns
Department - Primary column - Id and other detail columns
UserDepartment - Primary column - Id and other columns are UserId and DepartmentId
I want to find all users those are in department - (1 and 2).
I also want to find all users those are in department - (1 or 2).
Can anybody suggest me the criteria to get all users in department (1 and 2)? Another criteria to get all users in department - (1 or 2)?
I am new to FluentNHibernate, so didn't tried anything yet as I found nothing relevant on Google search? With the help of Google search, I was able to write criteria for 1-1 relations but not for above scenario.

assuming following classes
class User
{
public virtual int Id { get; private set; }
public virtual ICollection<Department> Departments { get; private set; }
}
class Department
{
public virtual int Id { get; private set; }
}
class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
HasManyToMany(x => x.Departments)
.Table("UserDepartment")
.ParentKeyColumn("UserId")
.ChildKeyColumn("DepartmentId");
}
}
then query for 1 or 2
var results = session.QueryOver<User>()
.JoinQueryOver<Department>(x => x.Departments)
.Where(d => d.Id.IsIn(departmentIds))
.List();
query for 1 and 2
User userAlias = null;
var subquery = session.QueryOver<Department>()
.Where(d => d.User.Id == userAlias.Id && d.Id.IsIn(departmentIds))
.Select(Projections.RowCount());
var results = session.QueryOver<User>()
.WithSubquery.WhereValue(departments.Count).Eq(subquery)
.List();

Related

Not sure how to join tables using Fluent NHibernate

I'm using NHibernate on legacy tables and after looking through our codebase, I seem to be the only person with this need. I need to join two tables so I can run a query, but I haven't made any progress today. I'll try to abbreviate where it makes sense in my code snippets. Care to help?
Tables--
Order
OrderID (primary key)
OrderName
OrderType
OrderLocation
OrderAppendix
ID (composite key)
Key (composite key)
Value (composite key)
The ID portion of the OrderAppendix composite key is associated with OrderID in the Order table; therefore, and Order can have several entries in the OrderAppendix table.
Domain Objects--
[Serializable]
public class Order
{
public virtual string OrderID { get; set; }
...
public virtual string OrderLocation { get; set; }
}
[Serializable]
public class OrderAppendix
{
public virtual string ID { get; set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
public override bool Equals(object obj)
{
...
}
public override int GetHashCode()
{
...
}
}
Mapping
internal sealed class OrderMap : ClassMap<Order>
{
Table("Order");
Id(x => x.OrderID).Column("OrderID").Length(20).GeneratedBy.Assigned();
Map( x => x.OrderName).Column("OrderName")
....
}
internal sealed class OrderAppendixMap : ClassMap<OrderAppendix>
{
Table("OrderAppendix");
CompositeId()
.KeyProperty(x => x.ID, "ID")
....
Map( x => x.ID).Column("ID);
...
}
I won't muddy this up with my futile attempts at joining these tables, but what I would like to do is query by things like OrderType or OrderLocation given that the results all have the same Value from the OrderAppendix table.
Example desired SQL
SELECT * FROM ORDER
INNER JOIN
ON Order.OrderID = OrderAppendix.ID
WHERE OrderAppendix.Key = "Stuff"
Edit
Here's where I've gotten by reading the documentation on "QueryOver"
http://nhibernate.info/doc/nh/en/index.html#queryqueryover
Order Order = null;
OrderAppendix OrderAppendix = null;
resultList = session.QueryOver<Order>(() => Order)
.JoinAlias(() => Order.OrderAppendix, () => OrderAppendix)
.Where(() => OrderAppendix.Key == "MatchThis")
.List();
I think I'm on the right track using aliases to join the table, but obviously I haven't found a way to inform NHibernate of the many-to-one mapping that's needed. Also, you can see that I've added a property of type OrderAppendix to Order in order to the use the alias functionality.
Well, I didn't get why you did not used standard parent-child approach using HasMany on Order side and Reference of Appendix. This is because of composite key on appendix side?
If you would do that, you will be able to provide following HQL:
SELECT o FROM Order o
LEFT JOIN FETCH o.apxs a
WHERE a.Key="Stuff"

(Fluent) NHibernate: Map field from separate table into object

I'm currently trying to get (Fluent)NHibernate to map an object to our legacy database schema. There are three tables involved.
Table a contains most information of the actual object I need to retrieve
Table b is a table which connects table a with table c
Table c has one additional field I need for the object
An SQL query to retrieve the information looks like this:
SELECT z.ID, z.ZANR, e.TDTEXT
FROM PUB.table_a z
JOIN PUB.table_b t ON (t.TDKEY = 602)
JOIN PUB.table_c e ON (e.ID = t.ID AND e.TDNR = z.ZANR)
WHERE z.ZANR = 1;
The main problem is how to specify these two join conditions in the mapping.
Entity for table a looks like this:
public class EntityA
{
public virtual long Id { get; set; }
public virtual int Number { get; set; }
public virtual string Name { get; set; }
}
Name should map to the column table_c.TDTEXT.
The mapping I have so far is this:
public class EntityAMap : ClassMap<EntityA>
{
public EntityAMap()
{
Table("PUB.table_a");
Id(x => x.Id).Column("ID");
Map(x => x.Number).Column("ZANR");
}
}
I tried to map the first join with the same strategy as in How to join table in fluent nhibernate, however this will not work, because I do not have a direct reference from table_a to table_b, the only thing connecting them is the constant number 602 (see SQL-query above).
I didn't find a way to specify that constant in the mapping somehow.
you could map the name as readonly property easily
public EntityAMap()
{
Table("PUB.table_a");
Id(x => x.Id).Column("ID");
// if ZANR always has to be 1
Where("ZANR = 1");
Map(x => x.Number).Column("ZANR");
Map(x => x.Name).Formula("(SELECT c.TDTEXT FROM PUB.table_b b JOIN PUB.table_c c ON (c.ID = b.ID AND b.TDKEY = 602 AND c.TDNR = ZANR))");
}
Update: i could only imagine a hidden reference and the name property delegating to there
public EntityAMap()
{
HasOne(Reveal.Member<EntityA>("hiddenEntityC"));
}
public class EntityB
{
public virtual int Id { get; set; }
public virtual EntityA EntityA { get; set; }
}
public EntityCMap()
{
Where("Id = (SELECT b.Id FROM PUB.table_b b WHERE b.TDKEY = 602)");
References(x => x.EntityA).PropertyRef(x => x.Number);
}

NHibernate: controlling Bidirectional ManyToMany. Adding from one entity and removing from the other entity

I have 2 tables with manyToMany relationship. user can have many groups and groups can have many users.
public class User
{
public virtual Guid Id { get; set; }
public virtual UInt64 UserId { get; set; }
public virtual IList<Group> Groups{ get; set; }
}
public class Group
{
public virtual Guid Id { get; set; }
public virtual Guid GroupGuid { get; set; }
}
I use Fluent NHibernate to AutoMap them as follows:
.Override<User>(obj => obj.HasManyToMany(x => x.Groups).Table("UserToGroup").Cascade.AllDeleteOrphan())
As a result Nhibernate produce 3 tables:
1. Users - Mapped object table
2. Groups Mapped Object table
3. UserToGroup - generated association table
The requirement is to have the following functionality on these tables:
1. AddUser(UInt64 UserId, IEnumerable groups)
2. RemoveGroup(GroupGuid groupGuild)
3. Dictionary> GetGroupToUsers();
AddUser method is work Ok, All table updated OK:
* Insert of new user in User table. OK
* Insert of new group in Group table. OK
* Insert of new relations recods in UserToGroup table. OK
I use the following code to add a User (including groups):
public void AddUser(User userData, IList<Guid> groupIds)
{
User user = new User();
user.UserId = userData.UserId;
IList<User> Users = new List<Users();
Users.Add(user);
IList<Group> groups = new List<Group>();
foreach (Guid groupId in groupIds)
{
Group grp = new Group();
grp.GroupGuid = groupId;
grp.Users = Users;
groups.Add(grp);
}
user.Groups = groups;
Session.Save(user);
}
I use the following code to Remove group:
public void RemoveGroup(GroupGuid groupGuild)
{
IList<Group> groupsToRemove = Session.QueryOver<Group>().Where(
row => row.GroupGuid == groupGuild).List();
foreach (Group group in groupsToRemove)
{
group.Users.Clear();
Session.Delete(group);
}
}
RemoveGroup method failed with the following exception:
threw exception:
NHibernate.Exceptions.GenericADOException: could not delete: [StorageObject.Group#11111111-1111-1111-1111-111111111111][SQL: DELETE FROM "Group" WHERE Id = ?] ---> System.Data.SqlServerCe.SqlCeException: The primary key value cannot be deleted because references to this key still exist. [ Foreign key constraint name = FK4B29424018FA0CD4 ]
I understand that Group table can't be delete since the reference UserToGroup table point to Group table.
1st question: how can I make hibernate to understand that I want to be able to controller this manyToMany relation from both side:
Add users with groups - from user side.
And delete groups - from group side
I tried to move Inverse to the User table but in this case when I add a User the association table is not populate.
What is the best way to define this kind of manyToMany relation?
Update:
Another way I tried is to have List of Users also in Groups in this configuration removeGroup is wokring but addUser in not working for specific scenario:
public class Group
{
public virtual Guid Id { get; set; }
public virtual Guid GroupGuid { get; set; }
public virtual IList<User> Users{ get; set; }
}
public class User
{
public virtual Guid Id { get; set; }
public virtual UInt64 UserId { get; set; }
public virtual IList<Group> Groups{ get; set; }
}
Now I have a bidirectional many to many.
My Fluent mapping is:
.Override<User>(obj => obj.HasManyToMany(x => x.Groups).ParentKeyColumn("UserId").ChildKeyColumn("GroupId").Table("UserToGroup").Cascade.SaveUpdate()
.Override<Group>(obj => obj.HasManyToMany(x => x.Users).ParentKeyColumn("GroupId").ChildKeyColumn("UserId").Table("UserToGroup")
If I use this configuration RemoveGroup works fine but AddUser is not working for the following case:
When adding 2 Users which contains the same Group the association table delete the first relation and holds just the last reference instead of having both relations.
Please advise.
Try to add an inverse collection in Group:
public class Group
{
public virtual Guid Id { get; set; }
public virtual Guid GroupGuid { get; set; }
public virtual IList<User> Users { get; set; }
}
In your mapping:
.Override<Group>(obj => obj.HasManyToMany(x => x.Users).Table("UserToGroup").Inverse().Cascade.SaveUpdate())
Your Remove method:
public void RemoveGroup(GroupGuid groupGuild)
{
IList<Group> groupsToRemove = Session.QueryOver<Group>().Where(
row => row.GroupGuid == groupGuild).List();
foreach (Group group in groupsToRemove)
{
group.Users.Clear(); // Removes references from all users pointing to that group
Session.Delete(group);
}
}
When you commit the transaction, the references should be automatically removed. I never tried this by myself, but it might work for you.

Trying to Query RavenDB Entity with a Collection via Index

I have a RavenDB mvc applicaton that has a document entity called Member. Each Member document has a list of users that are considered administrators. Only they can view and manage that Member document. On one of the pages I have a member search and have created an index to assist in the search.
public class Members_ByName : AbstractIndexCreationTask<Member>
{
public Members_ByName()
{
Map = members => from member in members select new {member.Title};
Indexes.Add(x => x.Title, FieldIndexing.Analyzed);
Sort(x => x.Title, SortOptions.String);
}
}
public class UserReference
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Member
{
public string Id { get; set; }
public string Title { get; set; }
public ICollection<UserReference> Administrators { get; set; }
}
Since the user can only view/manage Member documents where they are an Administrator when I do the following to get the members
RavenQueryStatistics stats;
var query = RavenSession.Query<Member, Members_ByName>().Statistics(out stats);
query = query.Where(x => x.Title.StartsWith("anything"));
query = query.Where(x => x.Administrators.Any(y => y.Id == CurrentUser.Id));
var list = query.OrderBy(x => x.Title).Paging(CurrentPage, Configuration.DefaultPage, CurrentPageSize).ToList();
When the above code runs I get "The field 'Administrators_Id' is not indexed, cannot query on fields that are not indexed" which I understand but every thing I have attempted to get Administrator's Id in the index has not worked and not sure how to make it work at this point.
Try this:
Map = members => from member in members
select new {member.Title, Administrators_Id = members.Administrators.Select(x=>x.Id)};

NHibernate returning duplicate rows

NHibernate appears to be returning the contents of the first row multiple times. As many times as there are actual, distinct rows in the database. For example, if one person has 3 campus affiliations like this:
Baker College - Teacher
Bryant Elementary - Teacher
Ohio State University - Student
NHibernate will return it like this:
Baker College - Teacher
Baker College - Teacher
Baker College - Teacher
I'm using FluentNHibernate. Here are some snippets of the entity and mapping files:
public class Person
{
public virtual string SysID { get; set; }
public virtual string FullName { get; set; }
public virtual ICollection<Campus> Campuses { get; set; }
}
public class Campus
{
public virtual string SysID { get; set; }
public virtual string Name { get; set; }
public virtual string Affiliation { get; set; }
}
public class PersonMapping
{
Table("Person");
Id(x => x.SysId);
Map(x => x.FullName).Column("FULL_NAME");
HasMany(x => x.Campuses).KeyColumn("SysId");
}
public class CampusMapping
{
Table("Campus");
Id(x => x.SysID);
Map(x => x.Name);
Map(x => x.Affiliation);
}
I'm iterating through the campuses in my view (MVC 3) like this:
#foreach(var campus in Model.Campuses)
{
#campus.Name #campus.Affiliation
}
I also tried adding this to the entity to make sure it wasn't a silly mistake with MVC or Razor and had the same result:
public virtual string campusesToString
{
get
{
string s = "";
for (int i = 0; i < Campuses.Count; i++)
{
s = s + Campuses.ElementAt(i).Name + " ";
}
return s;
}
}
Finally, I checked the SQL that was being output, and it's correct, and it's returning all of the rows uniquely...
Your mapping looks a bit weird.
First you set up this relationship: Person 1 -> * Campus
But in your mapping of Campus you make SysId primary key, but it is also the foreign key to Person? I think that is what confuses NHibernate..
What I think happens is that NHibernate sees the same SysId key multiple times and since it will resolve the same object for the same primary key to preserve object indentity it will return the same Campus object multiple times even though the other columns have different data.
You might want to use a many-to-many mapping as otherwise each Campus will only be able to have one person which seems wrong.
Ok. I found out my problem. I was indicating the Id in the mapping incorrectly.
There is one person and multiple campuses. The person ID is called SysID. It is also the foreign key on campus. But SysID is not the unique ID for campus. NHibernate was confused because it was trying to use the person unique ID for the campus ID.
public class Campus
{
public virtual string CampusID { get; set; }
public virtual string SysID { get; set; }
public virtual string Name { get; set; }
public virtual string Affiliation { get; set; }
}
public class CampusMapping
{
Table("Campus");
Id(x => x.CampusID);
Map(x => x.SysID);
Map(x => x.Name);
Map(x => x.Affiliation);
}