The Reality of Developer’s Life

Today I found a very funny page which describes the reality of developer’s life and illustrates with animated pics.

I need to share it 🙂

http://www.lordofthejars.com/2013/02/the-reality-of-developers-life.html

Kategória: Hétköznapok | Címke: , , , , | Megjegyzés hozzáfűzése

NHibernate, System.Data.SqlServerCe.SqlCeException (0x80004005): A duplicate value cannot be inserted into a unique index.

Yesterday I took part in database schema development with my team and a very strange and unexpected exception threw when we wanted to save data across NHibernate Entity Framework.
I spent several hours to figure out the root of the problem, so this experience may useful other developers in the future.
We are using Sql Server CE 4.0 in a decentralized synchronization system called Everlight.

The symptom:

2013-02-02 11:57:23,183 ERROR [10] [NHibernate.AdoNet.AbstractBatcher] - Could not execute command: INSERT INTO EmployeeGroup_Employees_Switch (employeeGroup_systemId, employeeGroup_deviceId, employeeGroup_id, employee_systemId, employee_deviceId, employee_id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5)
System.Data.SqlServerCe.SqlCeException (0x80004005): A duplicate value cannot be inserted into a unique index. [ Table name = EmployeeGroup_Employees_Switch,Constraint name = PK__EmployeeGroup_Employees_Switch__000000000000012F ]
   at System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr)
   at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommandText(IntPtr& pCursor, Boolean& isBaseTableCursor)
   at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
   at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
   at NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd) in p:\nhibernate-core\src\NHibernate\AdoNet\AbstractBatcher.cs:line 197
2013-02-02 11:57:23,355 ERROR [10] [NHibernate.Util.ADOExceptionReporter] - A duplicate value cannot be inserted into a unique index. [ Table name = EmployeeGroup_Employees_Switch,Constraint name = PK__EmployeeGroup_Employees_Switch__000000000000012F ]
2013-02-02 11:57:23,448 ERROR [10] [NHibernate.Event.Default.AbstractFlushingEventListener] - Could not synchronize database state with session
NHibernate.Exceptions.GenericADOException: could not insert collection: [HVS.Hvs360.EntityModel.Common.EmployeeGroup.employees#1:2066501244:634953994429806654][SQL: INSERT INTO EmployeeGroup_Employees_Switch (employeeGroup_systemId, employeeGroup_deviceId, employeeGroup_id, employee_systemId, employee_deviceId, employee_id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5)] ---> System.Data.SqlServerCe.SqlCeException: A duplicate value cannot be inserted into a unique index. [ Table name = EmployeeGroup_Employees_Switch,Constraint name = PK__EmployeeGroup_Employees_Switch__000000000000012F ]
   at System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr)
   at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommandText(IntPtr& pCursor, Boolean& isBaseTableCursor)
   at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
   at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
   at NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd) in p:\nhibernate-core\src\NHibernate\AdoNet\AbstractBatcher.cs:line 210
   at NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation expectation) in p:\nhibernate-core\src\NHibernate\AdoNet\NonBatchingBatcher.cs:line 40
   at NHibernate.Persister.Collection.AbstractCollectionPersister.PerformInsert(Object ownerId, IPersistentCollection collection, IExpectation expectation, Object entry, Int32 index, Boolean useBatch, Boolean callable, ISessionImplementor session) in p:\nhibernate-core\src\NHibernate\Persister\Collection\AbstractCollectionPersister.cs:line 2013
   at NHibernate.Persister.Collection.AbstractCollectionPersister.Recreate(IPersistentCollection collection, Object id, ISessionImplementor session) in p:\nhibernate-core\src\NHibernate\Persister\Collection\AbstractCollectionPersister.cs:line 1109
   --- End of inner exception stack trace ---
   at NHibernate.Persister.Collection.AbstractCollectionPersister.Recreate(IPersistentCollection collection, Object id, ISessionImplementor session) in p:\nhibernate-core\src\NHibernate\Persister\Collection\AbstractCollectionPersister.cs:line 1140
   at NHibernate.Action.CollectionRecreateAction.Execute() in p:\nhibernate-core\src\NHibernate\Action\CollectionRecreateAction.cs:line 35
   at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in p:\nhibernate-core\src\NHibernate\Engine\ActionQueue.cs:line 136
   at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in p:\nhibernate-core\src\NHibernate\Engine\ActionQueue.cs:line 125
   at NHibernate.Engine.ActionQueue.ExecuteActions() in p:\nhibernate-core\src\NHibernate\Engine\ActionQueue.cs:line 174
   at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in p:\nhibernate-core\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 241

The error message on the UI:

ErrorMessage

I have definied two entities:

  • EmployeeGroup
  • Employee

I am using bidirectional Many-To-Many relations, an EmployeeGroup may has several Employees and an Employee instance may belongs to several EmployeeGroups.

The source codes of the entities are the following:

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "employeeGroups", Cascade = "none", Generic = true, Lazy = CollectionLazy.True, Table = "EmployeeGroup_Employees_Switch")]
        [Key(1)]
        [Column(2, Name = "employee_systemId")]
        [Column(3, Name = "employee_deviceId")]
        [Column(4, Name = "employee_id")]
        [ManyToMany(5, NotFound = NotFoundMode.Exception, ClassType = typeof(EmployeeGroup))]
        [Column(6, Name = "employeeGroup_systemId")]
        [Column(7, Name = "employeeGroup_deviceId")]
        [Column(8, Name = "employeeGroup_id")]
        private ISet<EmployeeGroup> employeeGroups = new HashSet<EmployeeGroup>();
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "employees", Cascade = "none", Generic = true, Lazy = CollectionLazy.True, Table = "EmployeeGroup_Employees_Switch")]
        [Key(1)]
        [Column(2, Name = "employeeGroup_systemId")]
        [Column(3, Name = "employeeGroup_deviceId")]
        [Column(4, Name = "employeeGroup_id")]
        [ManyToMany(5, NotFound = NotFoundMode.Exception, ClassType = typeof(Employee))]
        [Column(6, Name = "employee_systemId")]
        [Column(7, Name = "employee_deviceId")]
        [Column(8, Name = "employee_id")]
        private ISet<Employee> employees = new HashSet<Employee>();

I use this code to instantiate and save entities:

            IUnitOfWork uow = null;
            try
            {
                uow = mDomainManager.CreateUnitOfWork();

                HashSet<SecurityRight> rights = null;
                {
                    OperatorGroup opGroup = new OperatorGroup();
                    opGroup.Name = "OperatorGroup";
                    opGroup.OperatorGroupType = OperatorGroupTypeEnum.Operators;
                    uow.EntitySave(opGroup);

                    Operator op = new Operator();
                    op.FullName = "OperatorUser";
                    op.Hidden = true;
                    op.HumanType = HumanTypeEnum.OperationTeamMember;

                    HashSet<EmployeeGroup> opGroups = new HashSet<EmployeeGroup>();
                    opGroups.Add(opGroup);
                    op.EmployeeGroups = opGroups;
                    uow.EntitySave(op);

                    HashSet<Employee> ops = new HashSet<Employee>();
                    ops.Add(op);
                    opGroup.Employees = ops;
                    uow.EntitySave(opGroup);
                }

                {
                    OperatorGroup salesGroup = new OperatorGroup();
                    salesGroup.Name = "SalesGroup";
                    salesGroup.OperatorGroupType = OperatorGroupTypeEnum.Sales;
                    uow.EntitySave(salesGroup);
                }

                uow.Commit("Operation_data_creation");
            }
            catch (Exception ex)
            {
                if (uow != null)
                {
                    uow.Disconnect();
                }
                MessageBox.Show(this, string.Format("Failed to load data. Reason: {0}", ex.Message), "Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                if (uow != null)
                {
                    uow.Disconnect();
                }
            }

The Operator inherits from Employee, the OperationGroup inherits the EmployeeGroup.
The exception comes from the database engine, when I commits the transaction. The NHibernate wants to save the relation information into the table “EmployeeGroup_Employees_Switch”. This table has six (6) colums as a primary key.

switch

This may looks strange, but in our system each entities has a composite primary key which consists of a systemId, deviceId and id. So the switch table stores the identifier of the OperationGroup on the left side and the Operator identifier on the right side.
The primary key must be unique. NHibernate wants to save the relation into this table twice.

At first, I did not understand why. Debugging the NHibernate code I figure out that this is a normal behavior, so something wrong with the database mapping.

Finally I found the problem successfully. I forgot to define the inverse property 🙂
In my previous post which deals with Many-To-Many relations, I had definied this property well. But now, I simply forgot it.

The correct definition on the Employee entity:

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "employeeGroups", Cascade = "none", Generic = true, Lazy = CollectionLazy.True, Table = "EmployeeGroup_Employees_Switch", Inverse = true)]
        [Key(1)]
        [Column(2, Name = "employee_systemId")]
        [Column(3, Name = "employee_deviceId")]
        [Column(4, Name = "employee_id")]
        [ManyToMany(5, NotFound = NotFoundMode.Exception, ClassType = typeof(EmployeeGroup))]
        [Column(6, Name = "employeeGroup_systemId")]
        [Column(7, Name = "employeeGroup_deviceId")]
        [Column(8, Name = "employeeGroup_id")]
        private ISet<EmployeeGroup> employeeGroups = new HashSet<EmployeeGroup>();

