AdFactum ObjectMapper .NET Blog

Official blog of the AdFactum ObjectMapper .NET

Archive for the 'Hint' Category

Best practice for designing compositions, aggregations and associations between entities.

Posted by Gerhard Stephan on 4th February 2008

Designing the data model can be a complex task. Specially if you think about how to bind entities together. You can bind entities together by using an composition, an aggregation or by creating an association between them. But what is the difference? And how does this affect the design of the data model?

In this post, I want to clarify the difference between composition, aggregation and association, and to show the difference of implementation. The definitions are taken from the Wikipedia.org (http://en.wikipedia.org/wiki/Class_diagram#Association)

Definitions:

Composition is a stronger variant of the "has a" or association relationship; composition is more specific than aggregation. Composition has a strong life cycle dependency between instances of the container class and instances of the contained class(es): If the container is destroyed, every instance that it contains is destroyed as well.

Aggregation is a variant of the "has a" or association relationship; aggregation is more specific than association. Aggregation can occur when a class is a collection or container of other classes, but where the contained classes do not have a strong life cycle dependency on the container–essentially, if the container is destroyed, its contents are not.

An Association can be named, and the ends of an association can be adorned with role names, ownership indicators, multiplicity, visibility, and other properties. There are five different types of association. Bi-directional and uni-directional associations are the most common ones. For instance, a flight class is associated with a plane class bi-directionally. Associations can only be shown on class diagrams.

What does this mean to the data model design?

Compositions and Aggregations are the strongest relationships. E.g. if you think of an Employee, the employee owns a work time model. If the employee will be fired, his work time modell will be deleted as well.

In that case you would directly place the work time model into the users entity. This has the advantage, that you can directly deep load the user, change the work model composition and store the changes to database.

    public class Employee : ValueObject

    {

        private WorkTimeModel worktimeModel;

 

        /// <summary>

        /// Gets or sets the worktime model.

        /// </summary>

        /// <value>The worktime model.</value>

        public WorkTimeModel WorktimeModel

        {

            get { return worktimeModel; }

            set { worktimeModel = value; }

        }

    }

 

In case of compositions you can delete the depending work time model directly when removing the employee from database. Therefore you can call the mothod : DeleteRecursive

Now back to our example. Think of a time entry. A time entry is always associated to an employee, but it does not own the employee. That mean, it does not aggregate the employee. If you delete a time entry, the user won’t be deleted. In that case it’s better to use a loose couple between the class TimeEntry and Employee.

    public class TimeEntry : ValueObject

    {

        private Guid employeeId;

 

        /// <summary>

        /// Gets or sets the employeeId.

        /// </summary>

        /// <value>The employeeId.</value>

        [ForeignKey(typeof(Employee), "Id")]

        public Guid EmployeeId

        {

            get { return employeeId; }

            set { employeeId = value; }

        }

    }

 

The [ForeignKey] attribute ensures, that the stored Property uses a foreign key to the Employee table. If you still need further informations of the employee who is associated, you can use a VirtualLink to join the employee table to the Time Entry.

    public class TimeEntry : ValueObject

    {

        private Guid employeeId;

        private string name;

 

        /// <summary>

        /// Gets or sets the employeeId.

        /// </summary>

        /// <value>The employeeId.</value>

        [ForeignKey(typeof(Employee), "Id")]

        public Guid EmployeeId

        {

            get { return employeeId; }

            set { employeeId = value; }

        }

 

        /// <summary>

        /// Gets or sets the name of the employee.

        /// </summary>

        /// <value>The name.</value>

        [VirtualLink(typeof(Employee), "Id", "Name", "EmployeeId")]

        public string Name

        {

            get { return name; }

            set { name = value; }

        }

    }

Conclusion

If you use this pattern in all your entities, you can load the entities most times without specifiing an hierarchy level. The advantage are more clearly designed business components that are much easier to understand.

I wish you a successfull time.

Cheers
 – Gerhard

Posted in Hint | No Comments »

Hierarchy Level Explained

Posted by Gerhard Stephan on 1st October 2007

The AdFactum ObjectMapper .NET uses hierarchy levels to define the degree of dependence that will be considered when loading or saving.

When loading with hierarchy level 0, the object will be loaded into memory without any dependencies. The same happens when the AdFactum ObjectMapper.NET is storing the objects to database. That means HierarchyLevel.FlatObject, loading and storing without any object dependencies.

For load operations the hierachy level step is always 2.

        public const int FlatObject = 0;

        public const int Dependend1stLvl = 2;

        public const int Dependend2ndLvl = 4;

        public const int AllDependencies = int.MaxValue;

 

In the following example you can see the object dependencies when loading the customer object.

 

For example, when loading the customer with HierarchyLevel.Dependent1stLvl the aggregated Order Objects will be loaded too. When loading the customer with HierarchyLevel.Dependent2ndLvl the OrderItems of the Order Object will be also loaded.

When storing objects to database the Hierarchy Level is even more senstive. That means that you can also specify, whether an object should only update the links to an aggregated object or whether the object should also update the aggregated object too.

These are the intermediate steps 1, 3, 5 and so on. That is, if you only add an existing OrderItem to the Order object, you can store the Order Object with the HierarchyLevel.FlatObjectWithLinks, which updates the link to the OrderItem, but not the OrderItem itself. The difference is very important to know.

For load operations the hierachy level step is always 1.

        public const int FlatObject               = 0;

        public const int FlatObjectWithLinks      = 1;

        public const int Dependend1stLvl          = 2;

        public const int Dependend1stLvlWithLinks = 3;

        public const int Dependend2ndLvl          = 4;

        public const int Dependend2ndLvlWithLinks = 5;

        public const int AllDependencies          = int.MaxValue;

 

In the following example you can see the object dependencies when storing the customer object.

 

For a better understanding I recommend you to output the SQL trace and try to store the object with a different hierarchy levels. That gives you a feeling for, which hierarchy level you use best to get the optimal performance.

 

Posted in Hint | No Comments »

Using Oracle DECODE function to evaluate if an aggregated object does exists

Posted by Gerhard Stephan on 23rd August 2007

When you are doing a flat load of an object, the aggregated child objects won’t be loaded and the property in your entity class is set to NULL. At the first glance you can’t say whether the aggregated child object is not loaded or the aggregated child object does not exist in database.

To change this, we have to grab into our bag of tricks. Using the DECODE Function of Oracle we can determinate if a aggregated child object does exists or not.

public class Invitee : ValueObject   
{
        Contact _invitedContact;
        bool    _hasInvitedContact;

        /// <summary>

        /// The aggregated Contact

        /// </summary>

        [PropertyName("CONTACT")]

        public Contact InvitedContact

        {

            get { return _invitedContact; }

            set { _invitedContact = value; }

        }

             

        /// <summary>

        /// Using the Select Function the decide if an

        /// aggregated object does exists in database.

        /// </summary>

        [SelectFunction("DECODE(#SR#INVITEE.CONTACT,NULL,0,1)")]

        public bool HasInvitedContact

        {

            get { return _hasInvitedContact; }

            set { _hasInvitedContact = value; }

        }

 

 

 

 

As you can see the Select Function is using the Oracle DECODE function to determinate whether the aggregated contact does exists or not. The result of the SelectFunction will be mapped to the boolean value "HasInvitedContact". Using this hint you don’t have to explicit load the aggregated child object from database in order to say whether it does exists or not.

Remark: The placeholder #SR# is a constant used by the AdFactum ObjectMapper .NET. The constant will be replaced with the oracle shema when generating the SQL.

 

Posted in Hint | No Comments »

Supported Data Types

Posted by Gerhard Stephan on 4th July 2007

The AdFactum ObjectMapper .NET supports most Data Types that are available when using the Microsoft .NET Framework. For a better overview how data types are mapped against the different database types, the following tables shows the mapping details.

    Oracle
System.Boolean NUMBER(1,0) DEFAULT 0 NOT NULL
System.Byte NUMBER(3)
System.DateTime DATE
System.Decimal NUMBER(*,12)
System.Double FLOAT({0})
System.Guid RAW(16)
System.Int16 NUMBER(6)
System.Int32 INTEGER
System.Int64 INTEGER
System.Single FLOAT({0})
System.String VARCHAR2({0})
System.TimeSpan DATE
System.Enum NUMBER(4) DEFAULT -1 NOT NULL
System.IO.Stream BLOB
System.Byte[] BLOB
System.Char CHAR(1)
Special case for unlimited string length CLOB
Microsoft SQL Server
System.Boolean BIT DEFAULT 0 NOT NULL
System.Byte TINYINT
System.DateTime DATETIME
System.Decimal DECIMAL(28,12)
System.Double FLOAT
System.Guid UNIQUEIDENTIFIER
System.Int16 SMALLINT
System.Int32 INT
System.Int64 BIGINT
System.Single REAL
System.String VARCHAR({0})
System.TimeSpan BIGINT
System.Enum SMALLINT NOT NULL
System.IO.Stream IMAGE
System.Byte[] IMAGE
System.Char CHAR(1)
Special case for unlimited string length TEXT
Microsoft SQL Server CE
System.Boolean BIT DEFAULT 0 NOT NULL
System.Byte TINYINT
System.DateTime DATETIME
System.Decimal FLOAT
System.Double FLOAT
System.Guid UNIQUEIDENTIFIER
System.Int16 SMALLINT
System.Int32 INT
System.Int64 BIGINT
System.Single REAL
System.String NVARCHAR({0})
System.TimeSpan BIGINT
System.Enum SMALLINT NOT NULL
System.IO.Stream IMAGE
System.Byte[] IMAGE
System.Char NCHAR(1)
Special case for unlimited string length NTEXT
Microsoft Access
System.Boolean BIT DEFAULT 0 NOT NULL
System.Byte BYTE
System.DateTime DATETIME
System.Decimal DOUBLE
System.Double DOUBLE
System.Guid GUID
System.Int16 SMALLINT
System.Int32 INT
System.Int64 LONG
System.Single SINGLE
System.String VARCHAR({0})
System.TimeSpan DATETIME
System.Enum SMALLINT NOT NULL
System.IO.Stream IMAGE
System.Byte[] IMAGE
System.Char CHAR(1)
Special case for unlimited string length MEMO

 

{0} = Replacement for the column length specified by the attribute PropertyLength.

Posted in Hint | No Comments »

Concepts for searching in database

Posted by Gerhard Stephan on 13th December 2006

The AdFactum ObjectMapper .NET makes it technically easy for applications to search for objects in database. Most problem I had, and what I had to learn was not how to search technically, but how to search in a clear conceptual way.

If we are talking about searching, we are talking about two issues.

  1. What to search
  2. Where to search

These two issues aren’t always easy to separate. I wrote this blog entry in order to help you to make your decision if a search condition is a "What" or a "Where".

"Where to search"
A "Where" is always a static property of the search within a search dialog. So If we not would specify any "What to search" constraints, we would get the complete content of the "Where" constraint.

Example:

  • We search within all contacts.
  • We search within all contacts who have sales within the running year.

In our application we would have two search dialogs, or at least two search areas. You see, the "Where" specifies the surrounding context of the search function.

"What to search"
A "What" is always a user defined limitation of the search result. Within the search dialog, the user has the right to change the "What" limitation for his needs.

Example:

  • I want to search contacts where surname is "Meyer"
  • I want to search contacts who are older than 30 years.

The "What" is always a user defined limitation of the search. As a result you can apply every "Where" (search scope) to any "What" (search criterias). So these two can be combined.

Consideration:
Splitting the search scope and the search criterias into distinct cases, offers you the possibility to re-use the search criterias for different use cases within your application.

How to split the search context from the search criterias
With this theoretical background I want to explain how to split the search scope and the search criterias.

    /// <summary>

    /// Interface that defines a scope for a search criteria

    /// </summary>

    public interface ISearchScope

    {

        /// <summary>

        /// Gets the search condition.

        /// </summary>

        /// <value>The search condition.</value>

        ICondition Condition

        {

            get;

        }

    }

The interface used to define a search scope is quite simple. Because a search scope is static, only a Condition must be returned that can be added to the condition of a search criteria. In praxis, the Condition returns a search condition of type ConditionList (which can contain multiple conditions).

    /// <summary>

    /// Interface that defines a search criteria

    /// </summary>

    public interface ISearchCriteria

    {

        /// <summary>

        /// Set the scope for a search criteria.

        /// </summary>

        /// <value>The Scope.</value>

        ISearchScope Scope

        {

            set;

        }

 

        /// <summary>

        /// Gets or sets the order by.

        /// </summary>

        /// <value>The order by.</value>

        OrderBy OrderBy

        {

            get;

        }

 

        /// <summary>

        /// Gets the search condition.

        /// </summary>

        /// <value>The search condition.</value>

        ICondition Condition

        {

            get;

        }

 

    }

The search criteria interface offers the possibility to set a search scope. This is used to set the "Where" Constraint of a search. A concrete implementation must use the result condition of the scope and add it to the result of the search criteria that will be returned by calling the Condition Property.

Example of a search scope that limits the search to active projects:

    /// <summary>

    /// Search criteria only works on active projects

    /// </summary>

    public class ActiveProjects : ISearchScope

    {

        /// <summary>

        /// Gets the search condition.

        /// </summary>

        /// <value>The search condition.</value>

        public ICondition Condition

        {

            get

            {

                return

                    new ConditionList(

                        new AndCondition(typeof(Project), "IsDeleted", false),

                        new Parenthesize(

                            new OrCondition(typeof(Project), "StartDate", QueryOperator.Is, null),

                            new OrCondition(typeof(Project), "StartDate", QueryOperator.LesserEqual, DateTime.Today)),

                        new Parenthesize(

                            new OrCondition(typeof(Project), "EndDate", QueryOperator.Is, null),

                            new OrCondition(typeof(Project), "EndDate", QueryOperator.GreaterEqual, DateTime.Today))

                        );

            }

        }

    }

Example of a base class that can be used to derive search criterias:

 

    /// <summary>

    /// Base class used for every search

    /// </summary>

    public class SearchCriteria : ISearchCriteria

    {

        private ISearchScope scope = null;

 

        /// <summary>

        /// Sets the scope.

        /// </summary>

        /// <value>The scope.</value>

        public ISearchScope Scope

        {

            set { scope = value; }

        }

 

        /// <summary>

        /// Gets or sets the order by.

        /// </summary>

        /// <value>The order by.</value>

        public OrderBy OrderBy

        {

            get { return null; }

        }

 

        /// <summary>

        /// Gets the search condition.

        /// </summary>

        /// <value>The search condition.</value>

        public virtual ICondition Condition

        {

            get

            {

                return scope.Condition;

            }

        }

    }

 

Example of a class that searches for projects:

    /// <summary>

    /// Project search Criterias

    /// </summary>

    public class ProjectSearchCriteria : SearchCriteria   

    {

        private string name = "";

        private DateTime startedAfter = DateTime.MinValue;

           

        /// <summary>

        /// Gets the search condition.

        /// </summary>

        /// <value>The search condition.</value>

        public override AdFactum.Data.ICondition Condition

        {

            get

            {

                ICondition result = base.Condition;

               

                if (name.Length>0)

                    result.Add(

                        new AndCondition(typeof(Project), "Name",
                            QueryOperator.Like_NoCaseSensitive, Name)

                        );

               

                if (startedAfter != DateTime.MinValue)

                    result.Add(

                        new AndCondition(typeof(Project), "StartDate",
                            QueryOperator
.GreaterEqual, StartedAfter)

                        );

                   

                return result;

            }

        }

 

        /// <summary>

        /// Gets or sets the name.

        /// </summary>

        /// <value>The name.</value>

        public string Name

        {

            get { return name; }

            set { name = value; }

        }

 

        /// <summary>

        /// Gets or sets the started after.

        /// </summary>

        /// <value>The started after.</value>

        public DateTime StartedAfter

        {

            get { return startedAfter; }

            set { startedAfter = value; }

        }

    }

Consideration of the example above:

As you can see, you can attach several search scopes to the project search criterias. Thus allows you to search within different scopes, e.g. search only in active projects or search in all projects without changing the project search criterias.

I think this is a great possibility to build complex search functionality without overloading the search criterias by context or scope changes.

Wish you all the best
Gerhard

Posted in Hint | 4 Comments »

A final performance boost

Posted by Gerhard Stephan on 17th August 2006

The following article is obsolete!
The ICreateObject interface is now part of the AdFactum ObjectMapper .NET 1.90.1917 and it’s supported by the new UniversalFactory class. So you don’t have to implement a new factory to get this performance boost.

If you need a final performance boost in your application you have to implement a HashFactory in order to create your entities.

As you might know the ObjectMapper .NET allows you to setup your own factory in order to create the business entities loaded by the ObjectMapper .NET. This circumstance can be used to implement an algorithm that is based on the factory pattern. The creation of a new object using the new method is much more faster than it’s pendant using the Activator object.

Therefore all objects must implement a new interface:

    public interface ICreateObject

    {

        /// <summary>

        /// Returns a new copy of the object type

        /// </summary>

        /// <returns></returns>

        IValueObject CreateNewObject();

    }

This may look like that:

    class Contact : ValueObject, ICreateObject

    {

       

       

 

        public IValueObject CreateNewObject()

        {

            return new Contact();

        }

    }

 

 Now the most exciting part of all. The Factory:

    class HashFactory : IObjectFactory

    {

        /// <summary>

        /// Hashtable that contains the list of template objects

        /// </summary>

        static Dictionary<Type, IFactoryMethod> voHash = new Dictionary<Type, IFactoryMethod>();

       

        /// <summary>

        /// Method to create an object from a type name

        /// </summary>

        /// <param name="typeName"></param>

        /// <returns></returns>

        public object Create(string typeName)

        {

            return Create(Type.GetType(typeName));

        }

 

        /// <summary>

        /// Method to create an object from a type

        /// </summary>

        /// <param name="type"></param>

        /// <returns></returns>

        public object Create(Type type)

        {

            IFactoryMethod voTemplate;

            voHash.TryGetValue(type, out voTemplate);

 

            IValueObject result;

            if (voTemplate == null)

            {

                result = Activator.CreateInstance(type) as IValueObject;

 

                voTemplate = result as ICreateObject;

                if (voTemplate != null)

                {

                    voHash.Add(type, voTemplate);

                    result = voTemplate.CreateNewObject();

                }

            }

            else

                result = voTemplate.CreateNewObject();

           

            return result;

        }

    }

Last but not least, that factory can be used to create the ObjectMapper .NET.

           ObjectMapper mapper = new ObjectMapper(new HashFactory(), persister, Transactions.Manual);

 

And now – boost your application !

Cheers
Gerhard

 

Posted in Hint | No Comments »

Persistent Domain Object Pattern meets the ObjectMapper .NET

Posted by Gerhard Stephan on 23rd June 2006

If you are using the ObjectMapper .NET your entities will at least need to implement the IValueObject interface. Furthermore you have to add some attributes (e.g. to specify the column length) to the properties of your business entities.

All these things are dependencies and due to this you’ll need to reference the ObjectMapper .NET assembly wherever you access the business entities.

So it would be nice if we could implement a loose coupling between the implementation details of the business entities and the UI.

The “Persistent Domain Object Pattern” – short named as PDO-Pattern – caters for that issue. It splits the business entities to an interface that is usually used by the UI and the concrete implementation for the persistent layer.

The UI only references the interfaces of the business entities. The implementation will be covered through the business components which are also using the interfaces for object exchange.

Component Diagram

For this Blog I implemented an example that shows how to separate the business entities and the user interface. Imagine you want to build an application that manages a company with one contact. Ok – not a real world example, but good enough to show how to implement the pattern.

PDO Pattern Class Diagram

As you can see the concrete implementation of the business entities implements the interfaces ICompany and IContact. Furthermore the business entities are derived from ValueObject which is important for the ObjectMapper .NET because of the object mapping.

      /// <summary>

      /// Implementation for the company

      /// </summary>

      [Table("Company")]

      public class CompanyImpl : ValueObject, ICompany

      {

            private string companyName;

            private string street;

            private string plz;

            private string city;

            private IContact companyContact;

           

           

If you are working with interfaces in a business entity you have to tell the ObjectMapper .NET the concrete implementation of the interface due to the object mapping.

            /// <summary>

            /// Gets or sets the company contact.

            /// </summary>

            /// <value>The company contact.</value>

            [Ignore]

            public IContact CompanyContact

            {

                  get { return companyContact; }

                  set { companyContact = value; }

            }

 

            /// <summary>

            /// Gets or sets the company contact used by the ObjectMapper .NET

            /// </summary>

            /// <value>The company contact.</value>

            [PropertyName("CompanyContact")]

            public ContactImpl CompanyContactImpl

            {

                  get { return companyContact as ContactImpl; }

                  set { companyContact = value; }

            }

 

As you can see the interface is tagged with an [Ignore] Attribute in order to tell the mapper that this interface should be ignored for mapping. Instead of an interface mapping we implemented a Getter / Setter to the real aggregated object. This method is not visible to the UI but used by the ObjectMapper .NET to store the Contact entity.

Last but not least the UI talks to the business components in order to receive and store the data.

      public class CompanyService 

      {

            public static ICompany CreateNewCompany ()

            { … }

                 

            public static void SaveCompany (ICompany company)

            { … }

 

            public static IList GetAllCompanies ()

            { … }

      }

 

And the UI only needs two references to external assemblies, the BusinessComponents and the Business Entities. There’s no need for adding a reference neither to the ObjectMapper nor to the concrete implementation of the Business Entities.

The complete example can be downloaded using the following link:

Persistent Domain Object Pattern meets the ObjectMapper .NET

 

Posted in Hint | No Comments »

Calling and Mapping Stored Procedures

Posted by Gerhard Stephan on 14th June 2006

Sometimes it is useful and necessary to call stored procedures. Imagine you have the following Stored Procedure code within your application.

                  SET ANSI_NULLS ON

                  GO

                  SET QUOTED_IDENTIFIER ON

                  GO

 

                  CREATE PROCEDURE SayHello

                        — Add the parameters for the stored procedure here

                        @Param1 as VARCHAR(255) = ‘everybody’

                  AS

                  BEGIN

                        — SET NOCOUNT ON added to prevent extra result sets from

                        — interfering with SELECT statements.

                        SET NOCOUNT ON;

 

                      — Insert statements for procedure here

                        SELECT ‘Say hello to ‘ + @Param1 as ResultValue, NEWID() as Id

                  END

                  GO

 

Due to the fact that stored procedures are specific to the SQL Server (Oracle are using PL SQL Blocks) this is handled by a special Interface. You will get that interface by casting the used persister to INativePersister. After that you can use the Execute and ExecuteWithParameter Method to call the stored procedure.

                  /*

                   * Get the low level Native Persister from the SQL Persister

                   */

                  INativePersister nativePersister = sqlPersister as INativePersister;

                 

                  // First example without Parameter

                  nativePersister.Execute("EXEC dbo.SayHello");

 

                  // Second example is using Parameters

                  nativePersister.ExecuteWithParameter("EXEC dbo.SayHello @p01", "Peter");

 

Remark: Because of the internal parameter handling it’s important that you enumerate your parameters from p01 to pNN.

Now we want to map that stored procedure to an object. Because it is necessary to have the mapping functionality this has to be done using the ObjectMapper class itself. 

                  /*

                   * Mapping the result of a stored procedures to an object

                   */

                  SortedList parameter = new SortedList();

                  parameter.Add("p01", "Peter") ;

                  IList resultSet = mapper.SelectNative(

                        typeof(Result), "EXEC dbo.SayHello @p01", parameter);

 

                  if (resultSet.Count>0)

                  {

                        Result peter = resultSet[0] as Result;

                        Console.WriteLine(peter.Value);

                  }

Remark: In order to map a stored procedure to a class it is important that the stored procedure additionaly returns a column named "Id" that contains a unique identifier for the object.

As you can see it is quite simple to map a stored procedure to a class using the ObjectMapper .NET. The working example can be downloaded using the following link:

Example: Calling and Mapping Stored Procedures

 

Posted in Hint | No Comments »

Mapping distinct classes with the same interface transparent to the application

Posted by Gerhard Stephan on 7th June 2006

Imagine that you want to map classes that implements the same interface to database.

Example:
You want to store personal informations about an individual. The individual (you) can drive a vehicle. This vehicle is implemented as an interface and has two concrete implementations like car and motorbike. So how to map these classes?

Solution 1:
You create a property in your class that returns the interface and mark this property with the attribute [GeneralLink].

             IVehicle _vehicle;

 

            /// <summary>

            /// Gets or sets the vehicle.

            /// </summary>

            /// <value>The vehicle.</value>

            [GeneralLink]

            public IVehicle Vehicle

            {

                  get { return _vehicle; }

                  set { _vehicle = value; }

            }

This is a very easy solution that handles every object that implements the interface IVehicle. To handle the different types, the ObjectMapper .NET adds an additional column that holds information about the class name of the stored object.

CREATE TABLE [INDIVIDUAL] (

         [ID] GUID, 

         [VEHICLE] GUID,

         [VEHICLE#TYP] VARCHAR(255),

         PRIMARY KEY([ID]));

The disadvantage of that method is that you can’t add foreign keys to the database column. Why? Because it contains IDs of different tables.

Solution 2:
The second way is to implement the interface property with an [Ignore] attribute and add access properties for storing the concrete object to database.

            IVehicle _vehicle;

 

            /// <summary>

            /// Gets or sets the vehicle.

            /// </summary>

            /// <value>The vehicle.</value>

            [Ignore]

            public IVehicle Vehicle

            {

                  get { return _vehicle; }

                  set { _vehicle = value; }

            }

 

            /*

             * Access Properties used by the Object Mapper .NET

             */

 

            public Car Car

            {

                  get { return _vehicle as Car; }

                  set { if (value != null) _vehicle = value; }

            }

 

            public Motorbike Motorbike

            {

                  get { return _vehicle as Motorbike; }

                  set { if (value != null) _vehicle = value; }

            }

This is a very smart solution, because the application still uses the Vehicle Property to access the vehicle for an individual. The ObjectMapper .NET uses the concrete accessor properties to set the object. Thus enables the ObjectMapper .NET to add foreign key constraints and therefore have a better object model.

CREATE TABLE [INDIVIDUAL] (

         [ID] GUID, 

         [MOTORBIKE] GUID,

         [CAR] GUID,

        

         PRIMARY KEY([ID]));

 

ALTER TABLE [INDIVIDUAL] ADD

        CONSTRAINT INDIVIDUAL_FK01

        FOREIGN KEY ([MOTORBIKE]) REFERENCES [MOTORBIKE];

       

CREATE INDEX INDIVIDUAL_FKI01 ON [INDIVIDUAL] ([MOTORBIKE]);

 

ALTER TABLE [INDIVIDUAL] ADD

        CONSTRAINT INDIVIDUAL_FK02

        FOREIGN KEY ([CAR]) REFERENCES [CAR];

       

CREATE INDEX INDIVIDUAL_FKI02 ON [INDIVIDUAL] ([CAR]);

 

Conclusion:
If you have few derivations of the interface the second solution is much smarter because it gives you the ability to add constraints to the database and enable you to query the concrete objects. If you have many different classes that implements your interface the first solution might be better, because it does not overload your class with accessor properties.

 

Posted in Hint | No Comments »