Click here to Skip to main content
15,891,316 members
Articles / Web Development / HTML
Tip/Trick

ObjectGenerator

Rate me:
Please Sign up or sign in to vote.
4.78/5 (5 votes)
16 Dec 2014GPL33 min read 19.1K   9   6
This post addresses the possibility to create any kind of object and fill it with random data based on properties data types to ease data creation and inserting it in the database in CRUD application.

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<MyClass>. 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 the 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>:

C#
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<T> which calls the invoker class to set the properties with the related random data per property.

C#
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 
              {
                  GetLongNumber();
              }
             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(256);
               return value >= 128;
        }
 
        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 a couple of downsides:

  1. 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.
  2. 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.

History

  • 16/12/2014: Initial version

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Team Leader CCC
Greece Greece
Nothing Much i love reading and developing new things and thats about it

Comments and Discussions

 
Questionissues in class RandomObjectsGenerator Pin
sx200819-Dec-14 12:52
sx200819-Dec-14 12:52 
AnswerRe: issues in class RandomObjectsGenerator Pin
saddam abu ghaida19-Dec-14 19:38
professionalsaddam abu ghaida19-Dec-14 19:38 
Questionsilly Pin
Rahman Mahmoodi16-Dec-14 10:28
Rahman Mahmoodi16-Dec-14 10:28 
You mean your silly thing is better than AutoFixture?
AnswerRe: silly Pin
Garth J Lancaster16-Dec-14 11:55
professionalGarth J Lancaster16-Dec-14 11:55 
AnswerRe: silly Pin
saddam abu ghaida16-Dec-14 19:34
professionalsaddam abu ghaida16-Dec-14 19:34 
GeneralRe: silly Pin
Rahman Mahmoodi16-Dec-14 20:07
Rahman Mahmoodi16-Dec-14 20:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.