I'm using EF4.3 so I'm referring to entities, however it could apply to any class containing properties.
I'm trying to figure out if its possible to compare 2 entities. Each entity has properties that are assigned values for clarity let say the entity is 'Customer'.
public partial class Customer
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
The customer visits my website and types in some details 'TypedCustomer'. I check this against the database and if some of the data matches, I return a record from the database 'StoredCustomer'.
So at this point I've identified that its the same customer returning but I wan't to valid the rest of the data. I could check each property one by one, but there are a fair few to check. Is it possible to make this comparison at a higher level which takes into account the current values of each?
if(TypedCustomer == StoredCustomer)
{
.... do something
}
If you're storing these things in the database, it is logical to assume you'd also have a primary key called something like Id.
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
Then all you do is:
if(TypedCustomer.Id == StoredCustomer.Id)
{
}
UPDATE:
In my project, I have a comparer for these circumstances:
public sealed class POCOComparer<TPOCO> : IEqualityComparer<TPOCO> where TPOCO : class
{
public bool Equals(TPOCO poco1, TPOCO poco2)
{
if (poco1 != null && poco2 != null)
{
bool areSame = true;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object v1 = property.GetValue(poco1, null);
object v2 = property.GetValue(poco2, null);
if (!object.Equals(v1, v2))
{
areSame = false;
break;
}
});
return areSame;
}
return poco1 == poco2;
} // eo Equals
public int GetHashCode(TPOCO poco)
{
int hash = 0;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object val = property.GetValue(poco, null);
hash += (val == null ? 0 : val.GetHashCode());
});
return hash;
} // eo GetHashCode
} // eo class POCOComparer
Uses an extension method:
public static partial class TypeExtensionMethods
{
public static PropertyInfo[] GetPublicProperties(this Type self)
{
self.ThrowIfDefault("self");
return self.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where((property) => property.GetIndexParameters().Length == 0 && property.CanRead && property.CanWrite).ToArray();
} // eo GetPublicProperties
} // eo class TypeExtensionMethods
Most simple seems to use reflexion : get the properties and/or fields you want to compare, and loop through them to compare your two objects.
This will be done with getType(Customer).getProperties and getType(Customer).getFields, then using getValue on each field/property and comparing.
You might want to add custom informations to your fields/properties to define the ones that needs
comparing. This could be done by defining a AttributeUsageAttribute, that would inherit from FlagsAttribute for instance. You'll then have to retrieve and handle those attributes in your isEqualTo method.
I don't think there's much of a purpose to checking the entire object in this scenario - they'd have to type every property in perfectly exactly as they did before, and a simple "do they match" doesn't really tell you a lot. But assuming that's what you want, I can see a few ways of doing this:
1) Just bite the bullet and compare each field. You can do this by overriding the bool Equals method, or IEquatable<T>.Equals, or just with a custom method.
2) Reflection, looping through the properties - simple if your properties are simple data fields, but more complex if you've got complex types to worry about.
foreach (var prop in typeof(Customer).GetProperties()) {
// needs better property and value validation
bool propertyMatches = prop.GetValue(cust1, null)
.Equals(prop.GetValue(cust2, null));
}
3) Serialization - serialize both objects to XML or JSON, and compare the strings.
// JSON.NET
string s1 = JsonConvert.SerializeObject(cust1);
string s2 = JsonConvert.SerializeObject(cust2);
bool match = s1 == s2;
Related
Having spent a long time solving this problem, I wanted to share the solution.
Background
I maintain a large web application with the primary function of managing orders. It is an MVC over C# application using EF6 for data.
There are LOTS of search screens. The search screens all have multiple parameters and return different object types.
The Problem
Every search screen had:
A ViewModel with the search parameters
A Controller method to handle the Search event
A method to pull the correct data for that screen
A method to apply all the search filters to the dataset
A method to convert the results into a NEW results ViewModel
The Results ViewModel
This adds up quickly. We have about 14 different search screens, which means about 84 models & methods to handle these searches.
My Goal
I wanted to be able to create a class, analogous to the current search parameter ViewModel, that would inherit from a base SearchQuery class such that my Controller could simply trigger the search to run to populate a Results field of the same object.
An Example of My Ideal State (Because It's a Bear To Explain)
Take the following class structure:
public class Order
{
public int TxNumber;
public Customer OrderCustomer;
public DateTime TxDate;
}
public class Customer
{
public string Name;
public Address CustomerAddress;
}
public class Address
{
public int StreetNumber;
public string StreetName;
public int ZipCode;
}
Let's assume I have lots of those records in a queryable format--an EF DBContext object, an XML object, whatever--and I want to search them. First, I create a derived class specific to my ResultType(in this case, Order).
public class OrderSearchFilter : SearchQuery
{
//this type specifies that I want my query result to be List<Order>
public OrderSearchFilter() : base(typeof(Order)) { }
[LinkedField("TxDate")]
[Comparison(ExpressionType.GreaterThanOrEqual)]
public DateTime? TransactionDateFrom { get; set; }
[LinkedField("TxDate")]
[Comparison(ExpressionType.LessThanOrEqual)]
public DateTime? TransactionDateTo { get; set; }
[LinkedField("")]
[Comparison(ExpressionType.Equal)]
public int? TxNumber { get; set; }
[LinkedField("Order.OrderCustomer.Name")]
[Comparison(ExpressionType.Equal)]
public string CustomerName { get; set; }
[LinkedField("Order.OrderCustomer.CustomerAddress.ZipCode")]
[Comparison(ExpressionType.Equal)]
public int? CustomerZip { get; set; }
}
I use attributes to specify what field/property of the target ResultType any given search field is linked to, as well as the comparison type (== < > <= >= !=). A blank LinkedField means that the name of the search field is the same as the name of the target object field.
With this configured, the only things I should need for a given search are:
A populated search object like the one above
A data source
No other scenario-specific coding should be required!
The Solution
For starters, we create:
public abstract class SearchQuery
{
public Type ResultType { get; set; }
public SearchQuery(Type searchResultType)
{
ResultType = searchResultType;
}
}
We'll also create the attributes we used above to define the search field:
protected class Comparison : Attribute
{
public ExpressionType Type;
public Comparison(ExpressionType type)
{
Type = type;
}
}
protected class LinkedField : Attribute
{
public string TargetField;
public LinkedField(string target)
{
TargetField = target;
}
}
For each search field, we'll need to know not only WHAT search is done, but also WHETHER the search is done. For example, if the value of "TxNumber" is null, we wouldn't want to run that search. So we create a SearchField object that contains, in addition to the actual search value, two expressions: one that represents performing the search, and one that validates whether the search should be applied.
private class SearchFilter<T>
{
public Expression<Func<object, bool>> ApplySearchCondition { get; set; }
public Expression<Func<T, bool>> SearchExpression { get; set; }
public object SearchValue { get; set; }
public IQueryable<T> Apply(IQueryable<T> query)
{
//if the search value meets the criteria (e.g. is not null), apply it; otherwise, just return the original query.
bool valid = ApplySearchCondition.Compile().Invoke(SearchValue);
return valid ? query.Where(SearchExpression) : query;
}
}
Once we have created all our filters, all we need to do is loop through them and call the "Apply" method on our dataset! Easy!
The next step is creating the validation expressions. We'll do this based on the Type; every int? is validated the same as every other int?.
private static Expression<Func<object, bool>> GetValidationExpression(Type type)
{
//throw exception for non-nullable types (strings are nullable, but is a reference type and thus has to be called out separately)
if (type != typeof(string) && !(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)))
throw new Exception("Non-nullable types not supported.");
//strings can't be blank, numbers can't be 0, and dates can't be minvalue
if (type == typeof(string )) return t => !string.IsNullOrWhiteSpace((string)t);
if (type == typeof(int? )) return t => t != null && (int)t >= 0;
if (type == typeof(decimal? )) return t => t != null && (decimal)t >= decimal.Zero;
if (type == typeof(DateTime?)) return t => t != null && (DateTime?)t != DateTime.MinValue;
//everything else just can't be null
return t => t != null;
}
This was all I needed for my application, but there is definitely more validation that could be done.
The search expression is slightly more complicated and required a parser to "De-qualify" Field/Property names (there's probably a better word, but if so, I don't know it). Basically, if I specified "Order.Customer.Name" as a linked field and I'm searching through Orders, I need to turn that into "Customer.Name" because there is no Order Field inside an Order object. Or at least I hope not. :) This isn't certain, but I considered it better to accept and correct fully-qualified object names than to support that edge case.
public static List<string> DeQualifyFieldName(string targetField, Type targetType)
{
var r = targetField.Split('.').ToList();
foreach (var p in targetType.Name.Split('.'))
if (r.First() == p) r.RemoveAt(0);
return r;
}
This is just straight text parsing, and returns the Field name in "levels" (e.g. "Customer"|"Name").
All right, let's get our search expression together.
private Expression<Func<T, bool>> GetSearchExpression<T>(
string targetField, ExpressionType comparison, object value)
{
//get the property or field of the target object (ResultType)
//which will contain the value to be checked
var param = Expression.Parameter(ResultType, "t");
Expression left = null;
foreach (var part in DeQualifyFieldName(targetField, ResultType))
left = Expression.PropertyOrField(left == null ? param : left, part);
//Get the value against which the property/field will be compared
var right = Expression.Constant(value);
//join the expressions with the specified operator
var binaryExpression = Expression.MakeBinary(comparison, left, right);
return Expression.Lambda<Func<T, bool>>(binaryExpression, param);
}
Not so bad! What we're trying to create is, for example:
t => t.Customer.Name == "Searched Name"
Where t is our ReturnType--an Order, in this case. First we create the parameter, t. Then, we loop through the parts of the property/field name until we have the full title of the object we're targeting (naming it "left" because it's the left side of our comparison). The "right" side of our comparison is simple: the constant provided by the user.
Then we create the binary expression and turn it into a lambda. Easy as falling off a log! If falling off a log required countless hours of frustration and failed methodologies, anyway. But I digress.
We've got all the pieces now; all we need is a method to assemble our query:
protected IQueryable<T> ApplyFilters<T>(IQueryable<T> data)
{
if (data == null) return null;
IQueryable<T> retVal = data.AsQueryable();
//get all the fields and properties that have search attributes specified
var fields = GetType().GetFields().Cast<MemberInfo>()
.Concat(GetType().GetProperties())
.Where(f => f.GetCustomAttribute(typeof(LinkedField)) != null)
.Where(f => f.GetCustomAttribute(typeof(Comparison)) != null);
//loop through them and generate expressions for validation and searching
try
{
foreach (var f in fields)
{
var value = f.MemberType == MemberTypes.Property ? ((PropertyInfo)f).GetValue(this) : ((FieldInfo)f).GetValue(this);
if (value == null) continue;
Type t = f.MemberType == MemberTypes.Property ? ((PropertyInfo)f).PropertyType : ((FieldInfo)f).FieldType;
retVal = new SearchFilter<T>
{
SearchValue = value,
ApplySearchCondition = GetValidationExpression(t),
SearchExpression = GetSearchExpression<T>(GetTargetField(f), ((Comparison)f.GetCustomAttribute(typeof(Comparison))).Type, value)
}.Apply(retVal); //once the expressions are generated, go ahead and (try to) apply it
}
}
catch (Exception ex) { throw (ErrorInfo = ex); }
return retVal;
}
Basically, we just grab a list of fields/properties in the derived class (that are linked), create a SearchFilter object from them, and apply them.
Clean-Up
There's a bit more, of course. For example, we're specifying object links with strings. What if there's a typo?
In my case, I have the class check whenever it spins up an instance of a derived class, like this:
private bool ValidateLinkedField(string fieldName)
{
//loop through the "levels" (e.g. Order / Customer / Name) validating that the fields/properties all exist
Type currentType = ResultType;
foreach (string currentLevel in DeQualifyFieldName(fieldName, ResultType))
{
MemberInfo match = (MemberInfo)currentType.GetField(currentLevel) ?? currentType.GetProperty(currentLevel);
if (match == null) return false;
currentType = match.MemberType == MemberTypes.Property ? ((PropertyInfo)match).PropertyType
: ((FieldInfo)match).FieldType;
}
return true; //if we checked all levels and found matches, exit
}
The rest is all implementation minutia. If you're interested in checking it out, a project that includes a full implementation, including test data, is here. It's a VS 2015 project, but if that's an issue, just grab the Program.cs and Search.cs files and throw them into a new project in your IDE of choice.
Thanks to everyone on StackOverflow who asked the questions and wrote the answers that helped me put this together!
This seems like the most basic thing ever but somehow I couldnt find the answer and couldnt figure it out.
Lets say I have a custom class:
public class WineCellar
{
public string year;
public string wine;
public double nrbottles;
}
Now I would like a function:
WineCellar ex = new WineCellar();
ex.members();
This should return: year, wine, nrbootles.
And:
ex.members().types();
Should return: string, string, double
I guess on the same note, lets say you have one instance {2010, Rioja, 6}. Is there syntax that returns these by indexing? i.e.
ex[1]
or
ex.{1}
that returns 2010?
Sorry for the basic question.
As Michelle said in the comments, this sounds like a wrong approach to a bigger problem.
However, if you do need this kind of things, you can get the using reflection:
//returns a list of propertyInfo objects for the class
// with all kinds of usefull information
public List<PropertyInfo> GetMemberInfos()
{
return this.GetType().GetProperties().ToList();
}
//returns a list of property names
public List<string> GetMemberNames
{
return this.GetType().GetProperties().Select(pi => pi.Name).ToList();
}
//returns a list of names of the property types
public List<string> GetMemberTypeNames
{
return this.GetType().GetProperties().Select(pi => pi.PropertyType.Name).ToList();
}
//indexer that uses the property name to get the value
//since you are mixing types, you can't get more specific than object
public object this[string property]
{
get { return this.GetType().GetProperty(property).GetValue(this); }
set { this.GetType().GetProperty(property).SetValue(this, value); }
}
//indexer that uses the property index in the properties array to get the value
public object this[int index]
{
get { return this.GetType().GetProperties()[index].GetValue(this); }
set { this.GetType().GetProperties()[index].SetValue(this, value); }
}
Note that all of these methods are very slow, because in general, reflection is slow. You can try to cache some thing to speed it up.
Also, the last method is downright dangerous. It will (try to) read and write to an array that does not have a guaranteed order. In fact, the documentation specifies:
The GetProperties method does not return properties in a particular
order, such as alphabetical or declaration order. Your code must not
depend on the order in which properties are returned, because that
order varies.
For example, if you change your class to:
public class WineCellar
{
public string year;
public string region;
public string wine;
public double nrbottles;
}
and you were used to using winecellar[1] = "Pinot Noir" that will most likely now update the region property, instead of the wine property.
This is how you would implement Members method (In case if you wanted property names as strings)
public List<string> Members()
{
List<string> propNames = new List<string>();
foreach (var prop in typeof(WineCellar).GetProperties())
{
propNames.Add(prop.Name);
}
return propNames;
}
And this is how you would implement Types (In same case)
public List<string> Types()
{
List<string> propTypes = new List<string>();
foreach (var prop in typeof(WineCellar).GetProperties())
{
propTypes.Add(prop.PropertyType.ToString());
}
return propTypes ;
}
And the last thing if you want to get values of the parameters like this ex[n] you can just make a simple indexer in you class like this
public string this[int n]
{
get
{
int current = 0;
foreach (var prop in typeof(WineCellar).GetProperties())
{
if (current == n)
return prop.GetValue(this, null).ToString();
current++;
}
return null;
}
}
but for these methods to work you should change your variables into properties like this
public class WineCellar
{
public string Year { get; set; }
public string Wine { get; set; }
public double Nrbottles { get; set; }
}
You can use reflection
foreach (var prop in typeof(WineCellar).GetProperties())
{
if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
{
}
}
to get the value, you can do:
prop.GetValue(obj);
I have a class with only string members like this :
public class MyClass
{
public string MyProp1 { get; set; }
public string MyProp2 { get; set; }
}
I create an instance :
Var myClass = new MyClass();
Later in the code, I’d like to know if all the member (MyProp1 and MyProp2) are not null or empty. I know I can use a if of course but there is much more properties than 2 in my real code.
Is there a way to do this ?
Thanks,
Using a dictionary based store for your properties is probably the easiest way of doing this:
public class MyClass
{
private IDictionary<String, String> _store;
public MyClass()
{
_store = new Dictionary<String, String>();
}
public string MyProp1 {
get { return GetOrDefault("MyProp1"); }
set { _store["MyProp1"] = value; }
}
public string MyProp2 {
get { return GetOrDefault("MyProp2"); }
set { _store["MyProp2"] = value; }
}
public Boolean HasData()
{
return _store.Any(x => !String.IsNullOrWhiteSpace(x.Value));
}
public Boolean IsEmpty()
{
return _store.All(x => String.IsNullOrWhiteSpace(x.Value));
}
private String GetOrDefault(String propertyName)
{
if (_store.ContainsKey(propertyName))
{
return _store[propertyName];
}
return String.Empty;
}
}
Another method for doing this would be to compare it with a default instance:
public class MyClass
{
public string MyProp1 { get; set; }
public string MyProp2 { get; set; }
public static readonly MyClass Empty = new MyClass();
public Boolean HasData()
{
return !Empty.Equals(this);
}
public Boolean IsEmpty()
{
return Empty.Equals(this);
}
}
You can try to use the reflect to check the properties. You should need confirm that all the properties are public, and the type is string. Here is the code.
public static bool IsNullOrEmpty(MyClass prop)
{
bool result = true;
PropertyInfo[] ps = prop.GetType().GetProperties();
foreach (PropertyInfo pi in ps)
{
string value = pi.GetValue(prop, null).ToString();
if (string.IsNullOrEmpty(value))
{
result = false;
break;
}
}
return result;
}
To check if your class contains 'any' properties which are null:
System.Reflection.PropertyInfo[] properties = myClass.GetType().GetProperties
(BindingFlags.Public | BindingFlags.Instance);
bool hasNullProperty = properties.Any(y => y.GetValue(x, null) == null);
You can always initialize your class like
public class MyClass
{
public MyClass() {
this.MyProp1 = this.MyProp2 = String.Empty;
}
public string MyProp1 { get; set; }
public string MyProp2 { get; set; }
}
and, unless your programmatically assign a null value to it, the new MyClass() will always have String.Empty in their 2 properties...
from comment:
What I do in those cases is call a helper, for example: string name = myHelper.CheckNode(xmlNode); and in that helper I check if it's null, any other check, I can easily tweek the helper method and it will be available to all elements, and you can extend it to support not only strings but all other data types as well
So, imagine that you are reading nodes from your XML, you write them like:
string name = myHelper.CheckNode(node);
in your helper, you could have something like:
XmlNodeList datasourceNodes = rootNode.SelectNodes("dataSources/dataSource");
foreach (XmlNode datasourceNode in datasourceNodes)
{
DataSource dataSource = new DataSource();
dataSource.Name = myHelper.CheckAttr(datasourceNode.Attributes["name"]);
dataSource.ODBC = myHelper.CheckNode(datasourceNode.SelectSingleNode("odbc"));
// or a variant (Extension Method)
dataSource.UID = datasourceNode.CheckNode("user");
dataSource.PWD = datasourceNode.CheckAttr("password");
ds.Add(dataSource);
}
your helper then could have a method like:
public static string CheckAttr(XmlAttribute attr)
{
return attr == null ? "" : attr.Value.Trim();
}
public static string CheckNode(XmlNode node)
{
return node == null ? "" : node.InnerText.Trim();
}
or for the variant (Extension Method)
public static string CheckAttr(this XmlNode, string attrName)
{
return attrName[attrName] == null ? "" : attrName[attrName].Value.Trim();
}
public static string CheckNode(this XmlNode, string nodeName)
{
return node.SelectSingleNode(nodeName) == null ?
"" :
node.SelectSingleNode(nodeName).InnerText.Trim();
}
If there are many properties in the class, one way of handling this is storing them in a collection, such as an array or a dictionary, instead of declaring each property as a separate member of the class.
Then you can access data in the dictionary by key, which is as easy as accessing a property of a class. And the advantage is that you can loop over the dictionary and check all the properties in a loop.
I would suggest creating a function in your class where you check String.IsNullOrEmpty(MyProp1) etc. for all your properties. This way you at least have gathered all the ckecking functionality in a single place. And you only have this place to modify whenever you add new properties.
To check if all the elements are set you could add a IsEmpty() method to your class that would check the internal properties. Then you wouldn't have to duplicate the if statements everywhere trough your code.
In your IsEmpty() method you can use a regular if statement to check all the fields or you can use reflection to automaticaly retrieve all string properties and check their values. The performance of reflection will be worse then a normal if check but if that's not a problem you can reuse the reflection code in all your entities to check their values.
By using Attribute Base programming you can achieve this. In this approach you will need to place attribute over the class member, and validation is can be done. You can also use Microsoft Enterprise Library for this.
Probably the best way would be to :
Restructure your properties in the form of a Dictionary of strings. Loop through the dictionary to test the strings using string.IsNullOrEmpty(). You could replace the N getter/setters by a single Indexer property which sets and retrieves the strings directly from the dictionary based on a key
You can use try the following style. I haven't tried it before but you might see if it helps
If (String.IsNullOrEmpty(string1 && string2 && string3))
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();
I'm using reflection to iterate over all the members of a given type. This interaction must support inheritance, since most type will be extended as necessary.
I've just found out that the order in which types present themselves when iterating over GetMembers isn't really what I'd expect -- the types of the derived classes appear first, in their current order, and the types of the base classes later, again in their current order.
Source:
using System;
class Program
{
class SomeClass
{
public string First { get; set; }
public int Second;
}
class AnotherClass : SomeClass
{
public int Third { get; set; }
public string Fourth;
}
public static void Main()
{
var obj = new AnotherClass { First = "asd", Second = 10};
foreach (var member in obj.GetType().GetMembers())
{
Console.WriteLine(member.Name);
}
}
}
Output:
get_Third
set_Third
get_First
set_First
Equals
GetHashCode
GetType
ToString
.ctor
Third
First
Fourth
Second
You can check a run here.
I'd like to invert this situation, using reflection to get only types from the base class, then from the derived. Is there any way to do this?
Another question on the same line: when searching members, properties come first and fields second. Anyway to change this behavior as well, or the metadata created will always present in that order?
Thanks!
To access the base type use BaseType property.
To check if a member is declared in the same type, use DeclaringType property:
public static bool DeclaredInType(Type typeToCheck, MemberInfo member)
{
return typeToCheck.Equals(member.DeclaringType);
}
EDIT: you can sort by type by using LINQ:
public static MemberInfo[] SortMembers(IEnumerable<MemberInfo> members)
{
return members.OrderBy(m => m.GetType().Name)
.ToArray();
}
This is not exactly an answer to the OP (and I'm a bit late for that), but I'll just describe what I did, in the hopes someone may find it helpful.
I have a home-made program that serializes to XML. It is driven by a List of CopierField objects that I create that contain the data I need to expedite the serialization. Here is a much-redacted version of that class:
private class CopierField
{
// Name of the field or property and a reference to the declaring Type
public string MemberName;
public Type DeclaringType;
// Reference to the FieldInfo or PropertyInfo object for this field or property. One of
// these will be null and the other non-null.
public FieldInfo MemberInfoForField = null;
public PropertyInfo MemberInfoForProperty = null;
// Ordering of this field, as returned by Type.GetMembers(). This is only used while
// building the List<> of these objects for XML serialization
public int FieldOrder;
/// <summary>
/// Comparison method that can be used to sort a collection of CopierField objects so they
/// are in the order wanted for XML serialization. This ordering is dependent on the depth
/// of derivation, and when that is equal it maintains the original ordering.
/// </summary>
public static readonly Comparison<CopierField> CompareForXml =
delegate(CopierField a, CopierField b)
{
int aDepth = GetTypeDepth(a.DeclaringType);
int bDepth = GetTypeDepth(b.DeclaringType);
if (aDepth != bDepth)
return aDepth.CompareTo(bDepth);
return a.FieldOrder.CompareTo(b.FieldOrder);
};
/// <summary>
/// Method to determine the depth of derivation for a type.
/// </summary>
private static int GetTypeDepth(Type aType)
{
int i = 0;
while (aType.BaseType != null)
{
i++;
aType = aType.BaseType;
}
return i;
}
}
Before serializing an object type to XML the first time I create a List of these objects sorted in the order I want using a method somewhat like this, where the input is a List of the CopierField objects that is based on data from Type.GetMembers().
private static List<CopierField> CreateCopierFieldListForXml(List<CopierField> copierFields)
{
// Build the new list, and note the original ordering as created by Type.GetMembers()
List<CopierField> copierFieldsForXml = new List<CopierField>(copierFields.Count);
for (int listIndex = 0; listIndex < copierFields.Count; listIndex++)
{
CopierField copierField = copierFields[listIndex];
copierField.FieldOrder = listIndex;
copierFieldsForXml.Add(copierField);
}
// Sort the new list as wanted for XML serialization
copierFieldsForXml.Sort(CopierField.CompareForXml);
return copierFieldsForXml;
}
TL;DR
orderby typeof(T).Equals(mi.DeclaringType) ? 1 : -1
will push base memberInfo first, and keep the order defined in the class.
Full answer:
to achieve the same goal, and using the DeclaringType as suggested previously, I defined the following method:
public static IEnumerable<MemberInfo> GetAllFieldsAndPropertiesOfClass<T>()
{
return
from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
let ignoreAttr = (IgnoreSerializationAttribute)Attribute.GetCustomAttribute(mi, typeof(IgnoreSerializationAttribute))
where (mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property)
&& (ignoreAttr == null || ignoreAttr != null && !ignoreAttr.Ignore)
orderby typeof(T).Equals(mi.DeclaringType) ? 1 : -1
select mi;
}
In that method, I also defined a custom attribute to explicitely ignore some properties from the serialization:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class IgnoreSerializationAttribute : Attribute
{
public bool Ignore { get; private set; }
public IgnoreSerializationAttribute(bool ignore)
{
Ignore = ignore;
}
}
It is also possible to add an other custom Attribute to define the order, e.g.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ColumnOrderAttribute : Attribute
{
public int Order { get; private set; }
public ColumnOrderAttribute(int order)
{
Order = order;
}
}
used as follow:
public static IEnumerable<MemberInfo> GetAllFieldsAndPropertiesOfClassOrdered<T>()
{
return
from mi in GetAllFieldsAndPropertiesOfClass<T>()
let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute))
orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name
select mi;
}
I am using those methods to serialize list of objects using other objects to CSV files...