kenegozi.com

<form id='kenegozi' action='post'></form>

   
2008 Jun 23

Fabio and Ayende On Caching

tagged as: architecture | nhibernate

A quick ripoff from NHibernate's users group:

 

Fabio Maulo:

The base concepts to understand are (my opinion):
- *The Cache is not the panacea of performance.*
- Don't use the Cache like the base of your app; add the management of Cache at the end of your development process to increase the performance only where you really need do it.
- Implementing a method named GetAll is, in most cases, a bad idea; an acceptable mediation is PaginateAll(pageSize).
- InMemoryFilter can have less performance than filter trough RDBMS (especially when you intent to do it trough Cache with a large amount of entities).
- Take care on what happen to the memory usage of your app when you are using Cache.

 

Ayende:

The cache is not magic, and should not be treated in such a fashion. I refuse to use the 2nd level cache in my applications until I have a perf problem that can't be solved by creating smarter queries.
Think about the cache as band aid, and good design as avoiding the need for that.

 

And I say Hallelujahs

2008 May 12

Cloning a DetachedCriteria

tagged as: activerecord | nhibernate

I always need to look it up, so I might as well put it here for future reference:

 

What is a DetachedCriteria?

This is an ICriteria object, that is detached from any ISession, therefore is suitable to be passed around as a specification that is being built up dynamically.

 

Why would I want to clone it?

Well, say I want to run two similar queries (same WHERE part) but with a different projection or aggregation. For example - when fetching a paged collection, you'd want to create the spec criteria, then add Count projection on A to get the 'total number of records' ,and add the paging restrictions (sort, then from row 101 take 10 records). Now if you'd try to add the restrictions to the criteria to which you have already applied Count on, and error would occur.

 

 

Solution:

There's CriteriaTransformer class, which holds a few useful static methods, amongst them, Clone(DetachedCritetia):

DetachedCriteria spec = DetachedCriteria.For<Whadever>()
   ...; <= setup criterions

DetachedCriteria countCriteria = CriteriaTransformer.Clone(spec)
...; <= setup the count projection

DetachedCriteria pagedCriteria = CriteriaTransformer.Clone(spec)
...; <= setup the paging


ICollection<Whadever> stuff = whadeverRepository.FindAll(pagedCriteria);

int total = whadeverRepository.Count(countCriteria);
2008 May 9

Using nhibernate's named queries with ActiveRecord

tagged as: castle | activerecord | nhibernate | hql

One of the methods of querying the DB when using NHibernate, is to issue HQL queries. HQL stands for "Hibernate Query Language". It has a SQL-like syntax and is very intuitive for people with SQL background.

 

The way this works is that NHibernate 'compiles' the HQL query into SQL, and then issues the SQL query (using ADO.NET's facilities) to the DB.

 

Sounds pricey?

enter Named Queries.

now these are HQL (or SQL) queries, each has a name (obviously), that are been supplied to NH through the mapping. The queries are being translated and cached as IDbCommand objects as part of the framework initialisation, which mean that you get rid of the HQL->SQL overhead throughout the life of the process.

 

One other major benefit, is that the mechanism to actually execute these named queries, does not differentiate between HQL and SQL queries (for the simple fact that these queries have already been transferred to SQL at runtime). That gives you the possibility to replace HQL queries into tighter SQL queries (with the same parameters and which returns the same resultset) should your DBA figure out a better one.

 

But if you're using ActiveRecord, you usually do not have direct access into the mapping (.hbm) files. So how would you use named queries with AR?

 

enter HqlNamedQueryAttribute (not such a great name, as I did state that it would also work for SQL queries).

 

So, for an example, on this blog's source code, within PostRepository.cs you'd see this code:

...

 

#region queries
[assembly: HqlNamedQuery(Queries.FindPostsInArchive, Queries.FindPostsInArchive)]
[assembly: HqlNamedQuery(Queries.FindByUrlFriendlyTagName, Queries.FindByUrlFriendlyTagName)]
namespace KenEgozi.Com.Domain
{
    internal partial class Queries
    {
        internal const string FindPostsInArchive = @"
                from Post p
                where
                    year(p.Lifecycle.CreationDate) = :year and
                    month(p.Lifecycle.CreationDate) = :month
                order by p.Lifecycle.CreationDate desc";
        internal const string FindByUrlFriendlyTagName = @"
                select p
                from
                            Post p
                    join    p.Tags t
                where t.UrlFriendlyName like :urlFriendlyTagName
                order by p.Lifecycle.CreationDate desc;";
    }
}
#endregion

...

 

        public ICollection<Post> FindInArchive(int year, int month)
        {

            return session
                .GetNamedQuery(Queries.FindPostsInArchive)
                .SetParameter("year", year)
                .SetParameter("month", month)
                .List<Post>();
        }

...

Queries class is marked as partial, as other queries might be presented on other repositories or services that would need to add more named queries. I considered grouping of queries into groups by the using repository, or by aggregate roots, but the thing is - having all of the queries under the same namespace helps discoverability, and helps with preventing duplications

2008 Apr 3

Logging SQL output from NHibernate, using Log4Net

tagged as: tools | nhibernate

Following a question from NHibernate's users list:

<configSections>
  <section name="log4net"
            type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
 
<log4net>
  <appender name="rollingFile"
            type="log4net.Appender.RollingFileAppender,log4net" >
    <param name="File" value="log.txt" />
    <param name="AppendToFile" value="true" />
    <param name="DatePattern" value="yyyy.MM.dd" />
    <layout type="log4net.Layout.PatternLayout,log4net">
      <conversionPattern value="%d %p %m%n" />
    </layout>
  </appender>
  <logger name="NHibernate.SQL">
    <level value="ALL" />
<appender-ref ref="rollingFile" />
</logger>
</log4net>

and configuring your application to use Log4Net (if you hadn't done that anyway):

 

log4net.Config.XmlConfigurator.Configure();

 

If you wan't to know more about log4net and it's configuration options - look here or use your favorite search engine.

2007 Nov 19

On Linq for SQL and POCO

tagged as: linq | activerecord | nhibernate

I'm looking at the option of using Linq To SQL for persistence.

 

Basic assumptions:

  1. Entities should be POCOs
  2. I like Linq as query language
  3. I hate visible generated code
  4. Would rather avoid xml configurations

Today I'm using NHibernate (so 1 and 3 are set), and AR Attributes (so 4 is set). As for querying, I resort to hql (nice, yet too stringy), ICriteria (still stringy) and NHQG (cool, super cool, yet coupled with NH, while Linq is a "query everything" language)

 

I tried Linq for SQL (on a VS C# 2008 Express Beta2). No designer. Hand coded the entity, and have used the attributes for mapping.

 

First problem encountered: in order to make a column lazy, I need to change the underlying type to Link<MyOriginalType>, and then I can tell the context (using a LoadingOptions) about whether to load the lazy properties.

 

Couldn't yet find a way to actually lazy load that property once the instance has already been loaded.

 

I much better like the way NH is handling things, with a runtime-generated proxy that takes care of lazy loading (among other stuff), so I get it without hassling my entities code.

 

Didn't even mention First and Second Level Caches.

 

I guess I'd have to try and hop into NHibernate.Linq, and try to help Ayende with bringing it forward. That would mean diving into NH code, something I haven't done for quite some time now ...

2007 Nov 18

ActiveRecord.Linq - naive but Working

tagged as: c# | linq | castle | activerecord | nhibernate

I've spent some times lately with Linq To SQL and have played a bit with the Mapping namespace.

Why I do not like it very much is a matter for a different post. The matter at hand is that I want the power of Linq, and I want the power of NHibernate, and I want the easy road of ActiveRecord.

 

What do I mean by that? I'd like:

  1. query language == Linq
  2. persistence engine == NHibernate
  3. Mapping == ActiveRecord attributes

the needed prequisites:

  1. Linq (framework => framework.Version >= 3.5)
  2. NHibernate.Linq (source => source.getFrom(Rhino-Tools) )
  3. Linq for ActiveRecord - Keep on Reading

So, Ayende has kick-started it, and with some help from Bobby Diaz, we have a prototype level NHibernate provider for Linq.

To make it work with ActiveRecord, all you need is to add:

using System;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using NHibernate;
namespace NHibernate.Linq
{
public class ActiveRecordContext : NHibernateContext
    {
        public ActiveRecordContext() : base(null)
        {
            session = GetSession();
        } 
        private ISession GetSession()
        {
            ISessionScope scope = SessionScope.Current;
            if (scope == null)
                throw new InvalidOperationException("You should be in a SessionScope()");
            ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionFactoryHolder();
            return holder.CreateSession(typeof(ActiveRecordBase));
        }
    }
}

and now you can do stuff like:

using (new SessionScope())
{
    ActiveRecordContext context = new ActiveRecordContext();
    var q =
            from c in context.Session.Linq<Category>()
            select c; 
    foreach (Category c in q)
        Console.WriteLine(c.Name);
}

Assuming Category has [ActiveRecord] mapping.

2007 Jul 28

A Sign That I'm Starting To Like NHQG Too Much

tagged as: tools | nhibernate

Today I noticed that piece of code on my working copy:

Repository.Blog.FindOne(Where.Blog.Id == blogId)
2007 Jun 20

Eagerly loading multiple collections in one roundtrip

tagged as: activerecord | nhibernate

Today at work I did a session with my team, showing them several methods to query the DB in a NH/AR enviroment.

When we talked about eager fetching, I said that doing it for more than one collection in one query isn't good (as advised in hibernate's site), since it might build up a rather large cartesian product.

Why? If we have a type Entity, that looks a bit like that:

class Entity
{
    ...
    [HasMany ...]
    public IList<Item> Items ...

    [HasMany ...]
    public IList<AnotherItem> AnotherItems ...
    ...
}

An eager fetch will do:

SELECT *
FROM
    Entities INNER JOIN
    Items ON Entities.Id = Items.EntityId INNER JOIN
    AnotherItems ON Entities.Id = AnotherItems.EntityId 

The result length will be e*i*a, where e is the number of rows in Entities table, i is the number of rows in Items table , and a .. (you fill the blank).

But, luckily for us, NH is keeping a single concrete copy of each entity in a session. Ayende is abusing that in a way, that makes the above possible, without the 3rd (or more) dimention in the cartesian product.

How it works? Using a MultiCriteria, he is getting his entities, eagerly fetching one collection at a time. So it would return e*i+e*a rows. So for the first query, NH will populate the Entity instances, with the first collection (Items) populated. For the second query, NH will populate *another* Entity instances with the second collection (AnotherItems) populated. But, since it's in the same session, actually the first Entity instances (with Items already populated) will get their AnotherItems collection populated.

That's why at the end of the snippet, you see that he actually deals with list[0]. That's where the first set of references is placed, and the other items in that list (list[1] ... list[n]) are just copies of the references to the same Entity objects.

Quite a similar approach was seen when Ayende has shown us how to eager fetch using SQL (at the end of the post). The query there returns an array of Message items and an array of User items, but the User instances actually are wired to their Message instances, so he is using only the first array.

2007 Mar 19

ActiveWriter - 2nd preview is out

ActiveWriter is a VS2005 plugin, that adds a new item type for your projects. This item is actually a visual designer for ActiveRecord classes. Quite neat, and hopefully will increase the penetration of Castle's ActiveRecord to the "If there's no VS wizard, I do not use it" kinda guys.

You can read about it and download it from http://altinoren.com/activewriter/

2007 Jan 4

NHibernate ' Could not find constructor for: ' in ' select new ' projection query

tagged as: castle | activerecord | nhibernate | hql

There are two facts here:
1. I love NHibernate.
2. I hate NHibernate's exception messages.

And here's my story.

On a project I'm working on, I need to show a projection of "top 10" from the database. let's show this on the good old Blog scenario: 

So I want to show the posts with the longest comments measured by the comment's length. Stupid, huh? but it's a demo only (I cannot expose the actual ERD). Let's say I want the top 5.

I am using Castle ActiveRecord. There is a Post and a Comment classes. However, I do not wish to load Posts objects, since It will load the Comments, too, and maybe other stuff that the Post class is related to. So I have defined a PostProjection class:

   1:  public class PostProjection
   2:  {
   3:      public string Title;
   4:      public int Length;
   5:      public PostProjection(string title, int length)
   6:      {
   7:          Title = title;
   8:          Length = length;
   9:      }
  10:  }

I have also added an [Import] attribute on the Post. The actual querying is done using the next hql :

   1:  public static PostProjection[] GetTopPosts(int postsToGet)
   2:  {
   3:      SimpleQuery<PostProjection> q =
   4:          new SimpleQuery<PostProjection>(typeof(Post), @"
   5:          select new PostProjection(p.Title, sum(c.LineCount)) 
   6:          from 
   7:              Post p inner join 
   8:              p.Comments c 
   9:          order by sum(o.LineCount) desc
  10:          group by p.Title");
  11:      q.SetQueryRange(postsToGet);
  12:      return q.Execute();
  13:  }

It worked great.

Yesterday I've upgraded my Castle dll's to the ones from build 229. It includes NHibernate 1.2.0.2002

Now the "select new" started to fail, and NHibernate started to claim than "Could not find constructor for: PostProjection".

I've been scratching my head, trying various approaches, and even was keen to skip the "new" and use an object[ ] and populate the Projection Array by hand, but then I tried changing the "length" parameter of the constructor from "int" to "long". Magically it solved the problem.

Now, if only NHibernate would have said :

Could not find constructor for: PostProjection.
Looking for: PostProjection(string, long)

I would've known what the problem was, and what should I change.

So what have we learned today?

1. NHibernate expects sum() to return "long" rather than "int"
2. NHibernate's error messages suck.

I've svn-ed the NHibernate trunk, added some code so this message would be more developer friendly, and I'm going to send the patch to NHibernate's JIRA.

Subscribe

Statistics

283
440

Related Books

Related Jobs

Related Ads

search page | Blog's home | About me