As you can see the difference, I have to define the “Inverse = true” on the attribute Set. This property guarantees that the NHibernate saves the relations only once.

At first sight the exception was not hint for a mapping problem.

Good to know that 🙂

Kategória: NHibernate | Címke: , , , , , , , , , , | Megjegyzés hozzáfűzése

Entity Framework 5, Many-To-One mapping, Composite Identifier, Unidirectional relationship, Code First

This topic talks about EF5 Many-To-One relations where the relations described with attributes. I will declare the mapping schema with code first approach.

For the presentation I have defined two entities:

  • Country
  • Address

Address holds the reference of Country, but Country does not know anything about Address entity. This is an Unidirectional relationship between entities.

EF5.DomainModel.ManyToOne

The Country entity class code:

    [Serializable]
    [Table("Addresses")]
    public class Address : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private string addressInfo = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private Country country = null;

        public Address()
            : base()
        {
        }

        [Column("addressInfo")]
        public string AddressInfo
        {
            get { return addressInfo; }
            set
            {
                OnPropertyChanging("AddressInfo");
                addressInfo = value;
                OnPropertyChanged("AddressInfo");
            }
        }

        /// <summary>
        /// Gets or sets the country.
        /// This is a trick to define custom foreign keys for composite indentifiers
        /// </summary>
        /// <value>
        /// The country.
        /// </value>
        [ForeignKey("country_systemId, country_deviceId, country_id")]
        [Required]
        public virtual Country Country
        {
            get { return country; }
            set
            {
                OnPropertyChanging("Country");
                country = value;
                OnPropertyChanged("Country");
            }
        }

        [DebuggerHidden]
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Browsable(false)]
        public long country_systemId { get; set; }

        [DebuggerHidden]
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Browsable(false)]
        public long country_deviceId { get; set; }

        [DebuggerHidden]
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Browsable(false)]
        public long country_id { get; set; }

    }

The Address entity class code:

    [Serializable]
    [Table("Countries")]
    [DataContractAttribute(IsReference = true)]
    public class Country : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private string name = string.Empty;

        public Country()
            : base()
        {
        }

        [DataMemberAttribute]
        [DebuggerHidden]
        [Column("name")]
        public string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

    }

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has three identifiers as complex primary key. The EntityId class simulates a composite key because EF5 does not support composite component key.

EF5.Db.ManyToOne

Here is an example how to create and save entities:

        [TestMethod]
        public void TestManyToOne()
        {
            using (ManyToOneObjectContext context = DbContextCommon.CreateObjectContext<ManyToOneObjectContext>(CreateConnection()))
            {
                Country countryHun = context.CreateObject<Country>();
                EFUtils.InitializeEntityForThisDemo(countryHun);
                countryHun.Name = "Hungary";
                context.Countries.AddObject(countryHun);

                Country countryGer = context.CreateObject<Country>();
                EFUtils.InitializeEntityForThisDemo(countryGer);
                countryGer.Name = "Germany";
                context.Countries.AddObject(countryGer);

                Country countryEng = context.CreateObject<Country>();
                EFUtils.InitializeEntityForThisDemo(countryEng);
                countryEng.Name = "United Kingdom";
                context.Countries.AddObject(countryEng);

                Address add1 = context.CreateObject<Address>();
                EFUtils.InitializeEntityForThisDemo(add1);
                add1.AddressInfo = "Szirmabesenyo";
                add1.Country = countryHun;
                context.Addresses.AddObject(add1);

                Address add2 = context.CreateObject<Address>();
                EFUtils.InitializeEntityForThisDemo(add2);
                add2.AddressInfo = "Miskolc";
                add2.Country = countryHun;
                context.Addresses.AddObject(add2);

                Address add3 = context.CreateObject<Address>();
                EFUtils.InitializeEntityForThisDemo(add3);
                add3.AddressInfo = "Berlin";
                add3.Country = countryGer;
                context.Addresses.AddObject(add3);

                context.SaveChanges();
            }

            using (ManyToOneObjectContext context = DbContextCommon.CreateObjectContext<ManyToOneObjectContext>(CreateConnection()))
            {
                var testQuery = (from addr in context.Addresses
                                where (addr.Country != null && "Hungary".Equals(addr.Country.Name))
                                select addr).ToList<Address>();

                // this test fails if you run this test more than once
                Assert.IsTrue(testQuery.Count >= 2);

            }

        }

Download the Many-To-One Unidirectional Visual Studio 2010 example project:

Kategória: Entity Framework | Címke: , , , , , , | Megjegyzés hozzáfűzése

NHibernate Many-To-Many mapping, Composite Identifier, Bidirectional relationship

Last topic is the Many-To-Many Unidirectional mapping in NHibernate. I will declare the relations as the same way as done it at my previous articles: Code First and XML Mapping. Each approaches are right, you can choose the solution which is the best for your project.

For the presentation I have defined two entities:

  • Car
  • Driver

Car and Driver entity too holds the references to each other. Car may belongs to several Drivers and vica versa. This is an Bidirectional relationship.

DomainModel.ManyToManyBi

The Car entity class code:

    [Serializable]
    [Class(Table = "Cars")]
    public class Car : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "drivers", Cascade = "none", Generic = true, Lazy = CollectionLazy.True, Table = "Drivers_Cars_Switch", Inverse = true)]
        [Key(1)]
        [Column(2, Name = "car_restId")]
        [Column(3, Name = "car_deviceId")]
        [Column(4, Name = "car_id")]
        [ManyToMany(5, NotFound = NotFoundMode.Exception, ClassType = typeof(Driver))]
        [Column(6, Name = "driver_restId")]
        [Column(7, Name = "driver_deviceId")]
        [Column(8, Name = "driver_id")]
        private ISet<Driver> drivers = new HashSet<Driver>();

        public Car()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        [DebuggerHidden]
        public virtual ISet<Driver> Drivers
        {
            get { return new HashSet<Driver>(drivers); }
            set
            {
                if (value == null)
                {
                    ThrowHelper.ThrowArgumentNullException("value");
                }

                OnPropertyChanging("Drivers");
                drivers = new HashSet<Driver>(value);
                OnPropertyChanged("Drivers");
            }
        }

    }

The Driver entity class code:

    [Serializable]
    [Class(Table = "Drivers")]
    public class Driver : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "cars", Cascade = "none", Generic = true, Lazy = CollectionLazy.True, Table = "Drivers_Cars_Switch")]
        [Key(1)]
        [Column(2, Name = "driver_restId")]
        [Column(3, Name = "driver_deviceId")]
        [Column(4, Name = "driver_id")]
        [ManyToMany(5, NotFound = NotFoundMode.Exception, ClassType = typeof(Car))]
        [Column(6, Name = "car_restId")]
        [Column(7, Name = "car_deviceId")]
        [Column(8, Name = "car_id")]
        private ISet<Car> cars = new HashSet<Car>();

        public Driver()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        [DebuggerHidden]
        public virtual ISet<Car> Cars
        {
            get { return new HashSet<Car>(cars); }
            set
            {
                if (value == null)
                {
                    ThrowHelper.ThrowArgumentNullException("value");
                }

                OnPropertyChanging("Cars");
                cars = new HashSet<Car>(value);
                OnPropertyChanged("Cars");
            }
        }

    }

