Sony Arouje

a programmer's log

Archive for January 2011

.NET Reflection–Helpful Functions

leave a comment »

In this post I will explain few functions I created to solve some of the scenarios in one of my project.

Property Defaulter

In one of my project I had to deal with a table that have hundreds of fields but we are dealing with very few field in the table. The several fields of the table needs default values either zero or empty. I know if I am not finding a generic solution I will be in trouble as I had to fill all the fields my self and I don’t want to do that. So I created a small function that will do the job of setting my entities to default values if the properties doesn’t have any values. Here is my function

public static void SetDefaultValue<TEntity>(TEntity entity) where TEntity : class
{
    try
    {
        PropertyInfo[] properties = typeof(TEntity).GetProperties();
        bool isSetDefaultVal = false;
        foreach (PropertyInfo property in properties)
        {
            object propertyValue;
            if (property.CanRead)
            {
                isSetDefaultVal = false;
                if (property.PropertyType.Name != "String" && 
                    property.PropertyType.Name != "Single" && 
                    property.PropertyType.Name != "Int32")
                    continue;
                try
                {
                    propertyValue = property.GetValue(entity, null);
                }
                catch (Exception ex)
                {
                    //log error
                    continue;
                }
                if (propertyValue==null)
                {
                    if (property.PropertyType.Name == "String")
                        propertyValue = "";
                    else
                        propertyValue = 0;

                    isSetDefaultVal = true;
                }
                //set the value if property value is null and set to 0 in above if block.
                if (property.CanWrite && isSetDefaultVal == true)
                {
                    if (property.PropertyType.Name == "Single")
                        property.SetValue(entity, Convert.ToSingle(propertyValue), null);
                    else if (property.PropertyType.Name == "Int32")
                        property.SetValue(entity, Convert.ToInt32(propertyValue), null);
                    else if (property.PropertyType.Name == "String")
                        property.SetValue(entity, "", null);
                }
            }

        }
    }
    catch (Exception ex)
    {
        throw;
    }
}

 

So how we use it, lets see with a e.g

PropertyDefaulter.SetDefaultValue<ItemMaster>(itemMaster);

The above function will inspect the properties in the itemMaster object, if any property doesn’t has value then based on the type the function will set the value. Int or float property will set to 0 and string property will set to empty.

Copy DataReader to Entities

When I was dealing with ADO.NET I used to fetch data from database table using SqlCommand’s execute reader and get the data in DataReader. I have DTO’s (Data Transfer Object) to transfer data between layers. So I have to copy all the data in DataReader to my DTO’s. I need to find an easy way rather than manually assigning values from datareader to properties. Here is my method to do the copy of data from DataReader to my DTO’s

public static List<TEntity> CopyDataReaderToEntity<TEntity>(IDataReader dataReader) 
    where TEntity : class
{
    List<TEntity> entities = new List<TEntity>();
    PropertyInfo[] properties = typeof(TEntity).GetProperties();
    while (dataReader.Read())
    {
        TEntity tempEntity = Activator.CreateInstance<TEntity>();
        foreach (PropertyInfo property in properties)
        {
            SetValue<TEntity>(property, tempEntity, dataReader[property.Name]);
        }
        entities.Add(tempEntity);
    }
    return entities;
}

public static TEntity SetValue<TEntity>(PropertyInfo property, TEntity entity, object propertyValue) 
       where TEntity : class
{
    if (property.CanRead)
    {
        if (property.PropertyType.Name != "String" &&
            property.PropertyType.Name != "Single" && 
            property.PropertyType.Name != "Int32")
            return entity;

        if (propertyValue == null)
        {
            if (property.PropertyType.Name == "String")
                propertyValue = "";
            else
                propertyValue = 0;
        }
        if (property.CanWrite)
        {
            if (property.PropertyType.Name == "Single")
                property.SetValue(entity, Convert.ToSingle(propertyValue), null);
            else if (property.PropertyType.Name == "Int32")
                property.SetValue(entity, Convert.ToInt32(propertyValue), null);
            else if (property.PropertyType.Name == "String")
                property.SetValue(entity, propertyValue, null);
        }
    }
    return entity;
}

 

