Archive for the ‘Programming’ Category

Dictionary: SerializationException

Wednesday, December 30th, 2009

When you to inherit your custom dictionary from Dictionary class you’ll get nasty exception during deserialization:

System.Runtime.Serialization.SerializationException : The constructor to deserialize an object of type ‘SafeDictionary`2[System.String,System.String]‘ was not found.

[Serializable]
internal class SafeDictionary<K, V> : Dictionary<K, V>
{
    public new V this[K key]
    {
            get
            {
                V value;
                if (this.TryGetValue(key, out value) == true)
                    return value;
                return default(V);
            }
            set
            {
                base[key] = value;
            }
    }

     public SafeDictionary()
    {
    }
};

Here’s the test:

[Test]
public void IsSerializable()
{
    SafeDictionary<string,string> dictionary =
        new SafeDictionary<string, string>();
    dictionary["key"] = "value";
    dictionary =
        SerializationHelper.SerializeAndDeserialize(dictionary);
    Assert.AreEqual("value", dictionary["key"]);
}

And serialization utility class:

class SerializationHelper
{
    public static T SerializeAndDeserialize<T>(T sm)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        using (MemoryStream stream = new MemoryStream())
        {
            formatter.Serialize(stream, sm);
            stream.Position = 0;
            return (T)formatter.Deserialize(stream);
        }
    }
}

Dictionary class implements ISerializable interface.

The ISerializable interface implies a constructor with the signature constructor (SerializationInfo information, StreamingContext context).

At deserialization time, the current constructor is called only after the data in the SerializationInfo has been deserialized by the formatter. In general, this constructor should be protected if the class is not sealed.

So what you need to do is to add the following constructor:

    //Needed for deserialization.
    protected SafeDictionary(SerializationInfo information, StreamingContext context)
            : base(information,context)
    {
    }

Now the test will pass.

NHibernate ignores SetMaxResults in SQL

Tuesday, December 8th, 2009

nhibernateRecently me and my team realized that several our queries, that include maximum limit on the recordset, are limited in the web application and not on the Oracle side.

First thought was that somebody simply forgot to add an invocation of SetMaxResults method, but our repository code was very obvious about it:

IQuery query = session.CreateQuery(hqlQuery);
query.SetMaxResults(100);

After some investigation it turned out that NHiberante is not able to use ROWNUM on the SQL side, when there is a fetch join on the collection.

EmployeeDatas

This is HQL query we use:

select
    user
from
    User user
    /* next line causes no rownum check */
    inner join fetch user.EmployeeDatas EmployeeData
    left join fetch EmployeeData.Country country

And some NHibernate code:

public IList List(ISessionImplementor session, QueryParameters queryParameters)
{
	// ...
	if ( hasLimit && ContainsCollectionFetches )
	{
		log.Warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );

NHibernate simply ignores the limit when executing the query and applies it on the result.

At some point I thought that it is not possible to even create such SQL query, but I was proven to be wrong. The query would look similar to this proof-of-concept:

SELECT
	   *
FROM
(
 	SELECT
		   *
    FROM
		 TUSER u
	WHERE
		  /* Add conditions here: */
		  EXISTS(...)
		  /* Here is DB-side limit: */
 		  AND ROWNUM <= 14
) AS u
/* And now fetch all collections needed */
INNER JOIN TEMPLOYEEDATA d ON u.ID = d.USERID

So it’s clear that it is possible to improve this in NHibernate.

In our case change was simple:
We changed one-to-many mapping to one-to-one.

But if you are not able to do this because of your requirements I would use custom SQL view and mapped it in NHibernate. The second idea would be to use NHibernate detached criteria and sub-queries.

Finally the unit test that we used to check if the limit is done on SQL database side:

[Test]
public void FindUsersBy_QueryWithLimit_LimitsOnSQLSide()
{
    using (LogChecker logChecker = new LogChecker("NHibernate", Level.Warn))
    {
        IList<User> users = this._context.ExecuteInTransaction(() =>
        {
            UserQuery q = new UserQuery();
            q.UserId = "ILMT";
            q.MaxNumberOfRecords = 14;
            return this._context.UserRepository.FindUsersBy(q);
        });
        Assert.IsEmpty(logChecker.Messages);
        Assert.AreEqual(14, users.Count);
    }
}

Here you can see the implementation of the LogChecker log4net log reader

Regex bug on Mono

Sunday, December 6th, 2009

monoThis is a good advice for all creating applications using .NET that need to work on both Microsoft .NET and Mono.

It seems that Mono ignores group indexes specified explicitly.
Here’s the code to reproduce this problem. And the results on Mono are incorrect.

Regex parser = new Regex("(?<2>.*?),(?<1>.*?),");
Match match = parser.Match("first,second,");
Console.WriteLine("Groups[1].Value={0}", match.Groups[1].Value);
Console.WriteLine("Groups[2].Value={0}", match.Groups[2].Value);

MS implementation:

C:1ConsoleApplication1binDebug>ConsoleApplication1.exe
Groups[1].Value=second
Groups[2].Value=first

Mono 2.4.2.3 implementation:

C:1ConsoleApplication1binDebug>mono.exe ConsoleApplication1.exe
Groups[1].Value=first
Groups[2].Value=second

The workaround is simple use names instead of numbers:

Regex parser = new Regex("(?<first>.*?),(?<second>.*?),");
Match match = parser.Match("first,second,");
Console.WriteLine("Groups[first].Value={0}", match.Groups["first"].Value);
Console.WriteLine("Groups[second].Value={0}", match.Groups["second"].Value);

Finder for any IEnumerable

Thursday, December 3rd, 2009

NHibernate requires you to use IList properties a lot:

public IList<Process> Processes { get; set; }

Searching through them is a real pain especially when you live in .NET 2.0 world:

public List<ProcessModel> GetDeleted()
{
    List<Process> list= new List<Process>();
    foreach (Process process in this.Processes)
    {
        if (process.IsDeleted)
            list.Add(process);
    }
    return list;
}

If you can’t move your project to .NET 3.5 and use extension methods here’s the solution for you.

This is the simple class for making searches on any IEnumerable easier:

public List<ProcessModel> GetDeleted()
{
        return Finder.Create(this.Processes)
                .FindAll(x => x.IsDeleted);
}

Nice, isn’t it?

The Finder class itself:

public class Finder
{
    public static Finder<T> Create<T>(IEnumerable<T> list)
    {
        return new Finder<T>(list);
    }
};

public class Finder<T>
{
    private readonly IEnumerable<T> _list;

    public Finder(IEnumerable<T> list)
    {
        _list = list;
    }

    public T Find(Predicate<T> func)
    {
        foreach (T element in _list)
        {
            if (func(element))
                return element;
        }
        return default(T);
    }

    public List<T> FindAll(Predicate<T> func)
    {
        List<T> list = new List<T>();
        foreach (T element in _list)
        {
            if (func(element))
                list.Add(element);
        }
        return list;
    }

    public ReadOnlyCollection<T> FindAllAsReadOnly(Predicate<T> func)
    {
        return new ReadOnlyCollection<T>(this.FindAll(func));
    }

    public bool Contains(Predicate<T> func)
    {
        foreach (T element in _list)
        {
            if (func(element))
                return true;
        }
        return false;
    }
};

and some tests:

[TestFixture]
public class FinderTest
{
    [Test]
    public void FindAll_ForValueType()
    {
        List<int> list = new List<int> { 1, 2, 10, 12 };
        Finder<int> finder = Finder.Create(list);
        Assert.AreEqual(1, finder.FindAll(x => x == 1).Count);
    }

    [Test]
    public void FindAll_ForRefType()
    {
        List<Box> list = new List<Box> { new Box(1), new Box(1), new Box(15) };
        Finder<Box> finder = Finder.Create(list);
        Assert.AreEqual(2, finder.FindAll(x => x.Id == 1).Count);
    }

    [Test]
    public void FindAllAsReadOnly_Find_ForRefType()
    {
        List<Box> list = new List<Box> { new Box(1), new Box(1), new Box(15) };
        Finder<Box> finder = Finder.Create(list);
        ReadOnlyCollection<Box> only = finder.FindAllAsReadOnly(x => x.Id == 1);
        Assert.AreEqual(2, only.Count);
    }

    [Test]
    public void Find_ForValueType()
    {
        List<int> list = new List<int> { 1 ,2, 10, 12 };
        Finder<int> finder = Finder.Create(list);
        Assert.AreEqual(1, finder.Find(x => x == 1));
        Assert.AreEqual(12, finder.Find(x => x == 12));
        Assert.AreEqual(0, finder.Find(x => x == 999));
    }

    [Test]
    public void Find_ForRefType()
    {
        List<Box> list = new List<Box> { new Box(21),  new Box(2),  new Box(15) };
        Finder<Box> finder = Finder.Create(list);
        Assert.AreEqual(21, finder.Find(x => x.Id == 21).Id);
        Assert.AreEqual(2, finder.Find(x => x.Id == 2).Id);
        Assert.AreEqual(null, finder.Find(x => x.Id == 999));
    }

    [Test]
    public void Find_ForEmptyList()
    {
        Finder<Box> finder = Finder.Create(new List<Box>());
        Assert.IsNull(finder.Find(x => x.Id == 21));
        Assert.IsNull(finder.Find(x => x.Id == 2));
        Assert.IsNull(finder.Find(x => x.Id == 999));
    }

    internal class Box
    {
        readonly int _id;

        public int Id
        {
            get { return _id; }
        }

        public Box(int id)
        {
            _id = id;
        }
    }
} ;

Many-to-one with non unique columns

Wednesday, December 2nd, 2009

6387611d

Tables look as follows:

User
+ID
+PERSON_ID
+PRIMARY_USER

DeputyRole
+PERSON_ID

Disclaimer: Yes I know the db schema is broken, but we can not do anything about it.

Why didn’t we use standard propery-ref and column mapping but formula instead?

  1. USER.PERSON_ID is not unique in USER table.
  2. USER.PERSON_ID AND USER.PRIMARY_USER = ‘Y’ is unique in USER table.
  3. We are using formula to get single record from USER (ID) with specified PERSON_ID and PRIMARY_USER = ‘Y’

In DeputyRole.hbm.xml we have:

<many-to-one name="User"
        class="DomainModel.Users.Entities.User"
        lazy="false"
        formula = "(SELECT u.ID FROM USER u WHERE
                                u.PRIMARYUSERID = 'Y'
                                AND u.PERSON_ID = PERSON_ID)"
/>

Can this be done in better way?