The mapping XML is the following:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateDemo.DomainModel.ManyToManyBiDirectional, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  
  <class table="Drivers" name="NHibernateDemo.DomainModel.ManyToManyBiDirectional.Driver, NHibernateDemo.DomainModel.ManyToManyBiDirectional">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
    
    <set name="cars" table="Drivers_Cars_Switch" lazy="true" cascade="none" generic="true">
      <key>
        <column name="driver_restId" />
        <column name="driver_deviceId" />
        <column name="driver_id" />
      </key>
      <many-to-many class="NHibernateDemo.DomainModel.ManyToManyBiDirectional.Car, NHibernateDemo.DomainModel.ManyToManyBiDirectional" not-found="exception">
        <column name="car_restId" />
        <column name="car_deviceId" />
        <column name="car_id" />
      </many-to-many>
    </set>
    
  </class>
  
  <class table="Cars" name="NHibernateDemo.DomainModel.ManyToManyBiDirectional.Car, NHibernateDemo.DomainModel.ManyToManyBiDirectional">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
    
    <set name="drivers" table="Drivers_Cars_Switch" lazy="true" inverse="true" cascade="none" generic="true">
      <key>
        <column name="car_restId" />
        <column name="car_deviceId" />
        <column name="car_id" />
      </key>
      <many-to-many class="NHibernateDemo.DomainModel.ManyToManyBiDirectional.Driver, NHibernateDemo.DomainModel.ManyToManyBiDirectional" not-found="exception">
        <column name="driver_restId" />
        <column name="driver_deviceId" />
        <column name="driver_id" />
      </many-to-many>
    </set>
    
  </class>
  
</hibernate-mapping>

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has a composite identifier as complex primary key.

Database.ManyToManyBi

Here is an example, how to create and save entities:

        [TestMethod]
        public void TestManyToManyBi()
        {
            // create the entities
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Car mazda = new Car();
                    EFUtils.InitializeEntityForThisDemo(mazda);
                    mazda.Name = "Mazda";
                    EFUtils.SaveEntity(mazda, session);

                    Car honda = new Car();
                    EFUtils.InitializeEntityForThisDemo(honda);
                    honda.Name = "Honda";
                    EFUtils.SaveEntity(honda, session);

                    Car kia = new Car();
                    EFUtils.InitializeEntityForThisDemo(kia);
                    kia.Name = "Kia";
                    EFUtils.SaveEntity(kia, session);

                    Car rover = new Car();
                    EFUtils.InitializeEntityForThisDemo(rover);
                    rover.Name = "Rover";
                    EFUtils.SaveEntity(rover, session);

                    Car audi = new Car();
                    EFUtils.InitializeEntityForThisDemo(audi);
                    audi.Name = "Audi";
                    EFUtils.SaveEntity(audi, session);

                    Car bmw = new Car();
                    EFUtils.InitializeEntityForThisDemo(bmw);
                    bmw.Name = "Bmw";
                    EFUtils.SaveEntity(bmw, session);

                    Driver john = new Driver();
                    EFUtils.InitializeEntityForThisDemo(john);
                    john.Name = "John";
                    EFUtils.SaveEntity(john, session);

                    Driver peter = new Driver();
                    EFUtils.InitializeEntityForThisDemo(peter);
                    peter.Name = "Peter";
                    EFUtils.SaveEntity(peter, session);

                    Driver zoltan = new Driver();
                    EFUtils.InitializeEntityForThisDemo(zoltan);
                    zoltan.Name = "Zoltan";
                    EFUtils.SaveEntity(zoltan, session);

                    // John drives the mazda, kia and rover
                    ISet<Car> cars = new HashSet<Car>();
                    cars.Add(mazda);
                    cars.Add(kia);
                    john.Cars = cars;
                    EFUtils.SaveEntity(john, session);

                    // Peter drivers the honda, kia and audi
                    cars = new HashSet<Car>();
                    cars.Add(honda);
                    cars.Add(kia);
                    cars.Add(audi);
                    peter.Cars = cars;
                    EFUtils.SaveEntity(john, session);

                    // Zoltan drivers all of the cars
                    cars = new HashSet<Car>();
                    cars.Add(mazda);
                    cars.Add(honda);
                    cars.Add(kia);
                    cars.Add(rover);
                    cars.Add(audi);
                    cars.Add(bmw);
                    zoltan.Cars = cars;
                    EFUtils.SaveEntity(john, session);

                    ISet<Driver> drivers = mazda.Drivers;
                    drivers.Add(john);
                    drivers.Add(zoltan);
                    mazda.Drivers = drivers;
                    EFUtils.SaveEntity(mazda, session);

                    drivers = honda.Drivers;
                    drivers.Add(peter);
                    drivers.Add(zoltan);
                    honda.Drivers = drivers;
                    EFUtils.SaveEntity(honda, session);

                    drivers = kia.Drivers;
                    drivers.Add(john);
                    drivers.Add(peter);
                    drivers.Add(zoltan);
                    kia.Drivers = drivers;
                    EFUtils.SaveEntity(kia, session);

                    drivers = rover.Drivers;
                    drivers.Add(zoltan);
                    rover.Drivers = drivers;
                    EFUtils.SaveEntity(rover, session);

                    drivers = audi.Drivers;
                    drivers.Add(peter);
                    drivers.Add(zoltan);
                    audi.Drivers = drivers;
                    EFUtils.SaveEntity(audi, session);

                    drivers = bmw.Drivers;
                    drivers.Add(zoltan);
                    bmw.Drivers = drivers;
                    EFUtils.SaveEntity(bmw, session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<Driver>();
                    dc.Add(Restrictions.Eq("name", "Zoltan"));
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<Driver> drivers = criteria.List<Driver>();
                    Assert.IsTrue(drivers != null);
                    Assert.IsTrue(drivers.Count == 1); // there is only one driver with name "Zoltan" exists in database
                    Assert.IsTrue(drivers[0].Cars.Count == 6); // three cars assigned

                    dc = DetachedCriteria.For<Driver>();
                    dc.Add(Restrictions.Eq("name", "Peter"));
                    criteria = dc.GetExecutableCriteria(session);
                    drivers = criteria.List<Driver>();
                    Assert.IsTrue(drivers != null);
                    Assert.IsTrue(drivers.Count == 1); // there is only one driver with name "Peter" exists in database
                    Assert.IsTrue(drivers[0].Cars.Count == 3); // three cars assigned

                    dc = DetachedCriteria.For<Driver>();
                    dc.Add(Restrictions.Eq("name", "John"));
                    criteria = dc.GetExecutableCriteria(session);
                    drivers = criteria.List<Driver>();
                    Assert.IsTrue(drivers != null);
                    Assert.IsTrue(drivers.Count == 1); // there is only one driver with name "John" exists in database
                    Assert.IsTrue(drivers[0].Cars.Count == 2); // two cars assigned

                    dc = DetachedCriteria.For<Car>();
                    dc.Add(Restrictions.Eq("name", "Mazda"));
                    criteria = dc.GetExecutableCriteria(session);
                    IList<Car> cars = criteria.List<Car>();
                    Assert.IsTrue(cars != null);
                    Assert.IsTrue(cars.Count == 1); // there is only one car with name "Mazda" exists in database
                    Assert.IsTrue(cars[0].Drivers.Count == 2); // two drivers assigned

                    dc = DetachedCriteria.For<Car>();
                    dc.Add(Restrictions.Eq("name", "Honda"));
                    criteria = dc.GetExecutableCriteria(session);
                    cars = criteria.List<Car>();
                    Assert.IsTrue(cars != null);
                    Assert.IsTrue(cars.Count == 1); // there is only one car with name "Mazda" exists in database
                    Assert.IsTrue(cars[0].Drivers.Count == 2); // two drivers assigned

                    dc = DetachedCriteria.For<Car>();
                    dc.Add(Restrictions.Eq("name", "Kia"));
                    criteria = dc.GetExecutableCriteria(session);
                    cars = criteria.List<Car>();
                    Assert.IsTrue(cars != null);
                    Assert.IsTrue(cars.Count == 1); // there is only one car with name "Mazda" exists in database
                    Assert.IsTrue(cars[0].Drivers.Count == 3); // three drivers assigned

                    dc = DetachedCriteria.For<Car>();
                    dc.Add(Restrictions.Eq("name", "Rover"));
                    criteria = dc.GetExecutableCriteria(session);
                    cars = criteria.List<Car>();
                    Assert.IsTrue(cars != null);
                    Assert.IsTrue(cars.Count == 1); // there is only one car with name "Rover" exists in database
                    Assert.IsTrue(cars[0].Drivers.Count == 1); // one driver assigned

                    dc = DetachedCriteria.For<Car>();
                    dc.Add(Restrictions.Eq("name", "Audi"));
                    criteria = dc.GetExecutableCriteria(session);
                    cars = criteria.List<Car>();
                    Assert.IsTrue(cars != null);
                    Assert.IsTrue(cars.Count == 1); // there is only one car with name "Audi" exists in database
                    Assert.IsTrue(cars[0].Drivers.Count == 2); // two drivers assigned

                    dc = DetachedCriteria.For<Car>();
                    dc.Add(Restrictions.Eq("name", "Bmw"));
                    criteria = dc.GetExecutableCriteria(session);
                    cars = criteria.List<Car>();
                    Assert.IsTrue(cars != null);
                    Assert.IsTrue(cars.Count == 1); // there is only one car with name "Bmw" exists in database
                    Assert.IsTrue(cars[0].Drivers.Count == 1); // one driver assigned

                    transaction.Rollback();
                }
            }
        }

