Mapping IDataReader to object without third party libraries - c#

Are there any quick way to map some IDataReader to object without third party libraries such as AutoMapper or ValueInjecter?

I'm not sure what you mean by quick, but you can put something together using reflection. There's a lot of assumptions you'll have to make, such as all your object's values are set via properties. And, your DataReader columns MUST match your object property name. But you could do something like this:
NOTE: The SetProperty function is from an article on DevX. (It was in VB.NET, and I converted it to C# -- if there are mistakes, I probably missed something.)
IList<MyObject> myObjList = new List<MyObject>();
while (reader.Read()){
int fieldCount = reader.FieldCount;
MyObject myObj = new MyObject();
for(int i=0;i<fieldCount;i++){
SetProperty(myObj, reader.GetName(i), reader.GetOrdinal(i));
}
myObjList.Add(myObj);
}
bool SetProperty(object obj, String propertyName, object val) {
try {
//get a reference to the PropertyInfo, exit if no property with that
//name
System.Reflection.PropertyInfo pi = obj.GetType().GetProperty(propertyName);
if (pi == null) then return false;
//convert the value to the expected type
val = Convert.ChangeType(val, pi.PropertyType);
//attempt the assignment
pi.SetValue(obj, val, null);
return true;
}
catch {
return false;
}
}
No guarantees that this code will run (I'm just typing it and not compiling/testing it), but it at least may be a start.
I've done things like this in the past and have gotten fancy with applying attributes to my properties stating what data reader column to map to the property. Too much to include here, but this is just a starter and hopefully is what you're looking for.
Hope this helps!

Sure,
class MyObject
{
public string SomeProperty { get; set; }
public MyObject(IDataReader reader)
{
SomeProperty = reader.GetString(0);
// - or -
SomeProperty = reader.GetString(reader.GetOrdinal("SomeProperty"));
// etc.
}
}

Related

c# SetIfChanged-Method with Expressions

I have to following C#-code quite a lot:
public void UpdateDB(Model.ResultContext db)
{
Model.Planning.Part dbPart = db.Parts.Where(/*someClause*/).FirstOrDefault();
//The awkward part
if (dbPart.Number != Number)
{
dbPart.Number = Number;
}
if (dbPart.NumberShort != NumberShort)
{
dbPart.NumberShort = NumberShort;
}
if (dbPart.Designation != Designation)
{
dbPart.Designation = Designation;
}
}
It is obviously kind of awkward to check every field and wrap it in if != then set
Yes, the check is needed because otherwise the database sees everything as changed columns.
The fields to set are auto-Properties:
public class Part
{
[MaxLength(36), MinLength(1)]
public string Number { get; set; } = null!;
[MaxLength(80)]
public string Designation { get; set; } = null!;
}
and I don't want to write an explicit setter for every field which of course could do the checking before setting.
So what I thought of is some Method ´SetIfChanged´ which is called like this to make the code more readable and less error-prone:
//Options
dbPart.SetIfChanged(dbPart.Number, this.Number);
dbPart.SetIfChanged(dbPart.Number = this.Number);
dbPart.SetIfChanged(Number, this.Number);
I think something like that is possible with expressions or lambdas but to be honest... I'm stuck with the syntax of declaring and calling such a method
Anybody can help me out?
Unfortunately, C# is lacking a number of things to help you with this (e.g. property refs or extension methods on reference objects), but you can use Reflection to help with this. It is likely to be quite slow, however.
With a method that takes a lambda, you can write a set method:
public static void SetIfDifferent<T>(Expression<Func<T>> getterFnE, T newVal) {
var me = (MemberExpression)getterFnE.Body;
var target = me.Expression;
var targetLambda = Expression.Lambda(target);
var prop = me.Member;
var oldVal = getterFnE.Compile().Invoke();
if ((oldVal == null && newVal != null) || !oldVal.Equals(newVal)) {
var obj = targetLambda.Compile().DynamicInvoke();
prop.SetValue(obj, newVal);
}
}
This would be used like:
SetIfDifferent(() => dbPart.Number, Number);
SetIfDifferent(() => dbPart.NumberShort, NumberShort);
SetIfDifferent(() => dbPart.Designation, Designation);
This would be slow because of the need to compile the Expression trees and use DynamicInvoke. One way to speed it up would be to pass in a setter and getter lambda instead, but that leads to as much duplication as your original code.
If you would be willing to pass the object and name of the property instead, you could use:
public static T GetValue<T>(this MemberInfo member, object srcObject) => (T)member.GetValue(srcObject);
public static void SetIfDifferent2<TObj, TField>(this TObj obj, string fieldName, TField newVal) {
var prop = typeof(TObj).GetProperty(fieldName);
var oldVal = prop.GetValue<TField>(fieldName);
if ((oldVal == null && newVal != null) || !oldVal.Equals(newVal))
prop.SetValue(obj, newVal);
}
Which you could use like:
dbPart.SetIfDifferent2(nameof(dbPart.Number), Number);
dbPart.SetIfDifferent2(nameof(dbPart.NumberShort), NumberShort);
dbPart.SetIfDifferent2(nameof(dbPart.Designation), Designation);
Unfortunately, it requires repeating dbPart unless you are willing to just put in the field name (e.g. "Number") but that will cause runtime errors if the field changes.
You could also cache the PropertyInfo instead of looking it up with GetProperty, but that is generally pretty fast and caching probably isn't worth it.
Well if you really need checking (lets say at the end you want to know if anything has been changed or not) you can use Reflection and loop through properties. but in your case no check is needed.
take this for instance:
if (dbPart.Number != Number)
{
dbPart.Number = Number;
}
true) if the value is different you are setting the new one
false) means that the new value and the old value are the same, so doesn't hurt to set it again
If you want to know if anything has changed at the end:
bool changed = false;
var type = dbPart.GetType();
foreach(var (PropertyInfo)pi in type.GetProperties()
{
if(pi.GetValue(dbPart) != newValue)
{
changed = true;
pi.SetValue(dbPart, newValue);
}
}
or you can do something like:
bool changed = dbPart.Number != Number || dbPart.Designation != Designation;
dbPart.Number = Number;
dbPart.Designation = Designation;

Using Reflection to copy across equivalent properties between struct and class

I'm making a game (see http://haighshroom.blogspot.com for context) and saving/loading the game using an XML file. It currently uses a different struct for the save format of each class, but continuing to update the save file structure is proving tiresome so I want to try and adopt the following generalised method.
Let's assume I have 3 classes, which all inherit from BaseClass:
Class1, which has Property1, Property2 (let's say all properties are ints)
Class2, which has Property2, Property3
Class3, which has Property1, Property3
Now my new generalised SaveStruct would look like this:
public struct EntityStruct
{
public string ClassName;
public int Property1;
public int Property2;
public int Property3;
public EntityStruct()
{
ClassName = "";
Property1 = Property2 = Property3 = 0;
}
}
When saving/loading a particular entity, I want to achieve the following Pseudocode (both functions called from BaseClass):
public EntityStruct GetSaveStruct()
{
EntityStruct es = new EntityStruct();
es.ClassName = this.GetType().Name;
if Exists(this.Property1) es.Property1 = Get(this.Property1);
if Exists(this.Property2) es.Property2 = Get(this.Property2);
if Exists(this.Property3) es.Property3 = Get(this.Property3);
}
public void LoadFromStruct(EntityStruct es)
{
BaseClass e = (BaseClass)(Activator.CreateInstance(null, GV.NameSpace + es.ClassName).Unwrap());
if Exists(e.Property1) Set(e.Property1 = es.Property1);
if Exists(e.Property2) Set(e.Property2 = es.Property2);
if Exists(e.Property3) Set(e.Property3 = es.Property3);
}
The parts I don't know how to define are:
-Exists(e.Property1) - this needs to use Reflection to determine whether the instance e has Property1 defined (we are calling this from BaseClass so we don't know without using Reflection).
-Get(e.Property1) - if Property 1 does exist for an instance e, we need to get its value
-Set(e.Property1 = es.Property1) - if Property 1 does exist from an instance e, we need to set its value
Many thanks.
you can try use this code
public void ShallowCopyValues<T1, T2>(T1 firstObject, T2 secondObject)
{
const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
var firstFieldDefinitions = firstObject.GetType().GetFields(bindingFlags);
IEnumerable<FieldInfo> secondFieldDefinitions = secondObject.GetType().GetFields(bindingFlags);
foreach (var fieldDefinition in firstFieldDefinitions)
{
var matchingFieldDefinition = secondFieldDefinitions.FirstOrDefault(fd => fd.Name == fieldDefinition.Name &&
fd.FieldType == fieldDefinition.FieldType);
if (matchingFieldDefinition == null)
continue;
var value = fieldDefinition.GetValue(firstObject);
matchingFieldDefinition.SetValue(secondObject, value);
}
}
The starting point for all this is the System.Type class. You can get an instance of this for your type using e.GetType().
To look for a field, use GetField. If that returns null, then the field doesn't exist at all.
If it returns a value (of type FieldInfo) then use GetValue to get the value and SetValue to set it.
Reflection is relatively slow, so if performance is a concern, grab the System.Type object ahead of time with something like System.Type.getType(name) and also get the FieldInfo objects. You don't need the actual instance of the class to do either of those two operations, though obviously you need it to get and set the field values.

Fill struct with String[]?

I'm parsing a CSV file and placing the data in a struct. I'm using the TextFieldParser from this question and it's working like a charm except that it returns a String[]. Currently I have the ugly process of:
String[] row = parser.ReadFields();
DispatchCall call = new DispatchCall();
if (!int.TryParse(row[0], out call.AccountID)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
continue;
}
call.WorkOrder = row[1];
call.Description = row[2];
call.Date = row[3];
call.RequestedDate = row[4];
call.EstStartDate = row[5];
call.CustomerID = row[6];
call.CustomerName = row[7];
call.Caller = row[8];
call.EquipmentID = row[9];
call.Item = row[10];
call.TerritoryDesc = row[11];
call.Technician = row[12];
call.BillCode = row[13];
call.CallType = row[14];
call.Priority = row[15];
call.Status = row[16];
call.Comment = row[17];
call.Street = row[18];
call.City = row[19];
call.State = row[20];
call.Zip = row[21];
call.EquipRemarks = row[22];
call.Contact = row[23];
call.ContactPhone = row[24];
call.Lat = row[25];
call.Lon = row[26];
call.FlagColor = row[27];
call.TextColor = row[28];
call.MarkerName = row[29];
The struct consists of all those fields being Strings except for AccountID being an int. It annoys me that they're not strongly typed, but let's over look that for now. Given that parser.ReadFields() returns a String[] is there a more efficient way to fill a struct (possibly converting some values such as row[0] needing to become an int) with the values in the array?
**EDIT:**One restriction I forgot to mention that may impact what kind of solutions will work is that this struct is [Serializable] and will be sent Tcp somewhere else.
Your mileage may vary on whether it is a better solution, but you could use reflection and define an Attribute class that you use to mark your struct members with. The attribute would take the array index as an argument. Assigning the value from the right array element would then happen by using reflection.
You could define your attribute like this:
[AttributeUsage(AttributeTargets.Property)]
public sealed class ArrayStructFieldAttribute : Attribute
{
public ArrayStructFieldAttribute(int index)
{
this.index = index;
}
private readonly int index;
public int Index {
get {
return index;
}
}
}
This means the attribute can simply be used to associate an int value named Index with a property.
Then, you could mark your properties in the struct with that attribute (just some exemplary lines):
[ArrayStructField(1)]
public string WorkOrder { // ...
[ArrayStructField(19)]
public string City { // ...
The values could then be set with the Type object for your struct type (you can obtain it with the typeof operator):
foreach (PropertyInfo prop in structType.GetProperties()) {
ArrayStructFieldAttribute attr = prop.GetCustomAttributes(typeof(ArrayStructFieldAttribute), false).Cast<ArrayStructFieldAttribute>().FirstOrDefault();
if (attr != null) {
// we have found a property that you want to load from an array element!
if (prop.PropertyType == typeof(string)) {
// the property is a string property, no conversion required
prop.SetValue(boxedStruct, row[attr.Index]);
} else if (prop.PropertyType == typeof(int)) {
// the property is an int property, conversion required
int value;
if (!int.TryParse(row[attr.Index], out value)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
} else {
prop.SetValue(boxedStruct, value);
}
}
}
}
This code iterates over all properties of your struct type. For each property, it checks for our custom attribute type defined above. If such an attribute is present, and if the property type is string or int, the value is copied from the respective array index.
I am checking for string and int properties as that's the two data types you mentioned in your question. even though you have only one particular index that contains an int value now, it's good for maintainability if this code is prepared to handle any index as a string or an int property.
Note that for a greater number of types to handle, I'd suggest not using a chain of if and else if, but rather a Dictionary<Type, Func<string, object>> that maps property types to conversion functions.
If you want to create something very flexible you can mark each property on DispatchCall using a custom attribute. Something like this:
class DispatchCall {
[CsvColumn(0)]
public Int32 AccountId { get; set; }
[CsvColumn(1)]
public String WorkOrder { get; set; }
[CsvColumn(3, Format = "yyyy-MM-dd")]
public DateTime Date { get; set; }
}
This allows you to associate each property with a column. For each row you can then iterate over all properties and by using the attribute you can assign the right value to the right property. You will have to do some type conversion from string to numbers, dates and perhaps enums. You can add extra properties to the attribute to assist you in that process. In the example I invented Format which should be used when a DateTime is parsed:
Object ParseValue(String value, TargetType targetType, String format) {
if (targetType == typeof(String))
return value;
if (targetType == typeof(Int32))
return Int32.Parse(value);
if (targetType == typeof(DateTime))
DateTime.ParseExact(value, format, CultureInfo.InvariantCulture);
...
}
Using TryParse methods in the above code can improve the error handling by allowing you to provide more context when an unparsable value is encountered.
Unfortunately, this approach is not very efficient because the reflection code will be executed for each row in your input file. If you want to make this more efficient you need to dynamically create a compiled method by reflecting once over DispatchCall that you then can apply on each row. It is possible but not particular easy.
How dependent are you on the library that you're using? I've found File Helpers to be quite useful for this sort of thing. Your code would look something like:
using FileHelpers;
// ...
[DelimitedRecord(",")]
class DispatchCall {
// Just make sure these are in order
public int AccountID { get; set; }
public string WorkOrder { get; set; }
public string Description { get; set; }
// ...
}
// And then to call the code
var engine = new FileHelperEngine(typeof(DispatchCall));
engine.Options.IgnoreFirstLines = 1; // If you have a header row
DispatchCall[] data = engine.ReadFile(FileName) as DispatchCall[];
You now have a DispatchCall array, and the engine did all the heavy lifting for you.
Use reflection as #Grozz suggested in the comment. Mark each property of the struct class with an attribute (ie [ColumnOrdinal] ) and then use this to map the information with the proper column. If you have double, decimal and so on as a target, you should also consider using Convert.ChangeType to proper convert in the target type. if you are not happy with the performances, you can enjoy create a DynamicMethod on the fly, more challenging, but really performant and beautiful. The challenge is to write the IL instruction in memory to do the "plumbing" you did by hand ( I usually create some example code, and then look inside it with IL spy as a starting point ). of course you will cache somewhere such dynamic methods so creating them is requested just once.
The first thing that comes to mind is to use reflection to iterate over the properties and match them up to the elements in the string[] based on an attribute value.
public struct DispatchCall
{
[MyAttribute(CsvIndex = 1)]
public string WorkOrder { get; set; }
}
MyAttribute would just be a custom attribute with an index that would match up to the field position in the CSV.
var row = parser.ReadFields();
for each property that has MyAttribute...
var indexAttrib = MyAttribute attached to property
property.Value = row[indexAttrib.Index]
next
(Pseudocode, obviously)
or
[StructLayout(LayoutKind.Sequential)] // keep fields in order
public strict DispatchCall
{
public string WorkOrder;
public string Description;
}
StructLayout will keep the struct fields in order, so you can iterate over them without having to explicitly specify a column number for each field. That can save some maintenance if you have a lot of fields.
Or, you could skip the process entirely, and store the field names in a dictionary:
var index = new Dictionary<int, string>();
/// populate index with row index : field name values, preferable from some sort of config file or database
index[0] = "WorkOrder";
index[1] = "Description";
...
var values = new Dictionary<string,object>();
for(var i=0;i<row.Length;i++)
{
values.Add(index[i],row[i]);
}
That's easier to load, but doesn't really take advantage of strong typing, which makes this less than ideal.
You could also generate a dynamic method or a T4 template. You could generate code from a mapping file in the format
0,WorkOrder
1,Description
...
load that, and generate a method that looks like this:
/// emit this
call.WorkOrder = row[0];
call.Description = row[1];
etc.
That approach is used in a few micro-ORMs floating around and seems to work pretty well.
Ideally, your CSV would include a row with field names that would make this a lot easier.
OR, yet another approach, use StructLayout along with a dynamic method to avoid having to keep a field:column_index mapping aside from the struct itself.
OR, create an enum
public enum FieldIndex
{
WorkOrder=0
,
Description // only have to specify explicit value for the first item in the enum
, /// ....
,
MAX /// useful for getting the maximum enum integer value
}
for(var i=0;i<FieldIndex.MAX;i++)
{
var fieldName = ((FieldIndex)i).ToString(); /// get string enum name
var value = row[i];
// use reflection to find the property/field FIELDNAME, and set it's value to VALUE.
}
if you are going for speed you could a brittle switch statement.
var columns = parser.ReadFields();
for (var i = 0; i < columns.Length; i++)
{
SetValue(call, i, columns[i]);
}
private static void SetValue(DispatchCall call, int column, string value)
{
switch column
{
case 0:
SetValue(ref call.AccountId, (value) => int.Parse, value);
return;
case 1:
SetValue(ref call.WorkOrder, (value) => value, value);
return;
...
default:
throw new UnexpectedColumnException();
}
}
private static void SetValue<T>(
ref T property,
Func<string, T> setter
value string)
{
property = setter(value);
}
Its a shame that TextFieldParser does not allow you to read one field at a time, then you could avoid building and indexing the columns array.

Using reflection in C# to modify fields in a loop, extension method

After retrieving data from a database I find myself doing this to create a domain object from the data in a DataRow (in this case, a DVD):
DataRow drDvd = myDataTable.Rows[0];
Dvd myDvd = new Dvd();
myDvd.id = drDvd.Field<long>("id");
myDvd.title = drDvd.Field<string>("title");
myDvd.description = drDvd.Field<string>("description");
myDvd.releaseDate = drDvd.Field<DateTime>("releaseDate");
As I soon felt of course, I am doing this over and over in pseudo-code:
myDvd.field = drDvd.Field<field.type>(field.name);
And I wondered if I could get it into a loop, however I've never used reflection before. The code I tried is this:
Dvd aDvd = new Dvd();
Type t = aDvd.GetType();
FieldInfo[] fields = t.GetFields();
foreach (FieldInfo fi in fields)
{
fi.SetValue(aDvd, drDvd.Field<fi.FieldType>(fi.Name));
}
The problem is, as you may know, that the extension for the Field method of class DataRow does not accept a variable and needs to be explicitely filled in.
I am not that experienced in C# so I would like to pose the following two questions:
Is it good practice what I am trying to do?
How can I fill in the correct extension for Field<extension>(name)?
You'll need to get the method info for the generic method, and call invoke on it. This way you can pass in the generic type to it programmatically. I'm on my phone, but it should look something like this:
MethodInfo mField = typeof(Dvd).GetMethod("Field");
MethodInfo genericMethod = mField.MakeGenericMethod(new Type[] { fi.FieldType });
GenericMethod.Invoke(aDvd,new Object[]{fi.Name});
It is usually a bad practice to use reflection when it is not really necessary. Because reflection methods are checked at runtime rather than compile time, faulty code is harder to track, because the compiler can't check for errors.
If I were you, id have a look at the Entity Framework, because youre basically mapping database data to domain objects. http://msdn.microsoft.com/en-us/library/aa697427%28v=vs.80%29.aspx
This is one of the way of constructing and populating your domain object
DataRow drDvd = new DataRow();
Dvd aDvd = new Dvd();
Type type = typeof(Dvd);
foreach (FieldInfo fi in type.GetFields())
{
fi.SetValue(aDvd, drDvd[fi.Name]);
}
Your approach of using DataRow.Field may be round about. In you case, it is not applicable.
Alternatively you can think about using one of the Entity frameworks (NHibernate, Microsoft EF etc) in your application.
I would do a custom attribute. In doing an attribute you are stuck with your field name being the same as the database. I currently use this in my current applications and it works great. It is very similar to Entity SQL.
public class SqlMetaAttribute : Attribute
{
public string ColumnName { get; set; }
}
Then you have your class like this
public class Person
{
[SqlMeta(ColumnName = "First_Name")]
publice string FirstName { get; set; }
[SqlMeta(ColumnName = "Last_Name")]
publice string LastName { get; set; }
}
You would then have a helper class with the same kind of functions. In this case I am assuming the outside caller is looping through the datatable. Making it generic using the template T makes this really reusable. Rather than just having a "DVD" type implementation and coping and pasting for another.
public static T CreateObjectFromRow<T>(DataRow row)
{
var newObject = new T();
if (row != null) SetAllProperties(row, newObject);
return newObject;
}
public static void SetAllProperties<T>(DataRow row, T newObject)
{
var properties = typeof(T).GetProperties();
foreach(var propertyInfo in properties)
{
SetPropertyValue(row, newObject, propertyInfo);
}
}
public static void SetPropertyValue(DataRow row, T newObject, PropertyInfo propertyInfo)
{
var columnAttribute = propertyInfo.FindAttribute<SqlMetaAttribute>();
if (columnAttribute == null) return;
// If the row type is different than the object type and exception will be thrown, but that is
// okay because if that happens you have to fix your object you are using, or might need some
// more custom code to help you with that.
propertyInfo.SetValue(newObject, row.GetValue<object>(columnAttribute.ColumnName), null);
}
// Extension method for row.GetValue<object> used above
public static T GetValue<T>(this DataRow row, string columnName)
{
if (row.ColumnNameNotFound(columnName) || row.Table.Columns[columnName] == null || row[columnName] is DBNull)
{
return default(T);
}
return (T)row[columnName];
}

Using LINQ to create a List<T> where T : someClass<U>

This is related to a prior question of mine C# Generic List conversion to Class implementing List<T>
I have the following code:
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
public static List<DataField> ConvertXML(XMLDocument data) {
result = (from d in XDocument.Parse(data.OuterXML).Root.Decendendants()
select new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
}).Cast<DataField>().ToList();
return result;
}
This works however I would like to be able to modify the select portion of the LINQ query to be something like this:
select new DataField<[type defined in attribute of XML Element]>
{
Name = d.Name.ToString(),
Value = d.Value
}
Is this just a poor approach? is it possible? Any suggestions?
Here is a working solution: (You must specify fully qualified type names for your Type attribute otherwise you have to configure a mapping somehow...)
I used the dynamic keyword, you can use reflection to set the value instead if you do not have C# 4...
public static void Test()
{
string xmlData = "<root><Name1 Type=\"System.String\">Value1</Name1><Name2 Type=\"System.Int32\">324</Name2></root>";
List<DataField> dataFieldList = DataField.ConvertXML(xmlData);
Debug.Assert(dataFieldList.Count == 2);
Debug.Assert(dataFieldList[0].GetType() == typeof(DataField<string>));
Debug.Assert(dataFieldList[1].GetType() == typeof(DataField<int>));
}
public abstract class DataField
{
public string Name { get; set; }
/// <summary>
/// Instanciate a generic DataField<T> given an XElement
/// </summary>
public static DataField CreateDataField(XElement element)
{
//Determine the type of element we deal with
string elementTypeName = element.Attribute("Type").Value;
Type elementType = Type.GetType(elementTypeName);
//Instanciate a new Generic element of type: DataField<T>
dynamic dataField = Activator.CreateInstance(typeof(DataField<>).MakeGenericType(elementType));
dataField.Name = element.Name.ToString();
//Convert the inner value to the target element type
dynamic value = Convert.ChangeType(element.Value, elementType);
//Set the value into DataField
dataField.Value = value;
return dataField;
}
/// <summary>
/// Take all the descendant of the root node and creates a DataField for each
/// </summary>
public static List<DataField> ConvertXML(string xmlData)
{
var result = (from d in XDocument.Parse(xmlData).Root.DescendantNodes().OfType<XElement>()
select CreateDataField(d)).ToList();
return result;
}
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
You cannot do this easily in C#. The generic type argument has to specified at compile time. You can use reflection to do otherwise
int X = 1;
Type listype = typeof(List<>);
Type constructed = listype.MakeGenericType( X.GetType() );
object runtimeList = Activator.CreateInstance(constructed);
Here we have just created a List<int>. You can do it with your type
Different instances of a generic class are actually different classes.
I.e. DataField<string> and DataField<int> are not the same class at all(!)
This means, that you can not define the generic parameter during run-time, as it has to be determined during compile-time.
I would say this is a poor approach. In reality, even after you parse your XML file, you're not going to know what types of "DataFields" you have. You might as well just parse them as objects.
However, if you know that you're only ever going to have x number of types, you can do like so:
var Dictionary<string, Func<string, string, DataField>> myFactoryMaps =
{
{"Type1", (name, value) => { return new DataField<Type1>(name, Type1.Parse(value); } },
{"Type2", (name, value) => { return new DataField<Type2>(name, Type2.Parse(value); } },
};
Termit's answer is certainly excellent. Here is a little variant.
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
public Type GenericType { get { return this.Value.GetType(); } }
}
static Func<XElement , DataField> dfSelector = new Func<XElement , DataField>( e =>
{
string strType = e.Attribute( "type" ).Value;
//if you dont have an attribute type, you could call an extension method to figure out the type (with regex patterns)
//that would only work for struct
Type type = Type.GetType( strType );
dynamic df = Activator.CreateInstance( typeof( DataField<>).MakeGenericType( type ) );
df.Name = e.Attribute( "name" ).Value;
dynamic value = Convert.ChangeType( e.Value , type );
df.Value = value;
return df;
} );
public static List<DataField> ConvertXML( string xmlstring )
{
var result = XDocument.Parse( xmlstring )
.Root.Descendants("object")
.Select( dfSelector )
.ToList();
return result;
}
static void Main( string[] args )
{
string xml = "<root><object name=\"im1\" type=\"System.String\">HelloWorld!</object><object name=\"im2\" type=\"System.Int32\">324</object></root>";
List<DataField> dfs = ConvertXML( xml );
}
you can create generic type by reflection
var instance = Activator.CreateInstance( typeof(DataField)
.MakeGenericType(Type.GetType(typeNameFromAttribute) );
// and here set properties also by reflection
#Termit and #Burnzy put forward good solutions involving factory methods.
The problem with that is that you're loading up your parsing routine with a bunch of extra logic (more testing, more errors) for dubious returns.
Another way to do it would be to use a simplified string-based DataField with typed read methods - the top answer for this question.
An implementation of a typed-value method that would be nice but only works for value types (which does not include strings but does include DateTimes):
public T? TypedValue<T>()
where T : struct
{
try { return (T?) Convert.ChangeType(this.Value, typeof(T)); }
catch { return null; }
}
I'm assuming that you're wanting to use the type information to do things like dynamically assigning user-controls to the field, validation rules, correct SQL types for persistence etc.
I've done a lot of this sort of thing with approaches that seem a bit like yours.
At the end of the day you should seperate your metadata from your code - #Burnzy's answer chooses the code based on the metadata (a "type" attribute of the DataField element) and is a very simple example of this.
If you're dealing with XML, XSDs are a very useful and extensible form of metadata.
As far as what you store each field's data in - use strings because:
they are nullable
they can store partial values
they can store invalid values (makes telling the user to sort their act out more transparent)
they can store lists
special cases won't invade unrelated code because there aren't any
learn regular expressions, validate, be happy
you can convert them to stronger types really easily
I found it very rewarding to develop little frameworks like this - it is a learning experience and you'll come out understanding a lot more about UX and the reality of modelling from it.
There are four groups of test cases that I would advise you to tackle first:
Dates, Times, Timestamps (what I call DateTime), Periods (Timespan)
in particular, make sure you test having a different server locality from the client's.
lists - multi-select foreign keys etc
null values
invalid input - this generally involves retaining the original value
Using strings simplifies all this greatly because it allows you to clearly demarcate responsibilities within your framework. Think about doing fields containing lists in your generic model - it gets hairy rather quickly and it is easy to end up with a special case for lists in pretty much every method. With strings, the buck stops there.
Finally, if you want a solid implementation of this sort of stuff without having to do anything much, consider DataSets - old school I know - they do all sorts of wonderful things you wouldn't expect but you do have to RTFM.
The main downfall of that idea would be that it isn't compatible with WPF data binding - though my experience has been that reality isn't compatible with WPF data binding.
I hope I interpreted your intentions correctly - good luck either way :)
Unfortunately, there no inheritance relation between C<T> and C<string> for instance.
However, you can inherit from a common non-generic class and in addition to this implement a generic interface.
Here I use explicit interface implementation in order to be able to declare a Value property typed as object, as well as a more specifically typed Value property.
The Values are read-only and can only be assigned through a typed constructor parameter. My construction is not perfect, but type safe and doesn't use reflection.
public interface IValue<T>
{
T Value { get; }
}
public abstract class DataField
{
public DataField(string name, object value)
{
Name = name;
Value = value;
}
public string Name { get; private set; }
public object Value { get; private set; }
}
public class StringDataField : DataField, IValue<string>
{
public StringDataField(string name, string value)
: base(name, value)
{
}
string IValue<string>.Value
{
get { return (string)Value; }
}
}
public class IntDataField : DataField, IValue<int>
{
public IntDataField(string name, int value)
: base(name, value)
{
}
int IValue<int>.Value
{
get { return (int)Value; }
}
}
The list can then be declared with the abstract base class DataField as generic parameter:
var list = new List<DataField>();
switch (fieldType) {
case "string":
list.Add(new StringDataField("Item", "Apple"));
break;
case "int":
list.Add(new IntDataField("Count", 12));
break;
}
Access the strongly typed field through the interface:
public void ProcessDataField(DataField field)
{
var stringField = field as IValue<string>;
if (stringField != null) {
string s = stringField.Value;
}
}
While the other questions mostly proposed an elegant solution to convert your XML elements to a generic class instance, I'm going to deal with the consequences of taking the approach to model the DataField class as a generic like DataField<[type defined in attribute of XML Element]>.
After selecting your DataField instance into the list you want to use these fields. Her polymorphism comes into play! You want to iterate your DataFields an treat them in a uniform way. Solutions that use generics often end up in a weird switch/if orgy since there is no easy way to associate behavior based on the generic type in c#.
You might have seen code like this (I'm trying to calculate the sum of all numeric DataField instances)
var list = new List<DataField>()
{
new DataField<int>() {Name = "int", Value = 2},
new DataField<string>() {Name = "string", Value = "stringValue"},
new DataField<float>() {Name = "string", Value = 2f},
};
var sum = 0.0;
foreach (var dataField in list)
{
if (dataField.GetType().IsGenericType)
{
if (dataField.GetType().GetGenericArguments()[0] == typeof(int))
{
sum += ((DataField<int>) dataField).Value;
}
else if (dataField.GetType().GetGenericArguments()[0] == typeof(float))
{
sum += ((DataField<float>)dataField).Value;
}
// ..
}
}
This code is a complete mess!
Let's go try the polymorphic implementation with your generic type DataField and add some method Sum to it that accepts the old some and returns the (possibly modified) new sum:
public class DataField<T> : DataField
{
public T Value { get; set; }
public override double Sum(double sum)
{
if (typeof(T) == typeof(int))
{
return sum + (int)Value; // Cannot really cast here!
}
else if (typeof(T) == typeof(float))
{
return sum + (float)Value; // Cannot really cast here!
}
// ...
return sum;
}
}
You can imagine that your iteration code gets a lot clearer now but you still have this weird switch/if statement in you code. And here comes the point: Generics do not help you here it's the wrong tool at the wrong place. Generics are designed in C# for giving you compile time type safety to avoid potential unsafe cast operations. They additionally add to code readability but that's not the case here :)
Let's take a look at the polymorphic solution:
public abstract class DataField
{
public string Name { get; set; }
public object Value { get; set; }
public abstract double Sum(double sum);
}
public class IntDataField : DataField
{
public override double Sum(double sum)
{
return (int)Value + sum;
}
}
public class FloatDataField : DataField
{
public override double Sum(double sum)
{
return (float)Value + sum;
}
}
I guess you will not need too much fantasy to imagine how much adds to your code's readability/quality.
The last point is how to create instances of these classes. Simply by using some convention TypeName + "DataField" and Activator:
Activator.CreateInstance("assemblyName", typeName);
Short Version:
Generics is not the appropriate approach for your problem because it does not add value to the handling of DataField instances. With the polymorphic approach you can work easily with the instances of DataField!
It's not impossible as you can do this with reflection. But this isn't what generics were designed for and isn't how it should be done. If you're going to use reflection to make the generic type, you may as well not use a generic type at all and just use the following class:
public class DataField
{
public string Name { get; set; }
public object Value { get; set; }
}
You'll need to insert the logic for determining the data type from your XML and add all the types you need to use but this should work:
result = (from d in XDocument.Parse(data.OuterXML).Root.Descendants()
let isString = true //Replace true with your logic to determine if it is a string.
let isInt = false //Replace false with your logic to determine if it is an integer.
let stringValue = isString ? (DataField)new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
} : null
let intValue = isInt ? (DataField)new DataField<int>
{
Name = d.Name.ToString(),
Value = Int32.Parse(d.Value)
} : null
select stringValue ?? intValue).ToList();

Categories