Here we need to follow a convention to get it working. The convention I follow here is, the Property name in the DTO and the table field should be same.

Let’s see how we can use it.

SqlCommand cmd = new SqlCommand(qryToExecute, connection);
SqlDataReader reader = cmd.ExecuteReader();
List<ItemDetails> itemDetails = SQLHelper.CopyDataReaderToEntity<ItemDetails>(reader);

Create Select and Insert SQL statement Dynamically

When I was dealing with some table that have a lot of fields, I always have trouble in creating select or insert statement. So I created a small function that creates the Select and Insert statement dynamically. Here also I follow the same convention I explained earlier, that is the field name and the property name should be same.

private static List<FiledAndValueHolder> CreateFieldValueMapper<TEntity>(TEntity entity) 
    where TEntity : class
{
    List<FiledAndValueHolder> filedAndValues = new List<FiledAndValueHolder>();
    PropertyInfo[] properties = typeof(TEntity).GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object propertyValue;
        string propertyName;

        if (property.CanRead)
        {
            if (property.PropertyType.Name != "String" &&
                property.PropertyType.Name != "Single" && 
                property.PropertyType.Name != "Int32")
                continue;

            propertyValue = property.GetValue(entity, null);
            propertyName = property.Name;

            if (propertyName.StartsWith("Native"))
                continue;
            FiledAndValueHolder fieldAndValue = new FiledAndValueHolder();
            fieldAndValue.FieldValue = propertyValue;
            fieldAndValue.FiledName = propertyName;
            fieldAndValue.FieldType = property.PropertyType.Name;
            filedAndValues.Add(fieldAndValue);
        }
    }
    return filedAndValues;
}

public static string CreateInsertStatement<TEntity>(TEntity entity, string tableName) 
   where TEntity : class
{
    List<FiledAndValueHolder> filedAndValues = CreateFieldValueMapper<TEntity>(entity);
    string sql = "insert into [dbo]." + tableName + "({0}) values ({1})";
    StringBuilder fieldBuilder = new StringBuilder();
    StringBuilder valueBuilder = new StringBuilder();
    string seprator = string.Empty;
    foreach (FiledAndValueHolder filedAndValue in filedAndValues)
    {
        fieldBuilder.Append(seprator);
        fieldBuilder.Append(filedAndValue.FiledName);

        valueBuilder.Append(seprator);
        if (filedAndValue.FieldType == "String")
        {
            valueBuilder.Append(string.Format("'{0}'", filedAndValue.FieldValue as string));
        }
        else
        {
            valueBuilder.Append(filedAndValue.FieldValue);
        }

        if (string.IsNullOrEmpty(seprator))
            seprator = ",";
    }

    string sqlStatement = string.Format(sql, fieldBuilder.ToString(), valueBuilder.ToString());
    return sqlStatement;
}
public static string CreateSelectStatement<TEntity>(TEntity entity, string tableName, string selectCondition) where TEntity : class
{
    List<FiledAndValueHolder> filedAndValues = CreateFieldValueMapper<TEntity>(entity);
    string sql = "select {0} from dbo.{1} where ({2})";
    StringBuilder fieldBuilder = new StringBuilder();
    string seprator = string.Empty;
    foreach (FiledAndValueHolder filedAndValue in filedAndValues)
    {
        fieldBuilder.Append(seprator);
        fieldBuilder.Append(filedAndValue.FiledName);
        if (string.IsNullOrEmpty(seprator))
            seprator = ",";
    }

    return string.Format(sql, fieldBuilder.ToString(), tableName, selectCondition);
}

 

Let’s see how we can use it.

insertItemMaster = SQLHelper.CreateInsertStatement<ItemMaster>(itemMaster, "ItemMaster");

Most of the functions I explained above doesn’t required much explanations. If any one need any more details then leme know.

Written by Sony Arouje

January 22, 2011 at 1:31 pm

db4o A light weight Object Oriented Database

with 8 comments

Now a days I am spending my free time in rewriting one of my freeware for Flickr users called iTraveller. One of the design goals was the application should be scalable. My major concern was, I cannot freeze my db design before my first release because I am planning to have multiple version with added functionalities, so the db structure may change in the future. If I use conventional db’s then I have to write functionality to add or remove columns. iTraveller is not very data centric application, so managing db change scripts and writing a functionality to apply these changes when user upgrade to new version will be a big pain for me.

