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>
:
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(
Expression.Equal(value, Expression.Constant(DBNull.Value)),
Expression.Default(type),
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.
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> ();
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<PropertyInfo> masterPropertyInfoFields = new List>PropertyInfo> ();
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:
- 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.
History
- 16/12/2014: Initial version