Download the Many-To-Many Bidirectional Visual Studio 2010 example project:

Kategória: NHibernate | Címke: , , , , , , , | Megjegyzés hozzáfűzése

NHibernate Many-To-Many mapping, Composite Identifier, Unidirectional relationship

Next topic is the Many-To-Many mapping in NHibernate. I will declare the relations as the same way as done it at my previous article: Code First and XML Mapping. Each approaches are right, you can choose the solution which is the best for your project.

For the presentation I have defined two entities:

  • Order
  • Product

Order entity holds the references of the Product entities. Products may belongs to several Orders, but the Product entity instance does not know anything about the Order. This is an Unidirectional relationship.

DomainModel.ManyToManyUni

The Product entity class code:

    [Serializable]
    [Class(Table = "Products")]
    public class Product : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        public Product()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

    }

The Order entity class code:

    [Serializable]
    [Class(Table = "Orders")]
    public class Order : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "products", Cascade = "none", Generic = true, Lazy = CollectionLazy.True, Table = "Orders_Products_Switch")]
        [Key(1)]
        [Column(2, Name = "order_restId")]
        [Column(3, Name = "order_deviceId")]
        [Column(4, Name = "order_id")]
        [ManyToMany(5, NotFound = NotFoundMode.Exception, ClassType = typeof(Product))]
        [Column(6, Name = "product_restId")]
        [Column(7, Name = "product_deviceId")]
        [Column(8, Name = "product_id")]
        private ISet<Product> products = new HashSet<Product>();

        public Order()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        [DebuggerHidden]
        public virtual ISet<Product> Products
        {
            get { return new HashSet<Product>(products); }
            set
            {
                if (value == null)
                {
                    ThrowHelper.ThrowArgumentNullException("value");
                }

                OnPropertyChanging("Products");
                products = new HashSet<Product>(value);
                OnPropertyChanged("Products");
            }
        }

    }

This is the mapping XML:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateDemo.DomainModel.ManyToManyUniDirectional, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  
  <class table="Orders" name="NHibernateDemo.DomainModel.ManyToManyUniDirectional.Order, NHibernateDemo.DomainModel.ManyToManyUniDirectional">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
    
    <set name="products" table="Orders_Products_Switch" lazy="true" cascade="none" generic="true">
      <key>
        <column name="order_restId" />
        <column name="order_deviceId" />
        <column name="order_id" />
      </key>
      <many-to-many class="NHibernateDemo.DomainModel.ManyToManyUniDirectional.Product, NHibernateDemo.DomainModel.ManyToManyUniDirectional" not-found="exception">
        <column name="product_restId" />
        <column name="product_deviceId" />
        <column name="product_id" />
      </many-to-many>
    </set>
  </class>
  
  <class table="Products" name="NHibernateDemo.DomainModel.ManyToManyUniDirectional.Product, NHibernateDemo.DomainModel.ManyToManyUniDirectional">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>
  
</hibernate-mapping>

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has a composite identifier as complex primary key.

Database.ManyToManyUni

Here is an example, how to create and save entities:

        [TestMethod]
        public void TestManyToManyUni()
        {
            // create the entities
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Product breed = new Product();
                    EFUtils.InitializeEntityForThisDemo(breed);
                    breed.Name = "Breed";
                    EFUtils.SaveEntity(breed, session);

                    Product butter = new Product();
                    EFUtils.InitializeEntityForThisDemo(butter);
                    butter.Name = "Butter";
                    EFUtils.SaveEntity(butter, session);

                    Product cheese = new Product();
                    EFUtils.InitializeEntityForThisDemo(cheese);
                    cheese.Name = "Cheese";
                    EFUtils.SaveEntity(cheese, session);

                    Product beer = new Product();
                    EFUtils.InitializeEntityForThisDemo(beer);
                    beer.Name = "Beer";
                    EFUtils.SaveEntity(beer, session);

                    ISet<Product> products = new HashSet<Product>();
                    UniDirectional.Order orderA = new UniDirectional.Order();
                    EFUtils.InitializeEntityForThisDemo(orderA);
                    orderA.Name = "John's order";
                    products.Add(breed);
                    products.Add(butter);
                    orderA.Products = products;
                    EFUtils.SaveEntity(orderA, session);

                    products = new HashSet<Product>();
                    UniDirectional.Order orderB = new UniDirectional.Order();
                    EFUtils.InitializeEntityForThisDemo(orderB);
                    orderB.Name = "Tom's order";
                    products.Add(butter);
                    products.Add(cheese);
                    products.Add(beer);
                    orderB.Products = products;
                    EFUtils.SaveEntity(orderB, session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<Product>();
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<Product> products = criteria.List<Product>();
                    Assert.IsTrue(products != null);
                    Assert.IsTrue(products.Count == 4); // there are four products exists in database

                    dc = DetachedCriteria.For<UniDirectional.Order>();
                    dc.Add(Restrictions.Eq("name", "John's order"));
                    criteria = dc.GetExecutableCriteria(session);
                    IList<UniDirectional.Order> orders = criteria.List<UniDirectional.Order>();
                    Assert.IsTrue(orders != null);
                    Assert.IsTrue(orders.Count == 1); // there is only one order with name "John's order" exists in database
                    Assert.IsTrue(orders[0].Products.Count == 2); // two products assigned

                    dc = DetachedCriteria.For<UniDirectional.Order>();
                    dc.Add(Restrictions.Eq("name", "Tom's order"));
                    criteria = dc.GetExecutableCriteria(session);
                    orders = criteria.List<UniDirectional.Order>();
                    Assert.IsTrue(orders != null);
                    Assert.IsTrue(orders.Count == 1); // there is only one order with name "Tom's order" exists in database
                    Assert.IsTrue(orders[0].Products.Count == 3); // three products assigned

                }
            }
        }

Download the Many-To-Many Unidirectional Visual Studio 2010 example project:

Kategória: NHibernate | Címke: , , , , , , , | Megjegyzés hozzáfűzése

NHibernate Any mapping, Composite Identifier, Unidirectional relationship

Next topic is the Any mapping in NHibernate. I will declare the relations as the same way as done it at my previous article: Code First and XML Mapping. Each approaches are right, you can choose the solution which is the best for your project.

For the presentation I have defined four entities:

  • House
  • Human
  • Cat
  • Dog

House entity holds the reference of the joined entity. This is an unidirectional relationship between the entities.

DomainModel.Any

The House entity class code:

    [Serializable]
    [Class(Table = "Houses")]
    public class House : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Any(0, Name = "something", IdTypeType = typeof(EntityId), MetaTypeType = typeof(string), Cascade = "none")]
        [MetaValue(1, ClassType = typeof(Human), Value = "This_is_a_human")]
        [MetaValue(2, ClassType = typeof(Cat), Value = "This_is_a_cat")]
        [MetaValue(3, ClassType = typeof(Dog), Value = "This_is_a_dog")]
        [Column(4, Name = "somethingType")]
        [Column(5, Name = "somethingId")]
        private EntityBase something = null;

        public House()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        [DebuggerHidden]
        public virtual EntityBase Something
        {
            get { return something; }
            set
            {
                OnPropertyChanging("Something");
                something = value;
                OnPropertyChanged("Something");
            }
        }

    }

The Human entity class code:

    [Serializable]
    [Class(Table = "Humans")]
    public class Human : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        public Human()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

    }

The Cat entity class code:

    [Serializable]
    [Class(Table = "Cats")]
    public class Cat : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        public Cat()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

    }

The Dog entity class code:

    [Serializable]
    [Class(Table = "Dogs")]
    public class Dog : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        public Dog()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

    }

