I have submitted a patch to add MultiQuery support to ActiveRecord. The link is at http://support.castleproject.org/browse/AR-139. I added ActiveRecordMultiQuery.cs and updated ActiveRecordBaseQuery.cs to allow subclasses to be aggregated (added) to the MultiQuery. I did this to speed up initialization of a form (I know ... premature optimization is the root of all evil). If you like this patch, log in and vote for it!
Update: The patch has been applied. In the mean time, let me caution you that ActiveRecordCriteriaQuery from the svn repository (which I also wrote and contributed) and any other "query" that uses criteria will not correctly participate in a multi-query. But if we give the NHibernate team some time, I'm sure this will be resolved.
Regarding the implementation, the only thing that I really wondered about was the "root type". This basically selects the session to be created. So all queries in the multi-query need to be registered with the same database.
Why did I do this? Because I would like all of my application code (after various patches are accepted) to be relatively free of NHibernate references. If my application only refers to ActiveRecord instances, the implementors will be free to switch to any other O/R persistence engine (not that I want that!), and also my code will be more resilient to NHibernate upgrades. At the moment, this is impossible, because I use Criteria, DetachedCriteria, and various other NHibernate expressions. I have considered creating an ARSupport namespace to wrap NHibernate Criteria, and add Find, FindAll, FindFirst, FindOne, Count and Exists. One alternative would be to leverage the IQueryModifier interface, but I can't really see this working, since it is only used to set values for named or positional parameters. I am just TOO hooked on Criteria. Anyway, I do not want to have references to ISession, IQuery, Configuration, etc. sprinkled throughout my code.
As a side note, it looks like NHibernate is trying to consolidate their interfaces. I very much approve of this. An example is that ISession.CreateSQLQuery has 3 methods, but 2 are tagged as obsolete with a message explaining correct usage.
ActiveRecordBaseQuery
ActiveRecordMultiQuery
Update: The patch has been applied. In the mean time, let me caution you that ActiveRecordCriteriaQuery from the svn repository (which I also wrote and contributed) and any other "query" that uses criteria will not correctly participate in a multi-query. But if we give the NHibernate team some time, I'm sure this will be resolved.
Regarding the implementation, the only thing that I really wondered about was the "root type". This basically selects the session to be created. So all queries in the multi-query need to be registered with the same database.
Why did I do this? Because I would like all of my application code (after various patches are accepted) to be relatively free of NHibernate references. If my application only refers to ActiveRecord instances, the implementors will be free to switch to any other O/R persistence engine (not that I want that!), and also my code will be more resilient to NHibernate upgrades. At the moment, this is impossible, because I use Criteria, DetachedCriteria, and various other NHibernate expressions. I have considered creating an ARSupport namespace to wrap NHibernate Criteria, and add Find, FindAll, FindFirst, FindOne, Count and Exists. One alternative would be to leverage the IQueryModifier interface, but I can't really see this working, since it is only used to set values for named or positional parameters. I am just TOO hooked on Criteria. Anyway, I do not want to have references to ISession, IQuery, Configuration, etc. sprinkled throughout my code.
As a side note, it looks like NHibernate is trying to consolidate their interfaces. I very much approve of this. An example is that ISession.CreateSQLQuery has 3 methods, but 2 are tagged as obsolete with a message explaining correct usage.
ActiveRecordBaseQuery
public abstract class ActiveRecordBaseQuery ...
...
/// <summary>
/// Add this query to a multiquery
/// </summary>
/// <param name="session">an <c>ISession</c> shared by all queries in the multiquery</param>
/// <param name="multiquery">the <c>IMultiQuery</c> that will receive the newly created query</param>
internal void AddQuery(ISession session, IMultiQuery multiquery)
{
IQuery query = CreateQuery(session);
multiquery.Add(query);
}
ActiveRecordMultiQuery
// Copyright 2004-2007 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace Castle.ActiveRecord.Queries
{
using System;
using System.Collections;
#if DOTNET2
using System.Collections.Generic;
#endif
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using Castle.ActiveRecord.Framework.Config;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Expression;
/// <summary>
/// wrapper for an IMultiQuery that executes a collection of queries.
/// </summary>
public class ActiveRecordMultiQuery : IActiveRecordQuery
{
Type _rootType;
#if DOTNET2
List<ActiveRecordBaseQuery> _queryList = new List<ActiveRecordBaseQuery>();
#else
ArrayList _queryList = new ArrayList();
#endif
/// <summary>
/// Initializes a new instance of the <see cref="ActiveRecordMultiQuery"/> class.
/// </summary>
/// <param name="RootType">the root type for all of the queries that will be included in the <c>IMultiQuery</c></param>
public ActiveRecordMultiQuery(Type RootType)
{
_rootType = RootType;
}
/// <summary>
/// Initializes a new instance of the <see cref="ActiveRecordMultiQuery"/> class.
/// </summary>
/// <param name="RootType">the root type for all of the queries that will be included in the <c>IMultiQuery</c></param>
/// <param name="activeRecordQueries">an array of <c>IActiveRecordQuery</c></param>
public ActiveRecordMultiQuery(Type RootType, ActiveRecordBaseQuery[] activeRecordQueries)
: this(RootType)
{
_queryList.AddRange(activeRecordQueries);
}
/// <summary>
/// Add an <c>IActiveRecordQuery</c> to our <see cref="ActiveRecordMultiQuery"/>
/// </summary>
/// <param name="activeRecordQuery"><c>IActiveRecordQuery</c> to be added to the MultiQuery</param>
public void Add(ActiveRecordBaseQuery activeRecordQuery)
{
_queryList.Add(activeRecordQuery);
}
#region IActiveRecordQuery Members
/// <summary>
/// Gets the target type of this query
/// </summary>
public Type RootType
{
get { return _rootType; }
}
/// <summary>
/// Executes the specified query and return the results
/// </summary>
/// <param name="session">The session to execute the query in.</param>
/// <returns>an array of results, one for each query added</returns>
public object Execute(NHibernate.ISession session)
{
// create a multi-query
IMultiQuery multiQuery = session.CreateMultiQuery();
foreach (ActiveRecordBaseQuery arQuery in _queryList)
{
// add the executable IQuery to our multi-query
arQuery.AddQuery(session, multiQuery);
}
// execute multiquery
object resultSetArray = multiQuery.List();
return resultSetArray;
}
/// <summary>
/// (Not Implemented!)
/// Enumerates over the result of the query.
/// Note: Only use if you expect most of your values to already exist in the second level cache!
/// </summary>
public System.Collections.IEnumerable Enumerate(NHibernate.ISession session)
{
throw new Exception("The method or operation is not implemented.");
}
#endregion
}
}
Comments