First I thought of using MongoDB, because I used Mongo db in a Silverlight application. But then finally dropped that idea. I want some thing more lighter than Mongo and NoSql sort of database. I evaluated several db’s but nothing suites what I am looking for. Finally my search ends at db4o. The usage of db4o is very straight forward and very easy to learn, db4o installer comes with a very good documentation to start off with the development.

db4o is a light weight Object Oriented database. Using db4o I can persist my entities very easily without any configuration. I can add or remove new properties to my entities without affecting the existing persisted entities.

I created a generic repository sort of class around db4o, the same way I did one for Entity Framework. This generic repository reduced lot of my work and act as the entry point to persist my entities to db4o data file. Below is the Generic repository I created for db4o.

using Db4objects.Db4o;
public class GenericRepository:IRepository,IDisposable
{
    private static IRepository _singleRepoInstance;
    public const string DBNAME = "Data.dat";

    public static IRepository GetRepositoryInstance()
    {
        if (_singleRepoInstance == null)
            _singleRepoInstance = new GenericRepository();

        return _singleRepoInstance;
    }

    IObjectContainer _context = null;
    private IObjectContainer Context
    {
        get 
        {
            if(_context==null)
                _context = Db4oFactory.OpenFile(DBNAME);
            
            return _context;
        }
    }

    private IList<TEntity> Query<TEntity>() where TEntity : class
    {
        return this.Context.Query<TEntity>();
    }

    public IList<TEntity> GetAll<TEntity>() where TEntity : class
    {
        return this.Query<TEntity>().ToList();
    }

    public IList<TEntity> GetAll<TEntity>(TEntity entity) where TEntity : class
    {
        return this.Context.QueryByExample(entity).Cast<TEntity>().ToList();
    }

    public IList<TEntity> Find<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
    {
        return this.Query<TEntity>().Where<TEntity>(criteria).ToList<TEntity>();
    }

    public TEntity Single<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
    {
        return this.Query<TEntity>().Single<TEntity>(criteria);
    }

    public TEntity First<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
    {
        return this.Query<TEntity>().FirstOrDefault<TEntity>(criteria);
    }
    public TEntity First<TEntity>(TEntity criteria) where TEntity : class
    {
        return this.Context.QueryByExample(criteria).Cast<TEntity>().First<TEntity>();
    }
    public void Add<TEntity>(TEntity entity) where TEntity : class
    {
        this.Context.Store(entity);
    }

    public void Add<TEntity>(List<TEntity> entities) where TEntity : class
    {
        foreach (TEntity entity in entities)
        {
            this.Add<TEntity>(entity);
        }
    }

    public void Delete<TEntity>(TEntity entity) where TEntity : class
    {
        this.Context.Delete(entity);
    }

    public void Update<TEntity>(TEntity entity) where TEntity : class
    {
        this.Context.Store(entity);
    }

    public void Dispose()
    {
        this._context.Close();
        this._context.Dispose();
        this._context = null;
    }
}

The above repository is created in singletone mode. I did that way because I noticed that opening the database file is taking some delay and wanted to avoid that delay. So I wont close the data file until iTraveller get closed.

Let’s see how we can call the above repository to save an entity

GenericRepository repository = GenericRepository.GetRepositoryInstance();
repository.Add<Photo>(photos);

 

Below is the way we can retrieve some entities from the data file

IRepository repository = GenericRepository.GetRepositoryInstance();
List<LocalCategory> categories = repository.GetAll<LocalCategory>().ToList<LocalCategory>();
 

 

You can also retrieve data based on any condition as well. The below code will return a list of Photo object whose categoryId is 10.

IRepository repository = GenericRepository.GetRepositoryInstance();
return repository.Find<Photo>(p => p.Category.ID == 10).ToList<Photo>();

 

As you can see the db4o is very easy to use light weight database without the hurdles of mapping to tables and all sort of stuffs.

Written by Sony Arouje

January 5, 2011 at 4:26 pm

Posted in .NET

Tagged with , , ,

%d bloggers like this: