Archive

Archive for the ‘NHibernate’ Category

OrmLite Blobbing done with NHibernate and Serialized JSON…

March 19th, 2012 1 comment

There seems to be a growing trend now with these Micro ORM’s, at least that is what I see with ServiceStack.OrmLite, which is the ability to persist properties of an object as a JSON, rather than in separate tables.

Usually with a Relational approach you would create a ‘Customer’ table, ‘Address’ table, and most likely shove the Phone Numbers under separate columns of the customer for ‘HomePhone’ and ‘Mobile’.

This means we are limited to two types of phone numbers, and require joining or querying for the addresses.

Do we really need separate columns for phone numbers? Do we really need to persist the addresses in another table?

One problem I see with putting addresses into it’s own table, is the temptation to relate them to an Order (assuming this is some sort of eCommerce system) when really there is no relationship between a customer’s address, and the address on an order.

Really, the order should have it’s own address, otherwise you can never delete or update an address on a customer, and you can’t delete the customer. However I digress and this is a topic for another day.

Example by OrmLite

This post is about how to do it with NHibernate, but I’m going to start by showing the example in OrmLite, then use the same example for NHibernate.

Note: OrmLite by default persists as JSV-format (JSON+CSV) rather than JSON. I’m currently unaware of any way to change it to be JSON.

The customer is the root aggregate, and has his own Addresses and Phone Numbers, and is modelled like so:

public enum PhoneType
{
    Home,
    Work,
    Mobile,
}

public enum AddressType
{
    Home,
    Work,
    Other,
}

public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string ZipCode { get; set; }
    public string State { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
}

public class Customer
{
    public Customer()
    {
        this.PhoneNumbers = new Dictionary<PhoneType, string>();
        this.Addresses = new Dictionary<AddressType, Address>();
    }

    [AutoIncrement] // Creates Auto primary key
    public virtual int Id { get; set; }
               
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
               
    [Index(Unique = true)] // Creates Unique Index
    public virtual string Email { get; set; }
               
    public virtual Dictionary<PhoneType, string> PhoneNumbers { get; set; }  //Blobbed
    public virtual Dictionary<AddressType, Address> Addresses { get; set; }  //Blobbed
    public virtual DateTime CreatedAt { get; set; }
}

Note: The attributes are for OrmLite and are not used by NHibernate, and the properties have been made virtual for NHibernate.

So using OrmLite if we insert some data like so:

var customer = new Customer
{
    FirstName = "Phillip",
    LastName = "Haydon",
    Email = "test@test.com"
};

customer.Addresses.Add(AddressType.Home, new Address
{
    Line1 = "Unit 31",
    Line2 = "102 Banana Street",
    City = "Sydney",
    Country = "Australia",
    State = "NSW",
    ZipCode = "2009"
});

customer.PhoneNumbers.Add(PhoneType.Mobile, "+61 411 122 34");
customer.PhoneNumbers.Add(PhoneType.Home, "+61 256 3234");

cmd.Insert(customer);

We can query for that data and we get the following results:

image

(Click on the image to see it fully)

As you can see PhoneNumbers are stored like so:

{Mobile:+61 411 122 34,Home:+61 256 3234}

And Addresses are stored like:

{Home:{Line1:Unit 31,Line2:102 Banana Street,ZipCode:2009,State:NSW,City:Sydney,Country:Australia}}

Now if we query for that data back out:

var customer = cmd.QuerySingle<Customer>(1);

image

You can see we get all the information back out again, no problem! This stuff is built into OrmLite which is awesome, but how do we do it in NHibernate?

Custom NHibernate UserType

So now we want to do this in NHibernate. This is a UserType I wrote a long time ago, well… I re-wrote it recently but wrote the initial idea a long time ago, and I’ve personally never seen anything similar in NHibernate.

I’ve put this on Gist – https://gist.github.com/1936188

[Serializable]
public class Blobbed<T> : IUserType where T : class
{
    public new bool Equals(object x, object y)
    {
        if (x == null && y == null)
            return true;

        if (x == null || y == null)
            return false;

        var xdocX = JsonConvert.SerializeObject(x);
        var xdocY = JsonConvert.SerializeObject(y);

        return xdocY == xdocX;
    }

    public int GetHashCode(object x)
    {
        if (x == null)
            return 0;

        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        if (names.Length != 1)
            throw new InvalidOperationException("Only expecting one column…");

        var val = rs[names[0]] as string;

        if (val != null && !string.IsNullOrWhiteSpace(val))
        {
            return JsonConvert.DeserializeObject<T>(val);
        }

        return null;
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var parameter = (DbParameter)cmd.Parameters[index];

        if (value == null)
        {
            parameter.Value = DBNull.Value;
        }
        else
        {
            parameter.Value = JsonConvert.SerializeObject(value);
        }
    }

    public object DeepCopy(object value)
    {
        if (value == null)
            return null;

        //Serialized and Deserialized using json.net so that I don't
        //have to mark the class as serializable. Most likely slower
        //but only done for convenience.

        var serialized = JsonConvert.SerializeObject(value);

        return JsonConvert.DeserializeObject<T>(serialized);
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    public object Assemble(object cached, object owner)
    {
        var str = cached as string;

        if (string.IsNullOrWhiteSpace(str))
            return null;

        return JsonConvert.DeserializeObject<T>(str);
    }

    public object Disassemble(object value)
    {
        if (value == null)
            return null;

        return JsonConvert.SerializeObject(value);
    }

    public SqlType[] SqlTypes
    {
        get
        {
            return new SqlType[] { new StringSqlType() };
        }
    }

    public Type ReturnedType
    {
        get { return typeof(T); }
    }

    public bool IsMutable
    {
        get { return true; }
    }
}

It’s a generic class so that I can return the type of object back, and uses json.net to handle the serialization/deserialization of the object to JSON.

Now when mapping the properties we can specify the CustomType like so:

Map(x => x.Addresses, "Addresses").CustomType<Blobbed<Dictionary<AddressType, Address>>>();
Map(x => x.PhoneNumbers, "PhoneNumbers").CustomType<Blobbed<Dictionary<PhoneType, string>>>();

The example I’m using has two dictionaries of values. But if you were mapping a single type such as a single ‘Address’, you would just specify the type as above, without the Dictionary, .CustomType<Blobbed<Address>>()

The full mapping for the Customer is:

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Table("Customer");

        Id(x => x.Id, "Id").GeneratedBy.Identity();

        Map(x => x.FirstName, "FirstName");
        Map(x => x.LastName, "LastName");
        Map(x => x.Email, "Email");
        Map(x => x.CreatedAt, "CreatedAt");

        Map(x => x.Addresses, "Addresses").CustomType<Blobbed<Dictionary<AddressType, Address>>>();
        Map(x => x.PhoneNumbers, "PhoneNumbers").CustomType<Blobbed<Dictionary<PhoneType, string>>>();
    }
}

Now we can insert some data:

using (var tx = session.BeginTransaction())
{
    var customer = new Customer
    {
        FirstName = "Prentice",
        LastName = "Porter",
        Email = "banana3@test.com"
    };

    customer.Addresses.Add(AddressType.Home, new Address
    {
        Line1 = "13/187 Jones St",
        City = "Auckland",
        Country = "New Zealand",
        ZipCode = "0629"
    });

    customer.PhoneNumbers.Add(PhoneType.Mobile, "+64 27 551 443");
    customer.PhoneNumbers.Add(PhoneType.Home, "+64 9445 1982");

    session.SaveOrUpdate(customer);

    tx.Commit();
}

Again, the data is inserted:

image

(Click on the image to see it fully)

Only this data is serialized as JSON rather than JSV-format.

PhoneNumbers:

{"Mobile":"+64 27 551 443","Home":"+64 9445 1982"}

And Addresses:

{"Home":{"Line1":"13/187 Jones St","Line2":null,"ZipCode":"0629","State":null,"City":"Auckland","Country":"New Zealand"}}

If we query for the data:

var customer = session.Get<Customer>(2);

image

Just like OrmLite we get the object back just the same.

Things to note:

The custom user type in it’s current state does not handle inherited objects. If you want to support it, then you can modify it to serialize and deserialize using the type information.

This can be done like so:

JsonConvert.SerializeObject(x,
    Formatting.None,
    new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });

And

JsonConvert.DeserializeObject<T>(val,
    new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });

What this will do is include the type information on the serialized object:

{"$type":"System.Collections.Generic.Dictionary2[[NHibernateJsonTest.AddressType, NHibernateJsonTest],[NHibernateJsonTest.Address, NHibernateJsonTest]], mscorlib","Home":{"$type":"NHibernateJsonTest.Address, NHibernateJsonTest","Line1":"13/187 Jones St","Line2":null,"ZipCode":"0629","State":null,"City":"Auckland","Country":"New Zealand"}}

Why would you want to do this

To avoid unnecessary tables and mappings. The example above is a perfect example where we can remove the need for a table on data that is never searched against and is never related to anything else.

There’s no need for joins or adding additional columns. We just map the object or collection to a single column and we are done, and our code knows no different.

Also, schema changes in blobs don’t need DDL updates. If your model changes, you add new properties, or remove old properties, the blob will get updated next time you update your data. No more scripting off schema changes.

NHibernate Designer 2

October 20th, 2011 No comments

The guys at MindScape have released their next version of NHibernate Designer, which now supports Fluent NHibernate and a whole heap of new features.

They also dropped the price down to just $99!

I recommend checking it out

http://www.mindscapehq.com/blog/index.php/2011/10/19/nhibernate-designer-2-is-here/

http://www.mindscapehq.com/products/nhdesigner

Categories: Fluent NHibernate, NHibernate Tags:

The benefits of letting the ORM generate the Identity (part 1)

September 22nd, 2011 No comments

One thing I’ve learnt is that letting the database generate the identity for you is a bad thing. It always annoyed me that Oracle never gave a feature like AUTO_INCREMENT in MySQL or IDENTITY in SQL Server. I never understood, when inserting data how do I give it an Id?

Well one of the benefits of ORMs such as NHibernate is we can generate the identity our-self, or rather, the ORM can generate it so we don’t rely on the database. This also plays a major part in our code base when we insert a graph or batch of data and how the identity is added to our object.

Ayende recommend avoiding identity.

The thing with using SQL Server”s identity is that we need to select the identity back out after we do an insert. Not only that, when using NEWID()/NEWSEQUENTIALID() there is no way to select the value back other than using all the fields in a select in order to get the GUID relates to the record with all those values matching.

For example:

Given this rather simple table using IDENTITY.

CREATE TABLE People
(
    Id int NOT NULL IDENTITY (1, 1) PRIMARY KEY,
    FirstName nvarchar(100) NOT NULL,
    Surname nvarchar(100) NOT NULL
)

And I’ll demo with NEWID() as well.

CREATE TABLE Fruit
(
    Id uniqueidentifier NOT NULL PRIMARY KEY DEFAULT (NEWID()),
    Name nvarchar(100) NOT NULL
)

Note: I’m unaware of anyway to get NHibernate to use NEWSEQUENTIALID()

We map these in NHibernate like so:

public class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
        Table("People");
        Id(x => x.Id).GeneratedBy.Identity();

        Map(x => x.FirstName);
        Map(x => x.Surname);
    }
}

public class FruitMap : ClassMap<Fruit>
{
    public FruitMap()
    {
        Table("Fruit");
        Id(x => x.Id).GeneratedBy.GuidNative();

        Map(x => x.Name);
    }
}

If we insert data into People and Fruit like so:

using (var tx = session.BeginTransaction())
{
    var person = new Person
                        {
                            FirstName = "Phillip",
                            Surname = "Haydon"
                        };

    var fruit = new Fruit
                    {
                        Name = "Apple"
                    };

    session.SaveOrUpdate(person);
    session.SaveOrUpdate(fruit);

    tx.Commit();
}

We get the following statement’s run:

– statement #1
begin transaction with isolation level: Unspecified

– statement #2
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

– statement #3
select newid()

– statement #4
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple' /* @p0 */,
             '3411c820-f9cc-4385-97a1-31cf7e7c612c' /* @p1 */)

– statement #5
commit transaction

What’s interesting is for the Identity, we have to select the SCOPE_IDENTITY() back after the insert so that we can populate the Person object, and for the Fruit object, we have to select NEWID() first as a separate statement, then add it to the business object, and commit it.

This round-trip to the database in order to get the GUID first before doing the insert is completely unnecessary, not to mention has a performance impact.

Inserting 50,000 items for each, with the batch-size set to 50, yields the following:

IDENTITY 28951
NEWID 30241

(value in milliseconds)

Note: These benchmarks are quick-nasty benchmarks and were only run once.

The interesting thing is neither IDENTITY or NEWID batched any of the insert statements together, they were all issued as separate statements.

(re-run the test inserting 3 to show SQL output)

The ‘Person’ insert looks like this:

– statement #1
begin transaction with isolation level: Unspecified

– statement #2
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip0' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

– statement #3
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip1' /* @p0 */,
             'Haydon' /* @p1 */);

select SCOPE_IDENTITY()

– statement #4
INSERT INTO People
            (FirstName,
             Surname)
VALUES      ('Phillip2' /* @p0 */,
             'Haydon' /* @p1 */);
             
select SCOPE_IDENTITY()

– statement #5
commit transaction

Each insert has to be done 1 by 1, since it needs to select the identity back.

The ‘Fruit’ table on the other hand:

– statement #1
begin transaction with isolation level: Unspecified

– statement #2
select newid()

– statement #3
select newid()

– statement #4
select newid()

– statement #5
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple0' /* @p0_0 */,
             '269bc638-74b4-4568-85d1-45b6e537fcbd' /* @p1_0 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple1' /* @p0_1 */,
             'fc848779-b173-4c31-b8b6-0a7735c0c2dc' /* @p1_1 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple2' /* @p0_2 */,
             '232c8971-18c7-486d-9152-26c969c3b632' /* @p1_2 */)

– statement #6
commit transaction

It select 50,000 GUIDs first, then it issues all the insert statement’s in batches of 50.

Now lets look at HiLo and GuidComb (GuidComb is a Sequencial Guid, but NH also allows normal Guids), two ways of generating identities in the ORM rather than the database.

The tables are the same as before, except they don’t have ‘IDENTITY’ or a Default Value.

The mappings have been updated to:

Id(x => x.Id).GeneratedBy.HiLo("100");

And

Id(x => x.Id).GeneratedBy.GuidComb();

Running a single insert for both results in:

– statement #1
begin transaction with isolation level: Unspecified

– statement #2
Reading high value:
select next_hi
from   hibernate_unique_key with (updlock, rowlock)

– statement #3
Updating high value:
update hibernate_unique_key
set    next_hi = 2 /* @p0 */
where  next_hi = 1 /* @p1 */

– statement #4
INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip' /* @p0_0 */,
             'Haydon' /* @p1_0 */,
             101 /* @p2_0 */)

– statement #5
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple' /* @p0_0 */,
             '3229618e-bd8a-45ae-8ad5-9f660016980d' /* @p1_0 */)

– statement #6
commit transaction

Besides NHibernate getting the first Hi value for use in the HiLo algorithm, both insert statement did not require selecting or generating any identity, it was all done in NHibernate.

This makes inserting 3 ‘Person’ much more efficient:

– statement #1
begin transaction with isolation level: Unspecified

– statement #2
Reading high value:
select next_hi
from   hibernate_unique_key with (updlock, rowlock)

– statement #3
Updating high value:
update hibernate_unique_key
set    next_hi = 3 /* @p0 */
where  next_hi = 2 /* @p1 */

– statement #4
INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip0' /* @p0_0 */,
             'Haydon' /* @p1_0 */,
             202 /* @p2_0 */)

INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip1' /* @p0_1 */,
             'Haydon' /* @p1_1 */,
             203 /* @p2_1 */)

INSERT INTO People
            (FirstName,
             Surname,
             Id)
VALUES      ('Phillip2' /* @p0_2 */,
             'Haydon' /* @p1_2 */,
             204 /* @p2_2 */)

– statement #5
commit transaction

3 insert’s done as a single batch statement.

And inserting 3 Fruit:

– statement #1
begin transaction with isolation level: Unspecified

– statement #2
INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple0' /* @p0_0 */,
             'db902160-edbb-49c7-bf52-9f660018299a' /* @p1_0 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple1' /* @p0_1 */,
             '5e852528-3a6f-41d2-a6b1-9f660018299a' /* @p1_1 */)

INSERT INTO Fruit
            (Name,
             Id)
VALUES      ('Apple2' /* @p0_2 */,
             '2f63c6e8-e595-4393-ad15-9f660018299a' /* @p1_2 */)

– statement #3
commit transaction

Again, 3 insert’s done as a single batch statement.

If we run 50,000 inserts again:

HiLo 9287
GuidComb 9060

That is over 3 times faster!

So as you can see, for just doing batch inserting, with a full Session (rather than stateless) and allowing the ORM to generate identities, we can significantly improve performance.

Fluent NHibernate – Table Inheritance – Discriminators (Part 2)

August 22nd, 2011 No comments

This is part two, to my post about Table Inheritance using Discriminators, in this post I just want to demonstrate the outcome when the sub-classes have their own properties, or possibly a property that maps to the same column.

First thing however is Mark Perry pointed out in the comments that specifying a value for the baseClassDiscriminator will force it to store the value in the database as an INT rather than a a VARCHAR.

DiscriminateSubClassesOnColumn("PostType", 0);

This will create the table with an INT like so:

create table WallPost (
  Id         UNIQUEIDENTIFIER   not null,
  PostType   INT   not null,
  DatePosted DATETIME   null,
  Title      NVARCHAR(255)   null,
  Content    NVARCHAR(255)   null,
    primary key ( Id ))

Maybe INT is too big however, maybe we only want a SMALLINT? That will give us 32k sub-classes…

DiscriminateSubClassesOnColumn("PostType", (short)0);

PostType   SMALLINT   not null,

But even that is too many, so maybe we need TINYINT, that gives us 0-255. I doubt you would ever have 255 sub-classes, so we can specify the discriminator as a byte.

DiscriminateSubClassesOnColumn("PostType", (byte)0);

And that gives us:

create table WallPost (
  Id         UNIQUEIDENTIFIER   not null,
  PostType   TINYINT   not null,
  DatePosted DATETIME   null,
  Title      NVARCHAR(255)   null,
  Content    NVARCHAR(255)   null,
    primary key ( Id ))

Nice, much better.

Not to mention when querying now, it uses the INT value rather than the number being used as a string like before:

SELECT this_.Id         as Id0_0_,
       this_.DatePosted as DatePosted0_0_,
       this_.Title      as Title0_0_,
       this_.Content    as Content0_0_
FROM   WallPost this_
WHERE  this_.PostType = 1

So the next thing I want to show is what happens when we add a property to 1 sub-class and not the other. (or properties than exist in 1, and properties that exist in the other)

Give the same example as my previous post, I’ve added one property to the class, a ‘Url’ property.

image

This will get created as a normal column in the database.

create table WallPost (
  Id         UNIQUEIDENTIFIER   not null,
  PostType   TINYINT   not null,
  DatePosted DATETIME   null,
  Title      NVARCHAR(255)   null,
  Content    NVARCHAR(255)   null,
  Url        NVARCHAR(255)   null,
    primary key ( Id ))

When we insert now, a LinkShare and a Text wallpost:

var wallPost = new TextWallPost
{
    DatePosted = DateTime.Now,
    Title = "My First Wall Post",
    Content = "Is Awesome!"
};
var linkPost = new LinkShareWallPost()
{
    DatePosted = DateTime.Now,
    Title = "My First Link Share",
    Content = "Is Awesome!",
    Url = "http://www.philliphaydon.com/"
};

session.Save(wallPost);
session.Save(linkPost);

The link share one will include the Url.

– statement #1
INSERT INTO WallPost
           (DatePosted,
            Title,
            Content,
            PostType,
            Id)
VALUES     ('2011-08-21T23:53:10.00' /* @p0 */,
            'My First Wall Post' /* @p1 */,
            'Is Awesome!' /* @p2 */,
            1,
            '2dc7981b-507b-4d36-8ecc-9f460189a27d' /* @p3 */)

– statement #2
INSERT INTO WallPost
           (DatePosted,
            Title,
            Content,
            Url,
            PostType,
            Id)
VALUES     ('2011-08-21T23:53:10.00' /* @p0 */,
            'My First Link Share' /* @p1 */,
            'Is Awesome!' /* @p2 */,
            'http://www.philliphaydon.com/' /* @p3 */,
            2,
            '5edfe0f2-e179-4615-88e4-9f460189a284' /* @p4 */)

The ‘Url’ column must be null, or have a default value assigned to it so that when inserting a Text wallpost, the column doesn’t need to be specified.

If we query for the base class:

var result = session.QueryOver<WallPost>().List();

This will query for all columns:

SELECT this_.Id         as Id0_0_,
       this_.DatePosted as DatePosted0_0_,
       this_.Title      as Title0_0_,
       this_.Content    as Content0_0_,
       this_.Url        as Url0_0_,
       this_.PostType   as PostType0_0_
FROM   WallPost this_

Just like it did before. No changes, likewise if we query for just the Text wallpost, it will not include the ‘Url’ column:

var result = session.QueryOver<TextWallPost>().List();

Results in:

SELECT this_.Id         as Id0_0_,
       this_.DatePosted as DatePosted0_0_,
       this_.Title      as Title0_0_,
       this_.Content    as Content0_0_
FROM   WallPost this_
WHERE  this_.PostType = 1

If we query for the LinkShare wall post:

var result = session.QueryOver<LinkShareWallPost>().List();

This results in the ‘Url’ column being selected:

SELECT this_.Id         as Id0_0_,
       this_.DatePosted as DatePosted0_0_,
       this_.Title      as Title0_0_,
       this_.Content    as Content0_0_,
       this_.Url        as Url0_0_
FROM   WallPost this_
WHERE  this_.PostType = 2

So NHibernate is efficient in that it only queries for what it actually needs. If you extend your sub-classes out to have a couple of properties each then they will only query for the required fields for that sub-class.

It is possible for sub-classes to share properties. For example if introduced a new sub-class, MovieShare, which has a VideoUrl, as well as a SiteUrl property:

image

We can map the classes like so:

public class TextWallPostMap : SubclassMap<TextWallPost>
{
    public TextWallPostMap()
    {
        DiscriminatorValue(1);
    }
}

public class LinkShareWallPostMap : SubclassMap<LinkShareWallPost>
{
    public LinkShareWallPostMap()
    {
        DiscriminatorValue(2);

        Map(x => x.Url).Column("Url");
    }
}

public class MovieShareWallPostMap : SubclassMap<MovieShareWallPost>
{
    public MovieShareWallPostMap()
    {
        DiscriminatorValue(3);

        Map(x => x.SiteUrl).Column("Url");
        Map(x => x.VideoUrl).Column("VideoUrl");
    }
}

When the table is created, ‘Url’ column is only created once:

create table WallPost (
  Id         UNIQUEIDENTIFIER   not null,
  PostType   TINYINT   not null,
  DatePosted DATETIME   null,
  Title      NVARCHAR(255)   null,
  Content    NVARCHAR(255)   null,
  Url        NVARCHAR(255)   null,
  VideoUrl   NVARCHAR(255)   null,
    primary key ( Id ))

Now when we insert:

var wallPost = new TextWallPost
{
    DatePosted = DateTime.Now,
    Title = "My First Wall Post",
    Content = "Is Awesome!"
};
var linkPost = new LinkShareWallPost()
{
    DatePosted = DateTime.Now,
    Title = "My First Link Share",
    Content = "Is Awesome!",
    Url = "http://www.philliphaydon.com/"
};
var moviePost = new MovieShareWallPost()
{
    DatePosted = DateTime.Now,
    Title = "My First Movie Share",
    Content = "Is Awesome!",
    SiteUrl = "http://www.philliphaydon.com/",
    VideoUrl = "http://www.youtube.com/watch?v=GaoLU6zKaws"
};

session.Save(wallPost);
session.Save(linkPost);
session.Save(moviePost);

The insert will share the same ‘Url’ column for both the LinkShare, and the MovieShare:

– statement #1
INSERT INTO WallPost
           (DatePosted,
            Title,
            Content,
            PostType,
            Id)
VALUES     ('2011-08-22T00:22:05.00' /* @p0 */,
            'My First Wall Post' /* @p1 */,
            'Is Awesome!' /* @p2 */,
            1,
            '0e9cef50-d609-4a62-8909-9f47000611cb' /* @p3 */)

– statement #2
INSERT INTO WallPost
           (DatePosted,
            Title,
            Content,
            Url,
            PostType,
            Id)
VALUES     ('2011-08-22T00:22:05.00' /* @p0 */,
            'My First Link Share' /* @p1 */,
            'Is Awesome!' /* @p2 */,
            'http://www.philliphaydon.com/' /* @p3 */,
            2,
            '8deb343e-941f-4ae1-aba7-9f47000611d0' /* @p4 */)

– statement #3
INSERT INTO WallPost
           (DatePosted,
            Title,
            Content,
            Url,
            VideoUrl,
            PostType,
            Id)
VALUES     ('2011-08-22T00:22:05.00' /* @p0 */,
            'My First Movie Share' /* @p1 */,
            'Is Awesome!' /* @p2 */,
            'http://www.philliphaydon.com/' /* @p3 */,
            'http://www.youtube.com/watch?v=GaoLU6zKaws' /* @p4 */,
            3,
            '6bc80750-3fb2-4830-bccf-9f47000611d0' /* @p5 */)

And querying is still as it was before. No changes.

One really important thing to remember is. You cannot change a type from 1 type to another, meaning you cannot change a LinkShare to a MovieShare. Any sub-class you create should never have any reason to change, if for some reason it DOES change, you should delete it, and create a new one.

By that I mean delete the object, and insert a new one of the specified sub-class. While it is possible to use native SQL to change the discriminator value, there’s no way to do it in HQL, Criteria, LINQ, or QueryOver, because it’s just wrong. If it needs to change, you probably need to re-think your domain and persistence.

Next post will be about Table per Sub-Class mapping.

NHibernate Work-Around is not really a Work-Around…

August 15th, 2011 2 comments

Over the weekend I came across a blog post about NHibernate, and an apparent work-around for a feature not supported by NHibernate.

The original post can be found here: nHibernate LINQ workaround for System.NotSupportedException

Basically the author wanted to write something along the lines of:

var fruitIds = new List<int> { 5, 8, 13 };

using (var session = factory.OpenSession())
{
    var result = from f in session.Query<NHFruit>()
                 join i in fruitIds
                     on f.Id equals i
                 select f;

    foreach (var fruit in result)
        Console.WriteLine(fruit.Name);
}

Where the query joins to a list of Ids to filter the results out. This however, happens to throw an exception:

image

Normally this sort of query would be written like so:

var fruitIds = new List<int> { 5, 8, 13 };

using (var session = factory.OpenSession())
{
    var result = from f in session.Query<NHFruit>()
                 where fruitIds.Contains(f.Id)
                 select f;

    foreach (var fruit in result)
        Console.WriteLine(fruit.Name);
}

This generates an ‘in’ clause in the SQL.

select nhfruit0_.Id   as Id0_,
       nhfruit0_.Name as Name0_
from   Fruit nhfruit0_
where  nhfruit0_.Id in (5 /* @p0 */,8 /* @p1 */,13 /* @p2 */)

Nice and clean, pretty standard SQL.

Now I must admit when I first read his code I made the assumption that it would actually pull ALL results into memory, and do the join in memory.

I commented on his blog with the following comment:

I don”t see how this is a ”work-around” your first attempt at the query is attempting to join something in memory, to something still in the database. Thats clearly not, and will never be a supported feature of NHibernate… The second query is passing a list of ID”s into the database to use for comparison.

At the time of writing the comment, I was a bit annoyed. The author replied:

Never be supported in NHi”bernate… well it works using Entity Framework and that”s why I consider it to be a workaround.

So lets give it a go.

Give the following query:

var fruitIds = new List<int> { 5, 8, 13 };

using (var ctx = new EFTestEntities())
{
    var result = from f in ctx.Fruits
                 join i in fruitIds
                     on f.Id equals i
                 select f;

    foreach (var fruit in result)
        Console.WriteLine(fruit.Name);
}

The exact same as the attempt in NHibernate, it generates the following SQL…

SELECT [Extent1].[Id]   AS [Id],
       [Extent1].[Name] AS [Name]
FROM   [dbo].[Fruit] AS [Extent1]
       INNER JOIN (SELECT [UnionAll1].[C1] AS [C1]
                   FROM   (SELECT 5 AS [C1]
                           FROM   (SELECT 1 AS X) AS [SingleRowTable1]
                           UNION ALL

                           SELECT 8 AS [C1]
                           FROM   (SELECT 1 AS X) AS [SingleRowTable2]) AS [UnionAll1]
                   UNION ALL

                   SELECT 13 AS [C1]
                   FROM   (SELECT 1 AS X) AS [SingleRowTable3]) AS [UnionAll2]
         ON [Extent1].[Id] = [UnionAll2].[C1]

WTF.

image

It creates a select statement, for each value that you want to select… I thought “oh ok, i wonder what the execution plan looks like for that, compared to a normal in-clause from NHibernate.

Using both the queries already shown above, showing the actual execution plans, I get the following:

image

Wow… But it gets better, I have 20 records in my database, and I thought, what happens if my list of Ids contains 10 different ids:

Same queries, but with:

var fruitIds = new List<int> { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };

Entity Framework (I don’t even want to paste this in here…):

SELECT [Extent1].[Id]   AS [Id],
       [Extent1].[Name] AS [Name]
FROM   [dbo].[Fruit] AS [Extent1]
       INNER JOIN (SELECT [UnionAll8].[C1] AS [C1]
                   FROM   (SELECT [UnionAll7].[C1] AS [C1]
                           FROM   (SELECT [UnionAll6].[C1] AS [C1]
                                   FROM   (SELECT [UnionAll5].[C1] AS [C1]
                                           FROM   (SELECT [UnionAll4].[C1] AS [C1]
                                                   FROM   (SELECT [UnionAll3].[C1] AS [C1]
                                                           FROM   (SELECT [UnionAll2].[C1] AS [C1]
                                                                   FROM   (SELECT [UnionAll1].[C1] AS [C1]
                                                                           FROM   (SELECT 2 AS [C1]
                                                                                   FROM   (SELECT 1 AS X) AS [SingleRowTable1]
                                                                                   UNION ALL

                                                                                   SELECT 4 AS [C1]
                                                                                   FROM   (SELECT 1 AS X) AS [SingleRowTable2]) AS [UnionAll1]
                                                                           UNION ALL

                                                                           SELECT 6 AS [C1]
                                                                           FROM   (SELECT 1 AS X) AS [SingleRowTable3]) AS [UnionAll2]
                                                                   UNION ALL

                                                                   SELECT 8 AS [C1]
                                                                   FROM   (SELECT 1 AS X) AS [SingleRowTable4]) AS [UnionAll3]
                                                           UNION ALL

                                                           SELECT 10 AS [C1]
                                                           FROM   (SELECT 1 AS X) AS [SingleRowTable5]) AS [UnionAll4]
                                                   UNION ALL

                                                   SELECT 12 AS [C1]
                                                   FROM   (SELECT 1 AS X) AS [SingleRowTable6]) AS [UnionAll5]
                                           UNION ALL

                                           SELECT 14 AS [C1]
                                           FROM   (SELECT 1 AS X) AS [SingleRowTable7]) AS [UnionAll6]
                                   UNION ALL

                                   SELECT 16 AS [C1]
                                   FROM   (SELECT 1 AS X) AS [SingleRowTable8]) AS [UnionAll7]
                           UNION ALL

                           SELECT 18 AS [C1]
                           FROM   (SELECT 1 AS X) AS [SingleRowTable9]) AS [UnionAll8]
                   UNION ALL

                   SELECT 20 AS [C1]
                   FROM   (SELECT 1 AS X) AS [SingleRowTable10]) AS [UnionAll9]
         ON [Extent1].[Id] = [UnionAll9].[C1]

NHibernate:

select nhfruit0_.Id   as Id0_,
       nhfruit0_.Name as Name0_
from   Fruit nhfruit0_
where  nhfruit0_.Id in (2 /* @p0 */,4 /* @p1 */,6 /* @p2 */,8 /* @p3 */,
                        10 /* @p4 */,12 /* @p5 */,14 /* @p6 */,16 /* @p7 */,
                        18 /* @p8 */,20 /* @p9 */)

Now if you run with the execution plans, (I pressed execute 10 times before taking this screenshot)

image

The more parameters you add, the slower it gets.

You can’t blame Entity Framework though. EF supports the same query as NHibernate!

var fruitIds = new List<int> { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };

using (var ctx = new EFTestEntities())
{
    var result = from f in ctx.Fruits
                 where fruitIds.Contains(f.Id)
                 select f;

    foreach (var fruit in result)
        Console.WriteLine(fruit.Name);
}

Entity Framework generates the following query:

SELECT [Extent1].[Id]   AS [Id],
       [Extent1].[Name] AS [Name]
FROM   [dbo].[Fruit] AS [Extent1]
WHERE  [Extent1].[Id] IN (2,4,6,8,
                          10,12,14,16,
                          18,20)

So I personally think the original approach, to do a join on an in-memory list, is bad practice, regardless of weather it works or not. It’s bad.

It’s also bad to blindly write LINQ queries without ever looking at the SQL they produce. Sure it’s great to not have to worry about writing SQL, but you should still always be aware of the SQL that is been run, sometimes slightly changing the LINQ can massively alter the SQL that is produced.

Fluent NHibernate – Table Inheritance – Discriminators

August 8th, 2011 3 comments

So a long time ago James Kovacs posted a article about get/load polymorphism with NHibernate, which was cool and all but I always wanted to know how to map it all in Fluent NHibernate. I worked it out at the time but I guess it’s taken me 7 months to write it down.

First up is using a single table, mapping them to multiple classes, this is done using a discriminator. Fluent NHibernate calls this “table-per-class-hierarchy strategy”, which doesn’t make sense to me. But meh.

So I’m going to begin with the following classes to demonstrate this:

image

So if I was to select all WallPost’s it would give me instances of LinkShare, and Text wall posts.

These classes are really basic at the moment.

public class WallPost
{
    public virtual Guid Id { get; set; }
    public virtual DateTime DatePosted { get; set; }
    public virtual string Title { get; set; }
    public virtual string Content { get; set; }
}

public class TextWallPost : WallPost
{
}

public class LinkShareWallPost : WallPost
{
}

First we map the Wall Post:

public class WallPostMap : ClassMap<WallPost>
{
    public WallPostMap()
    {
        Table("WallPost");

        Id(x => x.Id).GeneratedBy.GuidComb();

        DiscriminateSubClassesOnColumn("PostType");

        Map(x => x.DatePosted);
        Map(x => x.Title);
        Map(x => x.Content);
    }
}

As you see, there is a ‘DescriminateSubClassesOnColumn’ method which specifies a column. This is what NHibernate uses to figure out which type of Sub Class to create.

Next we need to map the Sub Classes.

public class TextWallPostMap : SubclassMap<TextWallPost>
{
    public TextWallPostMap()
    {
        DiscriminatorValue(1);
    }
}

public class LinkShareWallPostMap : SubclassMap<LinkShareWallPost>
{
    public LinkShareWallPostMap()
    {
        DiscriminatorValue(2);
    }
}

Here I have specified the Discriminator Value for each sub class. If I save a TextWallPost, it will save the value ‘1’ to the column ‘PostType’. Then when it pulls it from the database, it uses this value to decide the SubClass to create.

The generated table looks like:

image

Now if I insert a couple of posts:

using (var tx = session.BeginTransaction())
{
    var wallPost = new TextWallPost
    {
        DatePosted = DateTime.Now,
        Title = "My First Wall Post",
        Content = "Is Awesome!"
    };
    var linkPost = new LinkShareWallPost()
    {
        DatePosted = DateTime.Now,
        Title = "My First Link Share",
        Content = "Is Awesome!"
    };

    session.Save(wallPost);
    session.Save(linkPost);

    tx.Commit();
}

I’ve done nothing more than create instances of the classes I want, and commit them to the database, the SQL that is generated looks like:

image

So when it generates the SQL it puts the discriminator value in for us.

If I select all ‘WallPost’ from the database:

var result = session.QueryOver<WallPost>().List();

foreach (var wallPost in result)
{
    Console.WriteLine(wallPost.Title);
}

It just does a normal select:

image

Nice right? What’s cool is if we look at the list of results we get, we get instances of WallPost, and instances of LinkShareWallPost back:

image

Now suppose we wanted to select JUST Link Shares.

var result = session.QueryOver<LinkShareWallPost>().List();

foreach (var wallPost in result)
{
    Console.WriteLine(wallPost.Title);
}

NHibernate will actually write the query to select only WallPosts that specify the Discriminator type defined in our Sub Class mapping.

image

So if we look at the results in VS:

image

Very nice stuff indeed.

There’s future posts on this stuff to come Smile

NHibernate Querying for Max value

May 17th, 2011 Comments off

Browsing the net today for something to do with Fluent NHibernate I came across a blog post.

http://frankmao.com/2011/01/14/nhibernate-subquery/

The blog post is to do with Subquery, but I got a little bit confused since the post itself doesn’t have anything to do with Subqueries.

About the actual post tho, NHibernate.Linq does actually support Min/Max operators.

Infact I just wrote a quick test to see the SQL it generated, the following code:

var result = session.Linq<TestProduct>().Max(x => x.Value);

 

Generates the following SQL.

SELECT max(this_.Value) as y0_
FROM   [TestProduct] this_

 

NHibernate.Linq has basically been deprecated however since NH3.0 has it’s own built in Linq provider, rewriting that query in NH3.0 would look like:

var result = session.Query<BaseClass>().Max(x => x.Id);

Categories: NHibernate Tags: , ,

NHibernate – Querying relationships at depth!

April 13th, 2011 Comments off

The title may be a bit leading, this isn’t about querying one-to-many-to-many, that is simply not possible. Well it may be possible, but it’s not practical by any means.

It’s not practical because when querying for one-to-many you end up with a cartesian product. With a one-to-many it’s easy to transform the data knowing the root is a single result while the relationship is a collection of all the results.

If you have a many-to-many it works too since it’s the distinct root and the relating collection of all results for a single root item.

But to select a one-to-many-to-many it’s far too complicated to work out what the hell is going on.  Well in my opinion it’s far too complicated.

However if you have a one-to-many-to-one, so three levels deep… Lets say… A blog, with many posts, each with an author.

image

We can query the 3rd level with the posts relatively easily.

If we take a relatively simple query, to fetch a blog with posts and display the title/author like so:

var sessionFactory = new SessionFactoryManager().CreateSessionFactory();
Blog blog = null;

using (var session = sessionFactory.OpenSession())
{
    blog = session.QueryOver<Blog>()
                  .Where(x => x.Id == 1)
                  .Fetch(x => x.Posts).Eager
                  .SingleOrDefault();

    Console.WriteLine(blog.Name);
    Console.WriteLine("—-");

    foreach (var post in blog.Posts)
    {
        Console.WriteLine("Title: " + post.Title);
        Console.WriteLine("Author: " + post.Author.FirstName);
        Console.WriteLine("—-");
    }
}

We can eager load the posts…

image

No problems. Except when we grab the Author, we end up with a select+n scenario…

image

This is bad bad bad! So the idea would be to eager load the Author with the post, the problem is .Fetch() doesn’t allow you to specify a property of a collection…

That’s where ‘JoinAlias’ comes in handy, I assume this stuff comes from HQL/Criteria, but I prefer not to use that stuff.

Instead of using ‘Fetch’ we are going to replace it with a ‘JoinAlias’ like so:

    Post posts = null;
    Author author = null;

    blog = session.QueryOver<Blog>()
                  .Where(x => x.Id == 1)
                  .JoinAlias(x => x.Posts, () => posts)
                  .JoinAlias(() => posts.Author, () => author)
                  .SingleOrDefault();

Opps…

image

It’s executed two queries, the first query looks fine…

image

The data returned is correct too, except NH doesn’t seem to think so, it actually queries for the Posts a second time.

However, updating the query to specify a join type:

    Post posts = null;
    Author author = null;

    blog = session.QueryOver<Blog>()
                  .Where(x => x.Id == 1)
                  .JoinAlias(x => x.Posts, () => posts, JoinType.LeftOuterJoin)
                  .JoinAlias(() => posts.Author, () => author)
                  .SingleOrDefault();

We end up with a single query, with all the correct results.

image

image

Categories: NHibernate Tags: , ,

‘Not.LazyLoad’ – Eager Loading with NHibernate 3.0

January 30th, 2011 Comments off

A friend asked me about fetching relationships with NHibernate when something couldn’t be lazy-loaded.

The reason it couldn’t be lazy-loaded was because he uses Session-Per-Call, my preference is Session-Per-Request. (this is web based, I’ve never actually used NHibernate in a non-web scenario, yet)

The solution he’s currently using is to turn off lazy-loading at the mapping. This is all kinds of bad.

I think it’s best to demonstrate why turning off lazy-loading at the mapping is bad. I’ve come up with a, well what I consider pretty standard Blog.

BlogClassDiagram

Nothing special but needed something that has a few relationship”s. (I think you can click that image to make it bigger)

So as we can see, a Post has 4 relationships, and a Comment has a relationship back to the Post and User.

I’ve mapped it and created some test data:

View Full Maps

View SQL Script

Right, so the maps are set to Not LazyLoad the relationships.

Post:

public class PostMap : ClassMap<Post>
{
    public PostMap()
    {
        Id(x => x.Id);
        Map(x => x.Title);
        Map(x => x.Content);
        Map(x => x.PublishedAt);

        References(x => x.Author)
            .Column("UserId")
            .Not.LazyLoad();

        HasManyToMany(x => x.Tags)
            .Table("PostTags")
            .ParentKeyColumn("PostId")
            .ChildKeyColumn("TagId")
            .Not.LazyLoad();

        HasManyToMany(x => x.Categories)
            .Table("PostCategories")
            .ParentKeyColumn("PostId")
            .ChildKeyColumn("CategoryId")
            .Not.LazyLoad();

        HasMany(x => x.Comments)
            .KeyColumn("PostId")
            .Inverse()
            .Not.LazyLoad();
    }
}


Comment:

public class CommentMap : ClassMap<Comment>
{
    public CommentMap()
    {
        Id(x => x.Id);
        Map(x => x.Content);

        References(x => x.Commenter)
            .Column("UserId")
            .Not.LazyLoad();

        References(x => x.Post)
            .Column("PostId")
            .Not.LazyLoad();
    }
}

I haven’t mapped any relationships the other way, keeping it simple, stupid. Smile

Right, so lets assume we wanted to load Post 1, and display the Title and the Categories the post was in. We didn’t care about the User, or the Comments, or anything like that.

var sessionFactory = new SessionFactoryManager().CreateSessionFactory();

using (var session = sessionFactory.OpenSession())
{
    var post = session.Query<Post>()
                        .SingleOrDefault(x => x.Id == 1);

    Console.WriteLine("Post: " + post.Title);
    Console.WriteLine("Category: ");

    post.Categories.ForEach(x => Console.WriteLine(x.Name));
}

If we were writing the query ourselves, all we would want is the Post, and the Categories, well because we mapped all the references and collections as Not.LazyLoad, NHibernate is kind enough to go and grab that information for us. The SQL Generated results in:

image

(Image is screen shot from NHProf)

AHHHHH Bad bad bad!!! It’s loaded all that information we don’t care about!

Right, lets set everything back to normal, and leave Lazy Loading on! Run the same query, and see what happens.

Post:

public class PostMap : ClassMap<Post>
{
    public PostMap()
    {
        Id(x => x.Id);
        Map(x => x.Title);
        Map(x => x.Content);
        Map(x => x.PublishedAt);

        References(x => x.Author)
            .Column("UserId");

        HasManyToMany(x => x.Tags)
            .Table("PostTags")
            .ParentKeyColumn("PostId")
            .ChildKeyColumn("TagId");

        HasManyToMany(x => x.Categories)
            .Table("PostCategories")
            .ParentKeyColumn("PostId")
            .ChildKeyColumn("CategoryId");

        HasMany(x => x.Comments)
            .KeyColumn("PostId")
            .Inverse();
    }
}


Comment:

public class CommentMap : ClassMap<Comment>
{
    public CommentMap()
    {
        Id(x => x.Id);
        Map(x => x.Content);

        References(x => x.Commenter)
            .Column("UserId");

        References(x => x.Post)
            .Column("PostId");
    }
}

Lets see what happens now.

image

Great! Much nicer, we haven’t got all that information we didn’t want. The only problem is, what about this scenario?

var sessionFactory = new SessionFactoryManager().CreateSessionFactory();

Post post;

using (var session = sessionFactory.OpenSession())
{
    post = session.Query<Post>()
                  .SingleOrDefault(x => x.Id == 1);

    Console.WriteLine("Post: " + post.Title);
}

Console.WriteLine("Category: ");
post.Categories.ForEach(x => Console.WriteLine(x.Name));

Lets assume that the ‘using’ block was a call to a repository, to get a particular post, and we needed to iterate over the categories a little bit later?

image

Oh no, not what we wanted Sad smile because the object is now disconnected from the session, it can’t lazy load the categories. This I suspect is the issue my friend got.

So the solution? To Eager Load!

Post post;

using (var session = sessionFactory.OpenSession())
{
    post = session.Query<Post>()
                    .Where(x => x.Id == 1)
                    .Fetch(x => x.Categories)
                    .SingleOrDefault();

    Console.WriteLine("Post: " + post.Title);
}

Console.WriteLine("Category: ");
post.Categories.ForEach(x => Console.WriteLine(x.Name));

This results in a single query being issued:

image

What’s this query look like?

image

Not bad! Definitely saves a second trip to the database to grab the categories. But there’s a problem, there types of queries result in cartesian product result-set. (Think that’s what it’s called)

If we issue that query our-self we end up with this:

image

We actually get Post back twice, because it’s got two categories. It’s only because we told NHibernate we wanted a single result that it knew the Root aggregate is distinct.

So what happens if we wanted more than 1 post, and eager load the relationships?

var sessionFactory = new SessionFactoryManager().CreateSessionFactory();

IList<Post> posts;

using (var session = sessionFactory.OpenSession())
{
    posts = session.QueryOver<Post>()
                    .Fetch(x => x.Categories).Eager
                    .List();
}

foreach (var post in posts)
{
    Console.WriteLine("Post: " + post.Title);

    Console.WriteLine("Categories:");
                
    foreach (var category in post.Categories)
    {
        Console.WriteLine(" – " + category.Name);
    }

    Console.WriteLine("—–");
}

Nothing special, right? Except… because we get duplicate posts in the last query, imagine what we get when we actually iterate over this result.

Post: Post 1
Categories:
– Category 1
– Category 3
—–
Post: Post 1
Categories:
– Category 1
– Category 3
—–
Post: Post 2
Categories:
– Category 2
—–
Post: Post 3
Categories:
– Category 1
– Category 2
– Category 3
—–
Post: Post 3
Categories:
– Category 1
– Category 2
– Category 3
—–
Post: Post 3
Categories:
– Category 1
– Category 2
– Category 3
—–

Scary right? We can fix this using NH Transformations.

using (var session = sessionFactory.OpenSession())
{
    posts = session.QueryOver<Post>()
                    .Fetch(x => x.Categories).Eager
                    .TransformUsing(new DistinctRootEntityResultTransformer())
                    .List();
}

This formats the result to be distinct posts with the related Categories like so:

Post: Post 1
Categories:
– Category 1
– Category 3
—–
Post: Post 2
Categories:
– Category 2
—–
Post: Post 3
Categories:
– Category 1
– Category 2
– Category 3
—–

Perfect!

As you can imagine, eager loading is a pretty powerful feature, but we still have to be careful to not load too many relationships. Because eager loading doesn’t break the relationships into different result-set queries, we can end up with inefficient queries because there are too many, or possibly too complex joins.

We could over-come some of these BY we, Not.LazyLoadby creating the object graph ourselves by using NHibernates ‘Future<T>’ feature, to batch queries together to reduce the number of database trips. That’s a post for another day.

Categories: NHibernate Tags:

‘let’ keyword in ORMs

January 29th, 2011 1 comment

A while ago MindScape posted a new feature called Ninja Properties for their ORM, LightSpeed. Their post happened to be a scenario I used to solve with LINQ 2 SQL using the ‘let’ keyword to create a composite type to query against.

This would allow you to do something along the lines of:

var poo = from s in ctx.Members
            let fullname = s.FirstName + " " + s.LastName
            where fullname == "Robert Williams"
            select s;

This nifty query would generate SQL like so:

SELECT [t1].[Id], [t1].[FirstName], [t1].[LastName]
FROM (
    SELECT [t0].[Id], [t0].[FirstName],
           [t0].[LastName],
           ([t0].[FirstName] + ' ') + [t0].[LastName] AS [value]
    FROM [Member] AS [t0]
    ) AS [t1]
WHERE [t1].[value] = 'Robert Williams'

After commenting in their blog post about it they implemented it and put it into the nightly build. So after some months I finally got around to testing it, for a couple of reasons. Back when NHibernate 3.0 was in Alpha, this technique didn’t work! So I thought I would test it out and see if it does, as well as checkout the SQL that’s generated.

I’m using the following ORM’s

  • LightSpeed 3.11 Nightly Build (3.1 it doesn’t work)
  • NHibernate 3.0
  • Linq 2 Sql
  • Entity Framework 4
    The data model is simple, but I have two scenarios I want to test. I created a Member and Task. The Member has a One-to-Many with Tasks.The test data:
    insert into member (firstname, lastname) values ('Robert', 'Williams')
    insert into member (firstname, lastname) values ('Michael', 'Jones')
    insert into member (firstname, lastname) values ('William', 'Brown')
    insert into member (firstname, lastname) values ('David', 'Davis')
    insert into member (firstname, lastname) values ('Richard', 'Miller')
    insert into member (firstname, lastname) values ('Charles', 'Wilson')

    insert into task (MemberId, Name) values(1, 'Task 1')
    insert into task (MemberId, Name) values(1, 'Task 2')
    insert into task (MemberId, Name) values(1, 'Task 3')
    insert into task (MemberId, Name) values(1, 'Task 4')
    insert into task (MemberId, Name) values(2, 'Task 1')
    insert into task (MemberId, Name) values(2, 'Task 2')
    insert into task (MemberId, Name) values(2, 'Task 3')
    insert into task (MemberId, Name) values(3, 'Task 4')

    Scenario 1: Find all Members where fullname contains ‘w’, case insensitive (lowercase the fullname)
    Scenario 2: Find all Members who have more than 2 tasks.
    These tests are not to say one ORM is better than another, I did this purely out of curiosity and am sharing my results.

     

    Scenario 1:

    I ran the same query against each ORM (to see the full script click here), the query is:

    var poo = from s in ctx.Members
                let fullname = s.FirstName + " " + s.LastName
                where fullname.ToLower().Contains("w")
                select s;

All queries are identical with the exception of the ‘in’ part, varying between ORMs.

LightSpeed : from s in uow.Members
NHibernate 3.0 : from s in session.Query<NHibernate.Member>()
Linq 2 Sql : from s in ctx.Members
Entity Framework 4: from s in mmc.Members

The output was as follows:

————
LINQ 2 SQL
Robert
William
Charles
————
LightSpeed
Robert
William
Charles
————
NHibernate 3.0
Robert
William
Charles
————
EntityFramework 4
Robert
William
Charles

 

All ORM’s worked perfectly. How about the SQL Generated from these?

Linq 2 SQL:

SELECT [t1].[Id], [t1].[FirstName], [t1].[LastName]
FROM (
    SELECT [t0].[Id], [t0].[FirstName],
           [t0].[LastName],
           ([t0].[FirstName] + ' ') + [t0].[LastName] AS [value]
    FROM [Member] AS [t0]
    ) AS [t1]
WHERE LOWER([t1].[value]) LIKE '%w%'

LightSpeed:

SELECT
  Member.Id AS [Member.Id],
  Member.FirstName AS [Member.FirstName],
  Member.LastName AS [Member.LastName]
FROM
  Member
WHERE
  LOWER(((Member.FirstName + ' ') + Member.LastName)) LIKE '%w%'

NHibernate:

select member0_.Id as Id0_,
       member0_.FirstName as FirstName0_,
       member0_.LastName as LastName0_
from [Member] member0_
where lower(member0_.FirstName+' '+member0_.LastName) like ('%'+'w'+'%')


Entity Framework 4.0:

SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[FirstName] AS [FirstName],
    [Extent1].[LastName] AS [LastName]
FROM [dbo].[Member] AS [Extent1]
WHERE LOWER([Extent1].[FirstName] + N' ' + [Extent1].[LastName])
      LIKE N'%w%'

Note: The first 3 queries are RPC’s and show in SQL Profiler with parameters, I’ve stripped the parameters out to show the readable query.

Great, so all 3 work fine. Tho I don’t like the SQL generated by Linq 2 Sql. Hard to believe Stack Overflow runs off it.

 

Scenario 2

The second scenario is again, the exact same query, you could write this multiple ways, however I found it cleaner to use ‘let’ back when I was using Linq 2 Sql. (to see the full script click here), the query is:

var poo = from s in ctx.Members
            let tasks = s.Tasks.COUNT() as Computed
            where tasks > 2
            select s;

Again, the only difference is the ‘in’ part of the query.

The output was as follows:

————
LINQ 2 SQL
Robert
Michael
————
LightSpeed
Exception: Specified method is not supported.
————
NHibernate 3.0
Robert
Michael
————
EntityFramework 4
Robert
Michael

 

All achieved the same results, except for LightSpeed. However I didn’t think NHibernate would support this?!? Seems they have done quite a bit of work implementing LINQ since Alpha.

How about the SQL generated?

Linq 2 Sql:

SELECT [t2].[Id], [t2].[FirstName], [t2].[LastName]
FROM (
    SELECT [t0].[Id], [t0].[FirstName], [t0].[LastName], (
        SELECT COUNT(*)
        FROM [Task] AS [t1]
        WHERE [t1].[MemberId] = [t0].[Id]
        ) AS [value]
    FROM [Member] AS [t0]
    ) AS [t2]
WHERE [t2].[value] > 2

NHibernate:

select member0_.Id as Id0_,
       member0_.FirstName as FirstName0_,
       member0_.LastName as LastName0_
from [Member] member0_
where (select cast(count(*) as INT)
        from [Task] tasks1_
        where member0_.Id=tasks1_.MemberId) > 2

Entity Framework:

SELECT
[Project1].[Id] AS [Id],
[Project1].[FirstName] AS [FirstName],
[Project1].[LastName] AS [LastName]
FROM ( SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[FirstName] AS [FirstName],
    [Extent1].[LastName] AS [LastName],
    (SELECT
        COUNT(1) AS [A1]
        FROM [dbo].[Task] AS [Extent2]
        WHERE [Extent1].[Id] = [Extent2].[MemberId]) AS [C1]
    FROM [dbo].[Member] AS [Extent1]
)  AS [Project1]
WHERE [Project1].[C1] > 2

 

I get the feeling that NHibernate generates more efficient queries than L2S/EF. Might be worth doing some performance testing against them oneday. Smile

Anyway as you can see, ‘let’ keyword allows you to create some nice queries that are easy to ready instead of trying to complicate things in your ‘where’ clause, when writing LINQ.