C# Object Generator

Introduction
Most of the times, while developing a CRUD application, the data is not available prior to UI development to be able to insert the data, so the only way to do it is to do it the dirty way which is you go and insert data manually in the database. Inserting data manually works fine for some cases, but not in most cases, and what about developing a test case where you want to check if insert is working as it should be or getting a lot of data as an input for your application such as a List. There are a lot of utilities in the market that will be able to support all the things mentioned above or some of them, but most importantly how can we do it ourselves.

Background
I was looking for something to populate object on fly with random data to be able to use it for checking the integrity of my functions and I found something on Stackoverflow but I said to myself what the hell, since I have time let me try to build my own and see how it goes from there. And this is exactly what I did, what I did is not yet completed, but it paves the way for those who are interested in this topic.

Using the Code
The code is pretty simple and it has two Classes, one is Invoker which returns Action <T,object> which is the representation of the actual setter for MyClass Property, and then we have the RandomObjectGenerator which holds the function that fills the object with random data based on the Class Properties types.

So we start with the Invoker code, and specifically CreateSetter function which returns Action<T,object>:

public class Invoker
{
        public  static Action<T,object> CreateSetter<T> (PropertyInfo propertyInfo)
        {
               var targetType = propertyInfo.DeclaringType;
               
               var info = propertyInfo.GetSetMethod();
 
               Type type = propertyInfo.PropertyType;
 
               var target = Expression.Parameter(targetType, "t");
               
               var value = Expression.Parameter(typeof(object), "st");
 
               var condition = Expression.Condition(
                       // test
                       Expression.Equal(value, Expression.Constant(DBNull.Value)),
                       // if true
                       Expression.Default(type),
                       // if false
                       Expression.Convert(value, type)
               );
 
               var body = Expression.Call(
                  Expression.Convert(target, info.DeclaringType),
                  info,
                  condition
               );
 
               var lambda = Expression.Lambda<Action<T,object> >(body, target, value);
               
               var action = lambda.Compile();
 
               return action;
        } 
}

Then we have the RandomObjectsGenerator which calls the invoker class to set the properties with the related random data per property.

public class RandomObjectsGenerator<T> where T: class,new()
{
        private static  Random rand = new Random();
 
        private static List<long> randomLongNumbersList = new List<long> ();
        private static List<Int32> randomIntNumbersList = new List<Int32> ();
        private static List<DateTime> randomDateTimeList = new List<DateTime> ();
        
        // Randomization bounds 
        private int minLongRandBound = 1;
        private long maxLongRandBound = long.MaxValue;
 
        private int minIntRandBound = 1;
        private int MaxIntRandBound = Int32.MaxValue; 
 
        private int GetIntNumber()
        {
               byte[] buf = new byte[8];
               
               rand.NextBytes(buf);
           
               int intRand = BitConverter.ToInt32(buf, 0);
 
               var value = Math.Abs(intRand % 
               (minIntRandBound - MaxIntRandBound)) + minIntRandBound;
 
               if (!randomIntNumbersList.Contains(value))
               {
                       randomIntNumbersList.Add(value);
               }
               else
               {
                       GetIntNumber();
               }
 
               return value;
        }
 
        private long GetLongNumber() 
        {
               byte[] buf = new byte[8];
               rand.NextBytes(buf);
               long longRand = BitConverter.ToInt64(buf, 0);
 
               var value = Math.Abs(longRand % 
               (minLongRandBound - maxLongRandBound)) + minLongRandBound;
 
               if (!randomLongNumbersList.Contains(value))
               {
                       randomLongNumbersList.Add(value);
               }
               else 
               {
                       GetIntNumber();
               }
 
               return value;
        }
 
        private decimal GetDecimal() 
        {
               byte scale = (byte)rand.Next(29);
               bool sign = rand.Next(2) == 1;
               return new decimal
                       (
                                 GetIntNumber(),
                                 GetIntNumber(),
                                 GetIntNumber(),
                                 sign,
                                 scale
                       );
        }
 
        private bool GetBool() 
        {
               int value = rand.Next(100);
 
               if (value <= 50)
                       return true;
               else
                       return false;
        }
 
