NHibernate projecting a collection of elements - nhibernate

I want to select all requests that are outstanding for a given manager. A manager can have multiple teams.
I compose the queries applying various restrictions based upon permissions, and alter the queries to provide row counts, existence checks, sub queries, etc.
The composition makes use of QueryOver, though using ICriteria instead would also be acceptable.
Given the following classes;
class Team {
public virtual int Manager { get; set; }
public virtual ISet<int> Members { get; set; }
}
class Request {
public virtual int Owner { get; set; }
public virtual bool IsOutstanding { get; set; }
}
class static SomeRestrictions {
public static void TeamsForManager<TRoot> (this IQueryOver<TRoot, Team> query, int managerId) {
// In reality this is a little more complex
query.Where (x => x.Manager == managerId);
}
}
This is the current query that I'm trying (which doesn't work).
var users = QueryOver.Of<Team> ();
users.TeamsForManager (5)
users.Select (/* not sure */);
var requests = session.QueryOver<Request> ()
.Where (x => x.IsOutstanding)
.WithSubquery.WhereProperty (x => x.Owner).In (users);
The HQL to select the users would be:
"SELECT m FROM Team t JOIN t.Members m WHERE <TeamsForManager restrictions>"
But I don't want to use HQL because I can't then compose it with other restrictions based upon permissions. I also wouldn't be able to compose it with other queries to turn it into row counts/existence checks, etc.

i saw you changed the model but this would have been the way
var users = QueryOver.Of<Team> ();
users.TeamsForManager (5);
users.JoinAlias(t => t.Members, () => membervalue).Select(() => membervalue);

Related

NHibernate JoinAlias on collection multiple times

I'm using NHibernate 3.33 and QueryOver with Postgre 9.2.
I've got two entities:
public class User {
public virtual string Name { get; set; }
public virtual IList<Reports> Reports { get; set; }
}
and
public class Report {
public virtual string Type { get; set; }
public virtual DateTime ReportDate { get; set; }
public virtual User Author { get; set; }
}
with association - one-to-many (I didn't append additional fields to entities like Id or Name to snippets above). Some report's types are avaliable - month, day.
My goal is to get summary for user - find out whether user has day-report and month-report for current day.
Note: month-report's ReportDate looks like first day of month. Also I want to get it as one row (if it was an SQL) to transform to dto:
public class UserSummaryDto {
public bool HasDayReport { get; set; }
public bool HasMonthReport { get; set; }
}
To achieve my goal I've tried following:
Report dayReport = null;
Report monthReport = null;
var currentDay; // some value of current day
var firstDay; // some value of first day of month
var report = session.QueryOver<User>
.Left.JoinAlias(u => u.Reports, () => dayReport, r => r.ReportDate == currentDay)
.Left.JoinAlias(u => u.Reports, () => monthReport, r => r.ReportDate == firstDat)
.SelectList(
// some logic to check whether user has reports
.TransformUsing(Transformers.AliasToBean<UserSummaryDto>())
.List<UserSummaryDto>()
And I've got error:
'duplicate association path:Reports'.
Is it possible to avoid this problem or it's a limitation of HNibernate?
To answer your question:
...Is it possible to avoid this problem or it's a limitation of HNibernate?
Have to say NO.
For more information see similar Q & A: Rename NHibernate criteria
We are not querying the DB, not using SQL (which does allow to do a lot). Here we work with "mapped" domain model, and that could bring some limitations - as the one discussed here...
If that could help, the workaround is to map such property twice and use the WHERE clause: 6.2. Mapping a Collection
where="" (optional) specify an arbitrary SQL WHERE condition to be used when retrieving or removing the collection (useful if the collection should contain only a subset of the available data)

Losing the record ID

I have a record structure where I have a parent record with many children records. On the same page I will have a couple queries to get all the children.
A later query I will get a record set when I expand it it shows "Proxy". That is fine an all for getting data from the record since everything is generally there. Only problem I have is when I go to grab the record "ID" it is always "0" since it is proxy. This makes it pretty tough when building a dropdown list where I use the record ID as the "selected value". What makes this worse is it is random. So out of a list of 5 items 2 of them will have an ID of "0" because they are proxy.
I can use evict to force it to load at times. However when I am needing lazy load (For Grids) the evict is bad since it kills the lazy load and I can't display the grid contents on the fly.
I am using the following to start my session:
ISession session = FluentSessionManager.SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
I even use ".SetFetchMode("MyTable", Eager)" within my queries and it still shows "Proxy".
Proxy is fine, but I need the record ID. Anyone else run into this and have a simple fix?
I would greatly appreciate some help on this.
Thanks.
Per request, here is the query I am running that will result in Patients.Children having an ID of "0" because it is showing up as "Proxy":
public IList<Patients> GetAllPatients()
{
return FluentSessionManager.GetSession()
.CreateCriteria<Patients>()
.Add(Expression.Eq("IsDeleted", false))
.SetFetchMode("Children", Eager)
.List<Patients>();
}
I have found the silver bullet that fixes the proxy issue where you loose your record id!
I was using ClearCache to take care of the problem. That worked just fine for the first couple layers in the record structure. However when you have a scenario of Parient.Child.AnotherLevel.OneMoreLevel.DownOneMore that would not fix the 4th and 5th levels. This method I came up with does. I also did find it mostly presented itself when I would have one to many followed by many to one mapping. So here is the answer to everyone else out there that is running into the same problem.
Domain Structure:
public class Parent : DomainBase<int>
{
public virtual int ID { get { return base.ID2; } set { base.ID2 = value; } }
public virtual string Name { get; set; }
....
}
DomainBase:
public abstract class DomainBase<Y>, IDomainBase<Y>
{
public virtual Y ID //Everything has an identity Key.
{
get;
set;
}
protected internal virtual Y ID2 // Real identity Key
{
get
{
Y myID = this.ID;
if (typeof(Y).ToString() == "System.Int32")
{
if (int.Parse(this.ID.ToString()) == 0)
{
myID = ReadOnlyID;
}
}
return myID;
}
set
{
this.ID = value;
this.ReadOnlyID = value;
}
}
protected internal virtual Y ReadOnlyID { get; set; } // Real identity Key
}
IDomainBase:
public interface IDomainBase<Y>
{
Y ID { get; set; }
}
Domain Mapping:
public class ParentMap : ClassMap<Parent, int>
{
public ParentMap()
{
Schema("dbo");
Table("Parent");
Id(x => x.ID);
Map(x => x.Name);
....
}
}
ClassMap:
public class ClassMap<TEntityType, TIdType> : FluentNHibernate.Mapping.ClassMap<TEntityType> where TEntityType : DomainBase<TIdType>
{
public ClassMap()
{
Id(x => x.ID, "ID");
Map(x => x.ReadOnlyID, "ID").ReadOnly();
}
}

RavenDB static index on document with dynamic field

I am trying to create a static index for the following sample class:
public class Board {
...other assorted fields
List<dynamic> Messages {get; set;}
internal Board() {Messages = new List<dynamic>();}
}
The index is to filter boards which have messages which are a older than a certain date. The aim is to perform an "update" operation on messages which are due today, update their content, and persist them back. The index is needed to avoid traversing all the messages for a board for all clients as that may be computationally expensive. Messages is a list of message types which inherit from a base class which contains a property ExpiryDate.
Trying to create an index like follows results in an "An expression tree may not contain a
dynamic operation" error. I know that the dynamic type does not play well with Linq queries hence the need to use LuceneQueries instead of Query() in RavenDB. Is there any way to make this index work with dynamic properties? Thanks!
public class ScanBoardMessagesIndex : AbstractIndexCreationTask<Board>
{
public ScanBoardMessagesIndex () {
Map = boards => from board in boards
where board.Messages.Any(msg => ((MessageItem) msg).ExpiryDate <= DateTime.UtcNow.Date)
select board;
}
}
EDIT:
I ran into a raven serialization issue because the metadata clr-type of existing Board documents was set to a class namespace which was not valid anymore. I am doing a migration project so I went ahead and first issued a patch to change the metadata clr-type of the existing documents before migrating them to the new data structure which uses a base/abstract class for list of Messages instead of type dynamic.
A Map/Reduce index seems more appropriate for the given requirements. Effectively, you want to be able to query boards by the oldest expiry date of messages in the board. This is an aggregating operation, exactly what Map/Reduce was designed to solve. Also, using a base class for messages will allow you to define the index without resorting to the lower level IndexDefinition:
public class Message
{
public DateTime ExpiryDate { get; set; }
}
public class Board
{
public string Id { get; set; }
public List<Message> Messages { get; set; }
}
public class OldestExpiryDateMessageInBoard : AbstractIndexCreationTask<Board, OldestExpiryDateMessageInBoard.Result>
{
class Result
{
public string BoardId { get; set; }
public DateTime OldestExpiryDate { get; set; }
}
public OldestExpiryDateMessageInBoard()
{
this.Map = boards => from board in boards
from message in board.Messages
select new
{
BoardId = board.Id,
OldestExpiryDate = message.ExpiryDate
};
this.Reduce = results => from result in results
group result by result.BoardId into g
select new
{
BoardId = g.Key,
OldestExpiryDate = g.Min(x => x.OldestExpiryDate)
};
}
}
You can then query this index with Lucene syntax.

Supersedes clause in database structure

Imagine a database table that looks like this:
create table [dbo].[user]
(
id int IDENTITY(1,1),
username varchar(50) NOT NULL,
firstname varchar(20) NOT NULL,
lastname varchar(30) NOT NULL,
currentid int NULL,
processedby varchar(50) NOT NULL,
processeddate varchar(50) NOT NULL
processedaction varchar(50) NOT NULL
)
What I want to do is to setup NHibernate to load it into my user object, but I only want the current version of the object "user" to be brought back. I know how to do a SQL select to do this on my own, and I feel as if there's something in nHibernate with the usage of triggers and event listeners, but can anyone tell me how to implement the nHibernate repository so I can:
{Repository}.GetCurrent(id) <- pass it any of the ids that are assigned to any of the historical or the current record, and get back the current object.
{Repository}.Save(user) <- I want to always insert the changes to a new row, and then update the old versions to link back to the new id.
Edit
So, there's some confusion here, and maybe I explained it wrong... What I'm trying to do is this, in regards to always getting the current record back...
Select uc.*
FROM User uo
JOIN User uc on uo.currentid=uc.id
WHERE uo.id==:id
But, I don't want to expose "CurrentID" to my object model, since it has no bearing on the rest of the system, IMHO. In the above SQL statement, uo is considered the "original" object set, and uc is considered the current object in the system.
Edit #2:
Looking at this as a possible solution.
http://ayende.com/blog/4196/append-only-models-with-nhibernate
I'm honestly being pigheaded, as I'm thinking about this backward. In this way of running a database, the autoincrementing field should be the version field, and the "id" field should be whatever the autoincrementer's value has at the time of the initial insert.
Answer:
I don't want to take #Firo's fury, and I'm not going to remove it from him, as he took me down the right path... what I wound up with was:
Created a base generic class with two types given
a. type of the object's "ID"
b. type of the object itself.
instantiate all classes.
create a generic interface IRepository class with a type of the object to store/retrieve.
create an abstract generic class with a type of the object to store/retrieve.
create a concrete implementation class for each type to store/retrieve.
inside of the create/update, the procedure looks like:
Type Commit(Type item)
{
var clone = item.DeepClone();
_Session.Evict(item);
clone.Id = 0;
clone.ProcessedDate = DateTime.Now;
if (clone.Action.HasValue)
{
if (clone.Action == ProcessedAction.Create)
clone.Action = ProcessedAction.Update;
}
else
{
clone.Action = ProcessedAction.Create;
}
clone.ProcessedBy = UserRepos.Where(u => u.Username == System.Threading.Thread.CurrentPrincipal.Identity.Name).First().Current;
var savedItem = (_Session.Merge(clone) as Type);
_Session.CreateQuery("UPDATE Type SET CurrentID = :newID where ID=:newID OR CurrentID=:oldID")
.SetParameter("newID", savedItem.Id)
.SetParameter("oldID", item.Id)
.ExecuteUpdate();
return savedItem;
}
In the delete method, we simply update the {object}.Action = ProcessedAction.Delete
I wanted to do this another way, but realizing we need to eventually do historical comparisons, we weren't able to ask nHibernate to filter the deleted objects, as the users will want to see that. We'll create a business facade to take care of the deleted records.
Again, much thanks to #Firo for his help with this.
So, with all that, I can finally do this:
var result = {Repository}.Where(obj => obj.Id == {objectID from caller}).FirstOrDefault();
if (result != null)
{
return result.Current;
}
else
{
return null;
}
and always get my current object back for any requesting ID. Hope it helps someone that is in my situation.
in mapping if you use FluentNHibernate
public UserMap : ClassMap<User>
{
public UserMap()
{
Where("id = currentid"); // always bring back the most recent
}
}
// in Userrepository
public void Update(User user)
{
var clone = user.Clone();
session.Evict(user); // to prevent flushing the changes
var newId = session.Save(clone);
session.CreateQuery("UPDATE User u SET u.currentid = :current") // <-- hql
.SetParameter("current", newId)
.ExecuteUpdate();
}
objectgraphs are a lot trickier with this simple code. I would then do one of the following:
use NHibernate.Envers to store auditing information for me
explicitly creating new entities in BL code
i once saw an append-only-model doing something like the following
// UserBase is there to ensure that all others referencing the User doesnt have to update because user properties changed
class UserBase
{
public virtual int Id { get; set; }
public virtual ICollection<PersonDetails> AllDetails { get; private set; }
public virtual PersonDetails CurrentDetails
{
get { return _currentDetauils; }
set { _currentDetauils = value; AllDetails.Add(value); }
}
// same as above
public virtual ICollection<ConfigDetails> AllConfigs { get; set; }
}
class Order
{
public virtual int Id { get; set; }
public virtual UserBase User { get; set; }
public virtual IList<OrderDetail> AllDetails { get; private set; }
public virtual IList<OrderDetail> ActiveDetails { get; private set; }
public virtual void Add(OrderDetail detail)
{
AllDetails.Add(detail);
ActiveDetails.Add(detail);
}
public virtual void Delete(OrderDetail detail)
{
detail.Active = false;
ActiveDetails.Remove(detail);
}
}
class OrderDetail
{
public virtual int Id { get; set; }
public virtual Order Parent { get; set; }
public virtual bool Active { get; set; }
}
class OrderMap : ClassMap<Order>
{
public OrderMap()
{
HasMany(o => o.AllDetails);
HasMany(o => o.ActiveDetails).Where("active=1");
}
}
// somewhere
public void UpdateTaxCharge(OrderDetail detail, TaxCharge charge)
{
var clone = detail.Clone();
clone.TaxCharge = charge;
detail.Order.Delete(detail);
detail.Order.Add(clone);
}
You can tell NHibernate what exactly SQL it should generate when persisting and loading an entity. For example you can tell NHibernate to use a stored procedure instead of a plain SQL statement. If this is an option for you I can farther elaborate my answer.

EF4 conditional mapping over a relationship

I'm currently using conditional mappings in EF4 to filter out any records where their IsActive column is false. This works as intended, but I'm running into an issue when navigating relationships.
As an example, I have a one-to-many relationship where a Store can have many Inventory records. A Store can be marked IsActive as can the Inventory records that belong to it. Querying directly for records in those tables works as intended (only active records are returned), but I am also able to retrieve Inventory records where the associated store is not active. This allows the inactive Store to be accessed and gives me no way of detecting whether the store is active.
Prior to switching over to using conditional mapping, we were using queries similar to the following:
Inventories.Where(i => i.IsActive && i.Store.IsActive && i.Product.IsActive && i.Product.Id == productId);
I was hoping we could simplify that query to this:
Inventories.Where(i => i.Product.Id == productId);
This doesn't work for me though, as I no longer have any way of knowing whether the Store or Product are active (as I can't use conditional mapping on IsActive while still mapping that column).
Is there any way of replicating that query while taking advantage of conditional mapping in EF4? Am I forced to leave conditional mapping out of this and hope that all queries make sure to check all relevant IsActive fields?
You could write an expression visitor to add those properties to your query.
Example:
public abstract class ActiveObject
{
public bool IsActive { get; set; }
protected ActiveObject()
{
this.IsActive = true;
}
}
public class Inventory : ActiveObject
{
public Product Product { get; private set; }
public Store Store { get; private set; }
public Inventory()
{
this.Store = new Store();
this.Product = new Product { Id = 10 };
}
}
public class Product : ActiveObject
{
public int Id { get; set; }
}
public class Store : ActiveObject
{
public int Id { get; set; }
}
class Program
{
static void Main()
{
Expression<Func<Inventory, bool>> expression = i => i.Product.Id == 10;
Expression<Func<Inventory, bool>> expression2 = Rewrite(expression);
}
private static Expression<Func<Inventory, bool>> Rewrite(Expression<Func<Inventory, bool>> lambdaExpression)
{
var inventory = lambdaExpression.Parameters[0];
return Expression.Lambda<Func<Inventory, bool>>(
Expression.AndAlso(
Expression.AndAlso(
Expression.Property(
inventory,
"IsActive"
),
Expression.AndAlso(
Expression.Property(
Expression.Property(
inventory,
"Store"
),
"IsActive"
),
Expression.Property(
Expression.Property(
inventory,
"Product"
),
"IsActive"
)
)
),
lambdaExpression.Body
),
inventory
);
}
}
While the other answer may work for some, we decided to map the EF EntitySet to a view instead. The view joins to appropriate tables and checks their appropriate IsActive fields. The resulting portion of our EDMX looks similar to the following:
<EntitySet Name="Inventory" EntityType="Model.Store.Inventory" store:Type="Views" store:Schema="dbo" store:Name="Inventory">
<DefiningQuery>SELECT
[ActiveInventory].[InventoryId] AS [InventoryId],
{Other columns being selected}
FROM [dbo].[ActiveInventory] AS [ActiveInventory]</DefiningQuery>
</EntitySet>