Here is the mapping XML:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateDemo.DomainModel.Any, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  
  <class table="Houses" name="NHibernateDemo.DomainModel.Any.House, NHibernateDemo.DomainModel.Any">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
    
    <any id-type="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" meta-type="String" name="something" cascade="none">
      <meta-value value="This_is_a_human" class="NHibernateDemo.DomainModel.Any.Human, NHibernateDemo.DomainModel.Any" />
      <meta-value value="This_is_a_cat" class="NHibernateDemo.DomainModel.Any.Cat, NHibernateDemo.DomainModel.Any" />
      <meta-value value="This_is_a_dog" class="NHibernateDemo.DomainModel.Any.Dog, NHibernateDemo.DomainModel.Any" />
      <column name="somethingType" />
      <column name="somethingId" />
    </any>
  </class>
  
  <class table="Cats" name="NHibernateDemo.DomainModel.Any.Cat, NHibernateDemo.DomainModel.Any">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>
  
  <class table="Dogs" name="NHibernateDemo.DomainModel.Any.Dog, NHibernateDemo.DomainModel.Any">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>
  
  <class table="Humans" name="NHibernateDemo.DomainModel.Any.Human, NHibernateDemo.DomainModel.Any">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>
  
</hibernate-mapping>

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has a composite identifier as complex primary key.

Database.Any

Here is an example, how to create and save entities:

       [TestMethod]
        public void TestAnyRelations()
        {
            // create the entities
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Cat cat = new Cat();
                    EFUtils.InitializeEntityForThisDemo(cat);
                    cat.Name = "Cat";
                    EFUtils.SaveEntity(cat, session);

                    Dog dog = new Dog();
                    EFUtils.InitializeEntityForThisDemo(dog);
                    dog.Name = "Dog";
                    EFUtils.SaveEntity(dog, session);

                    Human human = new Human();
                    EFUtils.InitializeEntityForThisDemo(human);
                    human.Name = "Human";
                    EFUtils.SaveEntity(human, session);

                    House house = new House();
                    EFUtils.InitializeEntityForThisDemo(house);
                    house.Name = "JZO";
                    house.Something = cat;
                    EFUtils.SaveEntity(house, session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<House>();
                    dc.Add(Restrictions.Eq("name", "JZO"));
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<House> houses = criteria.List<House>();
                    Assert.IsTrue(houses != null);
                    Assert.IsTrue(houses.Count == 1); // there is one house exists in database
                    Assert.IsTrue(houses[0].Something != null);
                    Assert.IsTrue(houses[0].Something is Cat);

                    dc = DetachedCriteria.For<Dog>();
                    dc.Add(Restrictions.Eq("name", "Dog"));
                    criteria = dc.GetExecutableCriteria(session);
                    IList<Dog> dogs = criteria.List<Dog>();
                    Assert.IsTrue(dogs != null);
                    Assert.IsTrue(dogs.Count == 1); // there is one house exists in database

                    houses[0].Something = dogs[0];
                    EFUtils.SaveEntity(houses[0], session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<House>();
                    dc.Add(Restrictions.Eq("name", "JZO"));
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<House> houses = criteria.List<House>();
                    Assert.IsTrue(houses != null);
                    Assert.IsTrue(houses.Count == 1); // there is one house exists in database
                    Assert.IsTrue(houses[0].Something != null);
                    Assert.IsTrue(houses[0].Something is Dog);

                    dc = DetachedCriteria.For<Human>();
                    dc.Add(Restrictions.Eq("name", "Human"));
                    criteria = dc.GetExecutableCriteria(session);
                    IList<Human> humans = criteria.List<Human>();
                    Assert.IsTrue(humans != null);
                    Assert.IsTrue(humans.Count == 1); // there is one house exists in database

                    houses[0].Something = humans[0];
                    EFUtils.SaveEntity(houses[0], session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<House>();
                    dc.Add(Restrictions.Eq("name", "JZO"));
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<House> houses = criteria.List<House>();
                    Assert.IsTrue(houses != null);
                    Assert.IsTrue(houses.Count == 1); // there is one house exists in database
                    Assert.IsTrue(houses[0].Something != null);
                    Assert.IsTrue(houses[0].Something is Human);

                    transaction.Rollback();
                }
            }

        }

Download the Any Visual Studio 2010 example project:

Kategória: NHibernate | Címke: , , , , , , , | Megjegyzés hozzáfűzése

NHibernate Many-To-Any mapping, Composite Identifier, Unidirectional relationship

Next topic is the Many-To-Any mapping in NHibernate. I will declare the relations as the same way as done it at my previous article: Code First and XML Mapping. Each approaches are right, you can choose the solution which is the best for your project.

For the presentation I have defined five entities:

  • ShoppingBasket
  • Fruit
  • Apple
  • Peach
  • Banana

ShoppingBasket contains any type of Fruit: Apple, Peach and Banana. This is an unidirectional relationship between the entities.

DomainModel.ManyToAny

The ShoppingBasket entity class code:

    [Serializable]
    [Class(Table = "ShoppingBaskets")]
    public class ShoppingBasket : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "fruits", Generic = true, Lazy = CollectionLazy.True, Cascade = "none", Table = "ShoppingBasket_Fruits_Switch")]
        [Key(1)]
        [Column(2, Name = "shoppingBasket_systemId")]
        [Column(3, Name = "shoppingBasket_deviceId")]
        [Column(4, Name = "shoppingBasket_id")]
        [ManyToAny(5, IdTypeType = typeof(EntityId), MetaTypeType = typeof(string))]
        [MetaValue(6, ClassType = typeof(Apple), Value = "This_is_an_apple")]
        [MetaValue(7, ClassType = typeof(Peach), Value = "This_is_a_peach")]
        [MetaValue(8, ClassType = typeof(Banana), Value = "This_is_a_banana")]
        [Column(9, Name = "fruitType")]
        [Column(10, Name = "fruitId")]
        private ISet<Fruit> fruits = new HashSet<Fruit>();

        public ShoppingBasket()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual ISet<Fruit> Fruits
        {
            get { return new HashSet<Fruit>(fruits); }
            set
            {
                if (value == null)
                {
                    ThrowHelper.ThrowArgumentNullException("Fruits");
                }

                OnPropertyChanging("Fruits");
                fruits = new HashSet<Fruit>(value);
                OnPropertyChanged("Fruits");
            }
        }

    }

The Fruit entity class code:

    [Serializable]
    [Class(Abstract = true)]
    public abstract class Fruit : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string qrCode = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [ManyToOne(0, Name = "shoppingBasket", Cascade = "none")]
        [Column(1, Name = "shoppingBasket_systemId")]
        [Column(2, Name = "shoppingBasket_deviceId")]
        [Column(3, Name = "shoppingBasket_id")]
        private ShoppingBasket shoppingBasket = null;

        protected Fruit()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string QrCode
        {
            get { return qrCode; }
            set
            {
                OnPropertyChanging("QrCode");
                qrCode = value;
                OnPropertyChanged("QrCode");
            }
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        [DebuggerHidden]
        public virtual ShoppingBasket ShoppingBasket
        {
            get { return shoppingBasket; }
            set
            {
                OnPropertyChanging("ShoppingBasket");
                shoppingBasket = value;
                OnPropertyChanged("ShoppingBasket");
            }
        }

    }

The Apple entity class:

    [Serializable]
    [UnionSubclass(Table = "Apples", ExtendsType = typeof(Fruit))]
    public class Apple : Fruit
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = false)]
        private string appleType = string.Empty;

        public Apple()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string AppleType
        {
            get { return appleType; }
            set
            {
                OnPropertyChanging("AppleType");
                appleType = value;
                OnPropertyChanged("AppleType");
            }
        }

    }

The Banana entity class:

    [Serializable]
    [UnionSubclass(Table = "Bananas", ExtendsType = typeof(Fruit))]
    public class Banana : Fruit
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true, Precision = 10, Scale = 2)]
        private decimal mass = 0;

        public Banana()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual decimal Mass
        {
            get { return mass; }
            set
            {
                OnPropertyChanging("Mass");
                mass = value;
                OnPropertyChanged("Mass");
            }
        }

    }

The Peach entity class:

    [Serializable]
    [UnionSubclass(Table = "Peaches", ExtendsType = typeof(Fruit))]
    public class Peach : Fruit
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = false)]
        private string peachType = string.Empty;

        public Peach()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string PeachType
        {
            get { return peachType; }
            set
            {
                OnPropertyChanging("PeachType");
                peachType = value;
                OnPropertyChanged("PeachType");
            }
        }

    }

Here is the mapping XML:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateDemo.DomainModel.ManyToAny, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  
  <class table="ShoppingBaskets" name="NHibernateDemo.DomainModel.ManyToAny.ShoppingBasket, NHibernateDemo.DomainModel.ManyToAny">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
    
    <set name="fruits" table="ShoppingBasket_Fruits_Switch" lazy="true" cascade="none" generic="true">
      <key>
        <column name="shoppingBasket_systemId" />
        <column name="shoppingBasket_deviceId" />
        <column name="shoppingBasket_id" />
      </key>
      <many-to-any id-type="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" meta-type="String">
        <meta-value value="This_is_an_apple" class="NHibernateDemo.DomainModel.ManyToAny.Apple, NHibernateDemo.DomainModel.ManyToAny" />
        <meta-value value="This_is_a_peach" class="NHibernateDemo.DomainModel.ManyToAny.Peach, NHibernateDemo.DomainModel.ManyToAny" />
        <meta-value value="This_is_a_banana" class="NHibernateDemo.DomainModel.ManyToAny.Banana, NHibernateDemo.DomainModel.ManyToAny" />
        <column name="fruitType" />
        <column name="fruitId" />
      </many-to-any>
    </set>
    
  </class>
  
  <class abstract="true" name="NHibernateDemo.DomainModel.ManyToAny.Fruit, NHibernateDemo.DomainModel.ManyToAny">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="qrCode" not-null="true" />
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <many-to-one name="shoppingBasket" cascade="none">
      <column name="shoppingBasket_systemId" />
      <column name="shoppingBasket_deviceId" />
      <column name="shoppingBasket_id" />
    </many-to-one>
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
    
  </class>
  
  <union-subclass table="Bananas" extends="NHibernateDemo.DomainModel.ManyToAny.Fruit, NHibernateDemo.DomainModel.ManyToAny" name="NHibernateDemo.DomainModel.ManyToAny.Banana, NHibernateDemo.DomainModel.ManyToAny">
    <property name="mass" precision="10" scale="2" not-null="true" />
  </union-subclass>
  
  <union-subclass table="Peaches" extends="NHibernateDemo.DomainModel.ManyToAny.Fruit, NHibernateDemo.DomainModel.ManyToAny" name="NHibernateDemo.DomainModel.ManyToAny.Peach, NHibernateDemo.DomainModel.ManyToAny">
    <property name="peachType" not-null="false" />
  </union-subclass>
  
  <union-subclass table="Apples" extends="NHibernateDemo.DomainModel.ManyToAny.Fruit, NHibernateDemo.DomainModel.ManyToAny" name="NHibernateDemo.DomainModel.ManyToAny.Apple, NHibernateDemo.DomainModel.ManyToAny">
    <property name="appleType" not-null="false" />
  </union-subclass>
  
</hibernate-mapping>

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has a composite identifier as complex primary key.

Database.ManyToAny

Here is an example, how to create and save entities:

        [TestMethod]
        public void TestManyToAny()
        {
            // create the entities
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Apple apple = new Apple();
                    EFUtils.InitializeEntityForThisDemo(apple);
                    apple.AppleType = "Jonatan";
                    apple.Name = "Apple";
                    apple.QrCode = "123";
                    EFUtils.SaveEntity(apple, session);

                    Banana banana = new Banana();
                    EFUtils.InitializeEntityForThisDemo(banana);
                    banana.Name = "Banana";
                    banana.Mass = 1m;
                    banana.QrCode = "679";
                    EFUtils.SaveEntity(banana, session);

                    Peach peach = new Peach();
                    EFUtils.InitializeEntityForThisDemo(peach);
                    peach.Name = "Peach";
                    peach.PeachType = "Apricot";
                    peach.QrCode = "4443";
                    EFUtils.SaveEntity(peach, session);

                    ShoppingBasket basket = new ShoppingBasket();
                    EFUtils.InitializeEntityForThisDemo(basket);
                    ISet<Fruit> fruits = new HashSet<Fruit>();
                    fruits.Add(apple);
                    fruits.Add(banana);
                    fruits.Add(peach);
                    basket.Fruits = fruits;
                    EFUtils.SaveEntity(basket, session);

                    apple.ShoppingBasket = basket;
                    banana.ShoppingBasket = basket;
                    peach.ShoppingBasket = basket;
                    EFUtils.SaveEntity(apple, session);
                    EFUtils.SaveEntity(banana, session);
                    EFUtils.SaveEntity(peach, session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<ShoppingBasket>();
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<ShoppingBasket> baskets = criteria.List<ShoppingBasket>();
                    Assert.IsTrue(baskets != null);
                    Assert.IsTrue(baskets.Count == 1); // there is only one basket exists in database
                    Assert.IsTrue(baskets[0].Fruits.Count == 3); // three fruits assigned

                    dc = DetachedCriteria.For<Fruit>();
                    dc.Add(Restrictions.Not(Restrictions.IsNull("shoppingBasket")));
                    criteria = dc.GetExecutableCriteria(session);
                    IList<Fruit> fruits = criteria.List<Fruit>();
                    Assert.IsTrue(fruits != null);
                    Assert.IsTrue(fruits.Count == 3);
                    Assert.IsTrue(fruits[0].ShoppingBasket != null);
                    Assert.IsTrue(fruits[0].ShoppingBasket.Equals(baskets[0]));
                    Assert.IsTrue(fruits[1].ShoppingBasket != null);
                    Assert.IsTrue(fruits[1].ShoppingBasket.Equals(baskets[0]));
                    Assert.IsTrue(fruits[2].ShoppingBasket != null);
                    Assert.IsTrue(fruits[2].ShoppingBasket.Equals(baskets[0]));

                    transaction.Rollback();
                }
            }

        }

Download the Many-To-Any Visual Studio 2010 example project:

Kategória: NHibernate | Címke: , , , , , , , | Megjegyzés hozzáfűzése

NHibernate One-To-Many mapping, Composite Identifier, Bidirectional relationship

Next topic is the One-To-Many mapping in NHibernate. I will declare the relations as the same way as done it at my previous article: Code First and XML Mapping. Each approaches are right, you can choose the solution which is the best for your project.

For the presentation I have defined two entities:

  • Child
  • Mother

Each Mother have zero or more Children, but a Child belongs to only one Mother. Each side have reference points to the other side, so this is an bidirectional relationship between the entities.

Relations of the entities

The Child entity class code:

    [Serializable]
    [Class(Table = "Children")]
    public class Child : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [ManyToOne(0, Name = "mother", Cascade = "none")]
        [Column(1, Name = "mother_restId")]
        [Column(2, Name = "mother_deviceId")]
        [Column(3, Name = "mother_id")]
        private Mother mother = null;

        public Child()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        [DebuggerHidden]
        public virtual Mother Mother
        {
            get { return mother; }
            set
            {
                OnPropertyChanging("Mother");
                mother = value;
                OnPropertyChanged("Mother");
            }
        }

    }

The Mother entity class:

    [Serializable]
    [Class(Table = "Mothers")]
    public class Mother : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Set(0, Name = "children", Generic = true, Lazy = CollectionLazy.True, Cascade = "none")]
        [Key(1)]
        [Column(2, Name = "child_restId")]
        [Column(3, Name = "child_deviceId")]
        [Column(4, Name = "child_id")]
        [OneToMany(5, NotFound = NotFoundMode.Exception, ClassType = typeof(Child))]
        private ISet children = new HashSet();

        public Mother()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        /// NOTE: the Children property content is immutable.
        /// You should have to get the collection, modify it than re-set across the this property.
        /// The main reason for this solution, you cannot change the content of the set, without property
        /// notification. This is useful, if you bind the property to somewhere.
        [DebuggerHidden]
        public virtual ISet Children
        {
            get { return new HashSet(children); }
            set
            {
                if (value == null)
                {
                    ThrowHelper.ThrowArgumentNullException("value");
                }

                OnPropertyChanging("Children");
                children = new HashSet(value);
                OnPropertyChanged("Children");
            }
        }

    }

As you can see in the Children property comment there is a problem with NHibernate version 3.3.2.4000 GA. If you declare an entity which inherited from Child entity, add it to the Children set than save the mother entity and you will get an exception. This is a known issue in NHibernate, to track this problem please visit this Jira.

https://nhibernate.jira.com/browse/NH-3380

Here is the mapping XML:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateDemo.DomainModel.OneToMany, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  
  <class table="Children" name="NHibernateDemo.DomainModel.OneToMany.Child, NHibernateDemo.DomainModel.OneToMany">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <many-to-one name="mother" cascade="none">
      <column name="mother_restId" />
      <column name="mother_deviceId" />
      <column name="mother_id" />
    </many-to-one>
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>
  
  <class table="Mothers" name="NHibernateDemo.DomainModel.OneToMany.Mother, NHibernateDemo.DomainModel.OneToMany">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
    
    <set name="children" lazy="true" cascade="none" generic="true">
      <key>
        <column name="child_restId" />
        <column name="child_deviceId" />
        <column name="child_id" />
      </key>
      <one-to-many class="NHibernateDemo.DomainModel.OneToMany.Child, NHibernateDemo.DomainModel.OneToMany" not-found="exception" />
    </set>
  </class>
  
</hibernate-mapping>

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has a composite identifier as complex primary key.

Database.One-To-Many

Here is an example, how to create and save entities:

        public void TestOneToMany()
        {
            // create the entities
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Mother victoria = new Mother();
                    EFUtils.InitializeEntityForThisDemo(victoria);
                    victoria.Name = "Victoria";
                    EFUtils.SaveEntity(victoria, session);

                    Child tom = new Child();
                    EFUtils.InitializeEntityForThisDemo(tom);
                    tom.Name = "Tom";
                    EFUtils.SaveEntity(tom, session);

                    Child sue = new Child();
                    EFUtils.InitializeEntityForThisDemo(sue);
                    sue.Name = "Sue";
                    EFUtils.SaveEntity(sue, session);

                    // each mother may have several children, but each children belongs to one mother
                    ISet children = new HashSet();
                    children.Add(tom);
                    children.Add(sue);
                    victoria.Children = children;
                    EFUtils.SaveEntity(victoria, session);

                    tom.Mother = victoria;
                    sue.Mother = victoria;
                    EFUtils.SaveEntity(tom, session);
                    EFUtils.SaveEntity(sue, session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For();
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList mothers = criteria.List();
                    Assert.IsTrue(mothers != null);
                    Assert.IsTrue(mothers.Count == 1); // there is only one mother exists in database
                    Assert.IsTrue(mothers[0].Children.Count == 2); // two children assigned

                    dc = DetachedCriteria.For();
                    dc.Add(Restrictions.Not(Restrictions.IsNull("mother")));
                    criteria = dc.GetExecutableCriteria(session);
                    IList children = criteria.List();
                    Assert.IsTrue(children != null);
                    Assert.IsTrue(children.Count == 2);
                    Assert.IsTrue(children[0].Mother != null);
                    Assert.IsTrue(children[0].Mother.Equals(mothers[0]));
                    Assert.IsTrue(children[1].Mother != null);
                    Assert.IsTrue(children[1].Mother.Equals(mothers[0]));

                    transaction.Rollback();
                }
            }

        }

Download the One-To-Many Visual Studio 2010 example project:

Kategória: NHibernate | Címke: , , , , , , | Megjegyzés hozzáfűzése

NHibernate Many-To-One mapping, Composite Identifier, Unidirectional relationship

Next topic is the Many-To-One mapping in NHibernate. I will declare the relations as the same way as done it at my previous article: Code First and XML Mapping. Each approaches are right, you can choose the solution which is the best for your project.

For the presentation I have defined two entities:

  • Address
  • Country

Each Address has a Country (optionally), but the Country may belongs to several Address. Country does not reference the Address entity, so this is an Unidirectional relationship between these entities.

Relations of the entities

The Country entity class code

    [Serializable]
    [Class(Table = "Countries")]
    public class Country : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true, Unique = true)]
        private string name = string.Empty;

        public Country()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

    }

The Address entity class code:

    [Serializable]
    [Class(Table = "Addresses")]
    public class Address : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = false)]
        private string addressInfo = string.Empty;

        // the country is mandatory, because the NotNull = true.
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [ManyToOne(0, Name = "country", ClassType = typeof(Country), Cascade = "none", NotNull = true)]
        [Column(1, Name = "country_restId")]
        [Column(2, Name = "country_deviceId")]
        [Column(3, Name = "country_id")]
        private Country country = null;

        public Address()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string AddressInfo
        {
            get { return addressInfo; }
            set
            {
                OnPropertyChanging("AddressInfo");
                addressInfo = value;
                OnPropertyChanged("AddressInfo");
            }
        }

        [DebuggerHidden]
        public virtual Country Country
        {
            get { return country; }
            set
            {
                OnPropertyChanging("Country");
                country = value;
                OnPropertyChanged("Country");
            }
        }

    }

Mapping XML:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateDemo.DomainModel.ManyToOne, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">

  <class table="Countries" name="NHibernateDemo.DomainModel.ManyToOne.Country, NHibernateDemo.DomainModel.ManyToOne">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>

    <property name="name" not-null="true" unique="true" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />

    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>

  <class table="Addresses" name="NHibernateDemo.DomainModel.ManyToOne.Address, NHibernateDemo.DomainModel.ManyToOne">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>

    <property name="addressInfo" not-null="false" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />

    <many-to-one name="country" class="NHibernateDemo.DomainModel.ManyToOne.Country, NHibernateDemo.DomainModel.ManyToOne" not-null="true" cascade="none">
      <column name="country_restId" />
      <column name="country_deviceId" />
      <column name="country_id" />
    </many-to-one>

    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>

</hibernate-mapping>

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has a composite identifier as complex primary key.

Database Schema

Here is an example, how to create and save entities:

        [TestMethod]
        public void TestManyToOne()
        {
            // create countries and addresses
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Country countryHun = new Country();
                    EFUtils.InitializeEntityForThisDemo(countryHun);
                    countryHun.Name = "Hungary";
                    EFUtils.SaveEntity(countryHun, session);

                    Country countryGer = new Country();
                    EFUtils.InitializeEntityForThisDemo(countryGer);
                    countryGer.Name = "Germany";
                    EFUtils.SaveEntity(countryGer, session);

                    Country countryEng = new Country();
                    EFUtils.InitializeEntityForThisDemo(countryEng);
                    countryEng.Name = "United Kingdom";
                    EFUtils.SaveEntity(countryEng, session);

                    Address add1 = new Address();
                    EFUtils.InitializeEntityForThisDemo(add1);
                    add1.AddressInfo = "Szirmabesenyo";
                    add1.Country = countryHun;
                    EFUtils.SaveEntity(add1, session);

                    Address add2 = new Address();
                    EFUtils.InitializeEntityForThisDemo(add2);
                    add2.AddressInfo = "Miskolc";
                    add2.Country = countryHun;
                    EFUtils.SaveEntity(add2, session);

                    Address add3 = new Address();
                    EFUtils.InitializeEntityForThisDemo(add3);
                    add3.AddressInfo = "Berlin";
                    add3.Country = countryGer;
                    EFUtils.SaveEntity(add3, session);

                    transaction.Commit();
                }
            }

            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<Address>();
                    DetachedCriteria joinDc = dc.CreateCriteria("country", "c", JoinType.InnerJoin);
                    DetachedCriteria eqDc = joinDc.Add(Restrictions.Eq("name", "Hungary"));

                    ICriteria criteria = dc.GetExecutableCriteria(session);

                    IList<Address> resultList = criteria.List<Address>();
                    Assert.IsTrue(resultList != null);
                    Assert.IsTrue(resultList.Count == 2); // there are two addresses in the database which belongs to "Hungary"

                    transaction.Rollback();
                }
            }
        }

Download the Many-To-One Visual Studio 2010 example project:

Kategória: NHibernate | Címke: , , , , , , , , | Megjegyzés hozzáfűzése

NHibernate One-To-One mapping, Composite Identifier, Bidirectional relationship

I using Code First and mapping documents too to define the connection between the database tables and the POCOs.

For the presentation I have definied five entities:

  • Person, this is an abstract class for common properties
  • Boy
  • Man
  • Girl
  • Woman

Relations of the entities

The abstract Person entity class code:

    [Serializable]
    [Class(Abstract = true)]
    public abstract class Person : EntityBase
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private string name = string.Empty;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private int age = 0;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = false)]
        private string description = string.Empty;

        protected Person()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual string Name
        {
            get { return name; }
            set
            {
                OnPropertyChanging("Name");
                name = value;
                OnPropertyChanged("Name");
            }
        }

        [DebuggerHidden]
        public virtual int Age
        {
            get { return age; }
            set
            {
                OnPropertyChanging("Age");
                age = value;
                OnPropertyChanged("Age");
            }
        }

        [DebuggerHidden]
        public virtual string Description
        {
            get { return description; }
            set
            {
                OnPropertyChanging("Description");
                description = value;
                OnPropertyChanged("Description");
            }
        }

    }

The Girl entity class code:

    [Serializable]
    [UnionSubclass(Table = "Girls", ExtendsType = typeof(Person))]
    public class Girl : Person
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private bool hasLongHair = false;

        public Girl()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual bool HasLongHair
        {
            get { return hasLongHair; }
            set
            {
                OnPropertyChanging("HasLongHair");
                hasLongHair = value;
                OnPropertyChanged("HasLongHair");
            }
        }

    }

The Woman entity class code:

    [Serializable]
    [UnionSubclass(Table = "Women", ExtendsType = typeof(Girl))]
    public class Woman : Girl
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [OneToOne(Name = "husband", ClassType = typeof(Man), Cascade = "none", Constrained = false, PropertyRef = "wife")]
        private Man husband = null;

        public Woman()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual Man Husband
        {
            get { return husband; }
            set
            {
                OnPropertyChanging("Husband");
                husband = value;
                OnPropertyChanged("Husband");
            }
        }

    }

The Boy entity class code:

    [Serializable]
    [UnionSubclass(Table = "Boys", ExtendsType = typeof(Person))]
    public class Boy : Person
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [Property(NotNull = true)]
        private bool hasMatchBoxes = false;

        [DebuggerHidden]
        public virtual bool HasMatchBoxes
        {
            get { return hasMatchBoxes; }
            set
            {
                OnPropertyChanging("HasMatchBoxes");
                hasMatchBoxes = value;
                OnPropertyChanged("HasMatchBoxes");
            }
        }

    }

The Man entity class code:

    [Serializable]
    [UnionSubclass(Table = "Men", ExtendsType = typeof(Boy))]
    public class Man : Boy
    {

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [ManyToOne(0, Name = "wife", ClassType = typeof(Woman), Cascade = "none", Unique = true)]
        [Column(1, Name = "wife_restId")]
        [Column(2, Name = "wife_deviceId")]
        [Column(3, Name = "wife_id")]
        private Woman wife = null;

        public Man()
            : base()
        {
        }

        [DebuggerHidden]
        public virtual Woman Wife
        {
            get { return wife; }
            set
            {
                OnPropertyChanging("Wife");
                wife = value;
                OnPropertyChanged("Wife");
            }
        }

    }

Mapping XML:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateDemo.DomainModel.OneToOne, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  
  <class abstract="true" name="NHibernateDemo.DomainModel.OneToOne.Person, NHibernateDemo.DomainModel.OneToOne">
    <composite-id class="NHibernateDemo.Shared.EntityId, NHibernateDemo.Shared" name="id">
      <key-property name="systemId" column="systemId" />
      <key-property name="deviceId" column="deviceId" />
      <key-property name="id" column="id" />
    </composite-id>
    
    <property name="name" not-null="true" />
    <property name="age" not-null="true" />
    <property name="description" not-null="false" />
    <property name="entityCreationTime" not-null="true" />
    <property name="entityModificationTime" not-null="true" />
    <property name="deleted" column="isDeleted" not-null="true" />
    
    <component name="version">
      <property name="versionDeviceId" column="versionDeviceId" not-null="true" />
      <property name="versionSeqNumber" column="versionSeqNumber" not-null="true" />
    </component>
  </class>
  
  <union-subclass table="Girls" extends="NHibernateDemo.DomainModel.OneToOne.Person, NHibernateDemo.DomainModel.OneToOne" name="NHibernateDemo.DomainModel.OneToOne.Girl, NHibernateDemo.DomainModel.OneToOne">
    <property name="hasLongHair" not-null="true" />
  </union-subclass>
  
  <union-subclass table="Boys" extends="NHibernateDemo.DomainModel.OneToOne.Person, NHibernateDemo.DomainModel.OneToOne" name="NHibernateDemo.DomainModel.OneToOne.Boy, NHibernateDemo.DomainModel.OneToOne">
    <property name="hasMatchBoxes" not-null="true" />
  </union-subclass>
  
  <union-subclass table="Women" extends="NHibernateDemo.DomainModel.OneToOne.Girl, NHibernateDemo.DomainModel.OneToOne" name="NHibernateDemo.DomainModel.OneToOne.Woman, NHibernateDemo.DomainModel.OneToOne">
    <one-to-one name="husband" class="NHibernateDemo.DomainModel.OneToOne.Man, NHibernateDemo.DomainModel.OneToOne" cascade="none" constrained="false" property-ref="wife" />
  </union-subclass>
  
  <union-subclass table="Men" extends="NHibernateDemo.DomainModel.OneToOne.Boy, NHibernateDemo.DomainModel.OneToOne" name="NHibernateDemo.DomainModel.OneToOne.Man, NHibernateDemo.DomainModel.OneToOne">
    <many-to-one name="wife" class="NHibernateDemo.DomainModel.OneToOne.Woman, NHibernateDemo.DomainModel.OneToOne" unique="true" cascade="none">
      <column name="wife_restId" />
      <column name="wife_deviceId" />
      <column name="wife_id" />
    </many-to-one>
  </union-subclass>
  
</hibernate-mapping>

All entities has a base EntityBase class which contains shared properties for every entities. Every entities has a composite identifier as complex primary key.

Database Shema

Using Union Subclass to join tables, so each table has own properties. This method does not require discriminator column.
Code First attributes definied in the NHibernate.Mapping.Attributes.dll which is a free extension for NHibernate and available to download with NuGet in Visual Studio 2010 and later.

Here is an example, how to create and save entities:

        [TestMethod]
        public void TestOneToOne()
        {
            // create a boy and a girl entity
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Boy boy = new Boy();
                    EFUtils.InitializeEntityForThisDemo(boy);
                    boy.Name = "Little John";
                    boy.Age = 12;
                    EFUtils.SaveEntity(boy, session);

                    Girl girl = new Girl();
                    EFUtils.InitializeEntityForThisDemo(girl);
                    girl.Name = "Mariam";
                    girl.Age = 10;
                    EFUtils.SaveEntity(girl, session);

                    transaction.Commit();
                }
            }

            // create a single woman and man
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    Man boy = new Man();
                    EFUtils.InitializeEntityForThisDemo(boy);
                    boy.Name = "John Doo";
                    boy.Age = 28;
                    EFUtils.SaveEntity(boy, session);

                    Woman girl = new Woman();
                    EFUtils.InitializeEntityForThisDemo(girl);
                    girl.Name = "Sue";
                    girl.Age = 25;
                    EFUtils.SaveEntity(girl, session);

                    transaction.Commit();
                }
            }

            // check database content and make a wedding 😉
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<Boy>();
                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<Boy> resultOfBoys = criteria.List<Boy>();
                    Assert.IsTrue(resultOfBoys != null);
                    Assert.IsTrue(resultOfBoys.Count == 2); // there are a boy and a man in the database

                    dc = DetachedCriteria.For<Man>();
                    criteria = dc.GetExecutableCriteria(session);
                    IList<Man> resultOfMen = criteria.List<Man>();
                    Assert.IsTrue(resultOfMen != null);
                    Assert.IsTrue(resultOfMen.Count == 1); // there are only one man in the database

                    dc = DetachedCriteria.For<Girl>();
                    criteria = dc.GetExecutableCriteria(session);
                    IList<Girl> resultOfGirls = criteria.List<Girl>();
                    Assert.IsTrue(resultOfGirls != null);
                    Assert.IsTrue(resultOfGirls.Count == 2); // there are a girl and a woman in the database

                    dc = DetachedCriteria.For<Woman>();
                    criteria = dc.GetExecutableCriteria(session);
                    IList<Woman> resultOfWomen = criteria.List<Woman>();
                    Assert.IsTrue(resultOfWomen != null);
                    Assert.IsTrue(resultOfWomen.Count == 1); // there are only one woman in the database

                    // make a wedding 😉
                    Man man = resultOfMen[0];
                    Woman woman = resultOfWomen[0];
                    man.Wife = woman;
                    woman.Husband = man;
                    woman.Name = "Mrs. Sue Doo";
                    EFUtils.SaveEntity(man, session);
                    EFUtils.SaveEntity(woman, session);

                    transaction.Commit();
                }
            }

            // check content of the database
            using (ISession session = sessionFactory.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    DetachedCriteria dc = DetachedCriteria.For<Man>();
                    dc.Add(Restrictions.Not(Restrictions.IsNull("wife")));

                    ICriteria criteria = dc.GetExecutableCriteria(session);
                    IList<Man> resultOfMen = criteria.List<Man>();
                    Assert.IsTrue(resultOfMen != null);
                    Assert.IsTrue(resultOfMen.Count == 1); // there are only one married man in the database

                    // NOTE: do not execute query on the One-To-One side, because this field is just a placeholder.
                    // The field 'husband' does not exist in the database, so you can easily get unexpected
                    // results on query.

                    transaction.Rollback();
                }
            }

        }

Download the One-To-One Visual Studio 2010 example project:

Kategória: NHibernate | Címke: , , , , , , , , | Megjegyzés hozzáfűzése