        private string GetString() 
        {
               return Guid.NewGuid().ToString();
        }
 
        private DateTime GetDateTime() 
        {
               DateTime startingDate = DateTime.Now.AddYears(-2);
 
               int range = (DateTime.Today - startingDate).Days;
 
               DateTime value = startingDate
                       .AddDays(rand.Next(range))
                       .AddHours(rand.Next(0,24))
                       .AddMinutes(rand.Next(0,60))
                       .AddSeconds(rand.Next(0,60))
                       .AddMilliseconds(rand.Next(0,999));
 
               if (!randomDateTimeList.Contains(value))
               {
                       randomDateTimeList.Add(value);
               }
               else 
               {
                       GetDateTime();
               }
 
               return value;
        }
 
        private byte GetByte()
        {
               Byte[] b = new Byte[10];
               
               rand.NextBytes(b);
 
               return b[rand.Next(0,9)];
        }
 
        public  static T GenerateRandomObject()
        {
 
               RandomObjectsGenerator<T> randObjGen = new RandomObjectsGenerator<T> ();
 
               Dictionary<string, Action<T,object>> setters = 
               new Dictionary<string, Action<T,object>> ();
 
               // List of class property infos
               List<PropertyInfo> masterPropertyInfoFields = new List>PropertyInfo> ();
 
 
               //Define what attributes to be read from the class
               const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
 
               masterPropertyInfoFields = typeof(T).GetProperties(flags)
                  .Cast<PropertyInfo> ()
                  .ToList();
 
               foreach (var field in masterPropertyInfoFields)
               {
                       var propertyInfo = typeof(T).GetProperty(field.Name);
                       var propertyName = field.Name;
                       setters.Add(propertyName, Invoker.CreateSetter<T> (propertyInfo));
               }
 
               T obj = new T();
 
               var typedValueMap = new Dictionary<Type,Delegate>
               {
                       {typeof(int),new Func<int> (() => randObjGen.GetIntNumber())},
                       {typeof(long),new Func<long> (() => randObjGen.GetLongNumber())},
                       {typeof(decimal),new Func<decimal> (() => randObjGen.GetDecimal())},
                       {typeof(bool),new Func<bool> (() => randObjGen.GetBool())},
                       {typeof(DateTime),new Func<DateTime> (() => randObjGen.GetDateTime())},
                       {typeof(string),new Func<string> (() => randObjGen.GetString())},
                       {typeof(byte),new Func<byte> (() => randObjGen.GetByte())}
               };
 
               foreach (var setter in setters) 
               {
                       Type type = masterPropertyInfoFields.Where
                       (item => item.Name == setter.Key).Select
                       (item => item.PropertyType).FirstOrDefault();
 
                       if (type != null)
                       {
                               int y = randObjGen.GetIntNumber();
 
                               if (typedValueMap.ContainsKey(type))
                               {
                                      setter.Value(obj, typedValueMap[type].DynamicInvoke(null));
                               }
                       }
               }
               return obj;
        }
}

There are couple of things in the code above that need to be discussed, first the Delegates Dictionary and why I’m using it, to start .NET doesn’t support type switching such as switch(type){} and since all the random generators don’t take an argument and hey return typed data, I said to myself why not create a dictionary of types and delegates based on types and this is what I did, so as long as I know the type I can call the delegate function by calling DynamicInvoke(null).

Secondly, the Invoker which creates the Setters for the properties. For every PropertyInfo, there should be a function that represents the setter, and I’m adding them to a Dictionary to use it later on, finally call the setter from the dictionary to fill the property value which has been created by calling the Delegate function.

Notes
This implementation is not complete and not yet optimized, but it fits the purpose with couple of downsides:

It doesn’t respect sub-objects so you need to do them yourself like you did the master object and then attach it to the master object.
Uniqueness is always respected for Int, Long, DateTime Data Types, so it will affect any foreign key relation with another table if you are inserting the generated object to database.
Maybe I will enhance it in the future if I needed to, but for the time being it fits my needs and I hope that it fits yours.

Posted in Programming and tagged , , , , , . Bookmark the permalink. RSS feed for this post. Leave a trackback.

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.

Swedish Greys - a WordPress theme from Nordic Themepark.