Archives

Archives / 2009 / June
  • NHibernate Relations - One to One

    This is my first post on NHibernate relations. I will first talk about a somewhat misused relation, one to one.

    This relation is about two tables that share a common primary key, for example, one of these tables contains some data and the other some additional data, and there is a 1:1 relationship between the two, meaning that one record from the first table has exactly one corresponding record on the second table and that all records from the second table have also exactly one corresponding record on the first.

    The domain model for my example looks like this:

    One to One

    The classes look like this:

    One to One Classes

    For the moment, just ignore the Version property. Property E of class A is an instance of class E and property A of class E is an instance of class A. Class A holds a property which is the primary key of table A.

    Let's look at the mappings:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property">

    <class name="A" table="`A`" optimistic-lock="version">

    <id name="Id" column="`A1`">

    <generator class="foreign">

    <param name="property">E</param>

    </generator>

    </id>

    <version name="Version" generated="always" unsaved-value="null" type="BinaryBlob" column="`VERSION`" />

    <property name="SomeProperty" column="`SOME_PROPERTY`"/>

    <one-to-one name="E" class="E" constrained="true"/>

    <map name="Attributes" table="`F`" outer-join="true" cascade="all">

    <key column="`A1`"/>

    <index column="`KEY`" type="System.String"/>

    <element column="`VALUE`" type="System.String"/>

    </map>

    </class>

    </hibernate-mapping>

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property">

    <class name="E" table="`E`">

    <id type="System.Int32">

    <column name="`A1`"/>

    <generator class="assigned"/>

    </id>

    <one-to-one name="A" class="A" outer-join="true" constrained="true"/>

    <property name="SomeOtherProperty" column="`SOME_OTHER_PROPERTY`"/>

    </class>

    </hibernate-mapping>

    You can see that the id for class E is an instance of class A. Again, please disregard for now property Version.

    Read more...

  • NHibernate in Action Book

    NHibernate in Action

    I finally had the chance to read the long-waited NHibernate in Action, and I must say I am not very excited about it.

    Covering just NHibernate 1.2, the book is, of course, extremely outdated. It does not cover any of the new features, and, what's worse, it does not cover well some of the old features, for example, there is no HQL reference, which I think it's a shame, although it is somewhat referred in chapter 7, nor there is a reference to some other projects, such as NHibernate Validator.

    What matters is that if a developer wants to use NHibernate 2.x in a new project, he/she will find the information in this book inaccurate and misleading - NHibernate now supports SQL Server's TIMESTAMP data type for optimistic concurrency, for example, there is the NHibernate Burrow project to support long conversations, NHibernate Validator for validations, there is support for TransactionScope, <many-to-any> mappings, query-only properties, multi-queries, to name just a few.

    The book does a fair job in introducing NHibernate, but I sincerely don't like the organization of it.

    Read more...

  • NHibernate and Manually Assigned Identifiers

    Be careful when you use manually assigned identifiers on your entities, because NHibernate looks at them to determine if an object is new or is already in the database.

    For example, suppose you have a parent class A which holds a collection of child class B:

    using (ISession session = ...)

    using (ITransaction tx = session.BeginTransaction())

        A a = session.Load<A>(1);

        B b = new B();

        b.Id = 100;

        a.B.Add(b);

        b.A = a;

        tx.Commit();

    }

    NHibernate will mistakenly assume that, since b has a non-default id, it is an existing object, and will try to update it, which, of course, will throw an exception, since there is no associated record on the database. In this case, you must explicitly save the b object.

    }

    Read more...

  • NHibernate Mappings

    In this post, I will talk about NHibernate mappings. Let's start from the data model:

    Tables

    What we can see here are 4 types of relationships:

    1. one-to-one, from table A to table E;
    2. A many-to-one, from table B to table C;
    3. A one-to-many, from table C to table D;
    4. A many-to-one, from table D to table C.

    Also, note this:

    1. All tables, except A, D and E, have composite primary keys; this is the most generic case;
    2. All tables, except B_C, have a Text column;
    3. Primary keys are always named <tablename><number>, for example, A1, A2, etc;
    4. Foreign keys have the same name as the referring primary key.

    Now, let's see the classes that map these tables and relationships:

    Classes

    And the source code is:

    using System;

    using System.Collections.Generic;

    namespace NHibernateTest

    {

    [
    Serializable]

    public class A

    {

    public virtual Int32 Id  { get; set; }

    public virtual E E { get; set; }

    public virtual String Text { get; set; }

    public override Boolean Equals(Object obj)

    {

    if (!(obj is A))

    {

    return (false);

    }

    if (Object.ReferenceEquals(this, obj) == true)

    {

    return (true);

    }

    A other = obj as A;

    return(this.Id == other.Id);

    }

    public override Int32 GetHashCode()

    {

    return (this.Id);

    }

    }

    }

     

    using System;

    using System.Collections.Generic;

    namespace NHibernateTest

    {

    [
    Serializable]

    public class B

    {

    public class BKey

    {

    public virtual Int32 B1 { get; set; }

    public virtual Int32 B2 { get; set; }

    public override Boolean Equals(Object obj)

    {

    if (!(obj is BKey))

    {

    return (false);

    }

    if (Object.ReferenceEquals(this, obj) == true)

    {

    return (true);

    }

    BKey other = obj as BKey;

    return ((this.B1 == other.B1) && (this.B2 == other.B2));

    }

    public override Int32 GetHashCode()

    {

    return (this.B1 + (1000 * this.B2));

    }

    }

    public virtual BKey Id { get; set; }

    public virtual String Text { get; set; }

    public virtual ISet<C> C { get; set; }

    public override Boolean Equals(Object obj)

    {

    if (!(obj is B))

    {

    return (false);

    }

    if (Object.ReferenceEquals(this, obj) == true)

    {

    return (true);

    }

    B other = obj as B;

    return (Object.Equals(this.Id, other.Id) == true);

    }

    public override Int32 GetHashCode()

    {

    return (this.Id != null ? this.Id.GetHashCode() : 0);

    }

    }

    }

     

    using System;

    using System.Collections.Generic;

    namespace NHibernateTest

    {

    [
    Serializable]

    public class C

    {

    public class CKey

    {

    public virtual Int32 C1 { get; set; }

    public virtual Int32 C2 { get; set; }

    public override Boolean Equals(Object obj)

    {

    if (!(obj is CKey))

    {

    return (false);

    }

    if (Object.ReferenceEquals(this, obj) == true)

    {

    return (true);

    }

    CKey other = obj as CKey;

    return ((this.C1 == other.C1) && (this.C2 == other.C2));

    }

    public override Int32 GetHashCode()

    {

    return (this.C1 + (1000 * this.C2));

    }

    }

    public virtual CKey Id { get; set; }

    public virtual String Text { get; set; }

    public virtual ISet<B> B { get; set; }

    public virtual ISet<D> D { get; set; }

    public override Boolean Equals(Object obj)

    {

    if (!(obj is C))

    {

    return (false);

    }

    if (Object.ReferenceEquals(this, obj) == true)

    {

    return (true);

    }

    C other = obj as C;

    return (Object.Equals(this.Id, other.Id) == true);

    }

    public override Int32 GetHashCode()

    {

    return (this.Id != null ? this.Id.GetHashCode() : 0);

    }

    }

    }

     

    using System;

    using System.Collections.Generic;

    namespace NHibernateTest

    {

    [
    Serializable]

    public class D

    {

    public virtual Int32 Id { get; set; }

    public virtual C C { get; set; }

    public virtual String Text { get; set; }

    }

    }

     

    using System;

    using System.Collections.Generic;

    namespace NHibernateTest

    {

    [
    Serializable]

    public class E

    {

    public virtual A A { get; set; }

    public virtual String Text { get; set; }

    }

    }

    As you can see:

    1. There is no class to map table B_C, this is because this is pure mapping table, it contains no additional attributes other than the foreign keys;
    2. The id for class E is a property of type A;
    3. No base class;
    4. Methods Equals and GetHashCode are always implemented;
    5. Collections are ISet<T>, which means they cannot hold equal objects;
    6. Properties are auto properties and always have public setters;
    7. Id classes for mapping composite keys are implemented as internal.

    Now, here are the mappings:

    For class A:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property">     <class name="A" table="`A`">

            <id name="Id" column="`A1`">

                 <generator class="foreign">

                    <param name="property">E</param>

                 </generator>

            </id> 

            <property name="Text" column="`TEXT`"/>

            <one-to-one name="E" class="E" />

        </class>

    </hibernate-mapping>

    For class B:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property">

        <class name="B" table="`B`">

            <composite-id class="B+BKey" name="Id">

                <key-property column="`B1`" name="B1"/>

                <key-property column="`B2`" name="B2"/>         </composite-id>

            <property name="Text" column="`TEXT`"/>

             <set name="C" table="`B_C`" fetch="joincascade="save-update" outer-join="true">

                <key>

                     <column name="`B1`"/>

                    <column name="`B2`"/>

                 </key>

                <many-to-many class="C" fetch="join" outer-join="true">

                     <column name="`C1`"/>

                    <column name="`C2`"/>

                 </many-to-many>

            </set>

         </class>

    </hibernate-mapping>

    For class C:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property">

        <class name="C" table="`C`">

            <composite-id class="C+CKey" name="Id">

                <key-property column="`C1`" name="C1"/>

                <key-property column="`C2`" name="C2"/>

            </composite-id>

            <property name="Text" column="`TEXT`" />

            <set name="B" table="`B_C`" fetch="join" cascade="save-update" outer-join="true">

                <key>

                    <column name="`C1`"/>

                    <column name="`C2`"/>

                </key>

                <many-to-many class="B" fetch="join">                 <column name="`B1`"/>

                    <column name="`B2`"/>

                 </many-to-many>

            </set>

             <set name="D" inverse="true" cascade="all-delete-orphan" outer-join="true">

                <key>

                     <column name="`C1`"/>

                    <column name="`C2`"/>

                 </key>

                <one-to-many class="D"/>

             </set>

        </class>

    </hibernate-mapping>

    For class D:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property">

        <class name="D" table="`D`">

            <id name="Id" column="`D1`">

                <generator class="assigned"/>

            </id>

            <property name="Text" column="`TEXT`"/>

            <many-to-one name="C">

                <column name="`C1`"/>

                <column name="`C2`"/>

            </many-to-one>

        </class>

    </hibernate-mapping>

    And finally, for class E:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property">

        <class name="E" table="`E`">

            <id type="System.Int32">

                <column name="`A1`"/>

                <generator class="assigned"/>

            </id>

            <one-to-one name="A" class="A"/>

            <property name="Text" column="`TEXT`"/>

        </class>

    </hibernate-mapping>

    The notes for the mappings are:

    1. Table and column names are enclosed in `;
    2. Collections are sets, not lists, idbags or bags; 
    3. Primary keys are manually assigned;
    4. Properties are directly used to access values;
    5. Collections always use outer-join.

    Hope this is useful to somebody!

    Read more...

  • Sharing Resources between a Web and a Windows Forms Application

    It is possible to share a common set of resource files between a web and a windows forms application quite easily. You first create the resource files on the web application as global resource files, and place them on the App_GlobalResources folder. On the windows application, add a link to these files, that is, do not copy them. Make sure you mark them as embedded resources. Here comes the tricky part: you can see from the generated classes that these classes are trying to load from the App_GlobalResources folder, what you have to do is, through reflection, change the resource manager to point to the current assembly and resource location. Let's see how this is done: FieldInfo fi = typeof(MyResourceClass).GetField("resourceMan", BindingFlags.Static | BindingFlags.NonPublic); fi.SetValue(null, new ResourceManager("My.Full.Default.Assembly.Namespace.MyResourceClass", Assembly.GetExecutingAssembly()); In this example, suppose your windows forms assembly has a default namespace of My.Full.Default.Assembly.Namespace. And that's it! You can now do: String myVariable = MyResourceClass.MyResource on both projects.

    Read more...

  • FormsAuthentication and Session Timeouts

    Because the FormsAuthentication and the Session cookies are not the same, it is possible that when you are accessing your application you are still logged in, but the session has expired. In this situation, perhaps the best thing to do is logout from FormsAuthentication and redirect to the same page. You can do this through a custom module. Let's see how:

    public class CheckSessionModule: IHttpModule

    {

        public void Init(HttpApplication app)

        {

            ctx.Application.AcquireRequestState += this.OnAcquireRequestState;

        }

        public void Dispose() {}

        public void OnAcquireRequestState(Object sender, EventArgs args)

        {

            if ((HttpContext.Current.User.Identity.IsAuthenticated == true) && (HttpContext.Current.Session.IsNewSession == true))

            {

                FormsAuthentication.SignOut();

                HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Url.ToString(), false);

                HttpContext.Current.ApplicationInstance.CompleteRequest();

            }

        }

    }

    Read more...