I need to create the ability to drill through an objects properties like two or three deep. For instance, class A has a property reference to class B, which I need to access class C. What is the best way to do this: straight reflection, or maybe using the TypeDescriptor, or something else?
Thanks.
It's not too hard to write. I put a few classes together to deal with this so I could serialize properties of a WinForm. Take a look at this class and the related classes.
http://csharptest.net/browse/src/Library/Reflection/PropertySerializer.cs
If you know the path in a static context (ie the path is always the same) and the properties are accessible (internal or public) you can use dynamic
[Test]
public void Foo()
{
var a = new A
{
B = new B
{
C = new C
{
Name = "hello"
}
}
};
DoReflection(a);
}
private void DoReflection(dynamic value)
{
string message = value.B.C.Name;
Debug.WriteLine(message);
}
I you wanna write you own serialization code for whatever reason, you'll be using reflection.
What you do is that you write a recursive method of serlizating a type. You then apply this as you see fit to get the result.
var type = myObjectOfSomeType.GetType();
// now depending on what you want to store
// I'll save all public properties
var properties = type.GetProperties(); // get all public properties
foreach(var p in properties)
{
var value = p.GetValue(myObjectOfSomeType, null);
Writevalue(p.Name, value);
}
The implementation of WriteValue have to recognize the built in types and treat them accordingly, that's typical things like string, char, integer, double, DateTime etc.
If it encounters a sequence or collection you need to write out many values.
If it encounters a non trivial type you'll apply this recursive pattern again.
The end result is a recursive algorithm that traverses your object model and writes out values as it encounters types that I know how to serialize.
However, I do recommend looking into WCF, not for building services, but for serialization. It shipped as part of the .NET 3.0 framework with a new assembly System.Runtime.Serilization and in general is very capable when dealing with serialization and data annotations.
Related
I have some rather large files around that I would like to load in automatically with deserialization and then simply merge together, if possible maintaining memory references in a central object to make the merge as benign as possible.
Merges seem to be anything but simple though, in concept it seems easy
If there are some nulls, use the non null values
If there are if there are conflicting objects, dig deeper and merge their internals or just prefer one, maybe write custom code just for those classes to merge.
If there are collections, definitely combine them, if there are keys, like in a dictionary, try to add them, when there is a key conflict, as before, merge them or prefer one.
I've seen a lot of people around stack recommending I use Automapper to try and achieve this, though that seems flawed. Automapper isn't made for this and the overall task doesn't seem complex enough to warrant it. Its also not amazing encapsulation to put all your class specific merge code anywhere but that class. Your data pertaining to a given aspect of your code should sit in central locations, like a class object, to enable programmers to understand the usage of the data structure around them more readily. So I don't feel that automapper is a good solution for merging objects rather than simply, keeping one.
How would you recommend automating the merge of two structurally identical c# objects with nested heirarchies of custom classes?
I will post my own solution as well, but I encourage other developers, certainly many more intelligent than I, to recommend solutions.
While #JodySowald's answer decribes a nice generic approach, merging sounds to me like something that could involve an awful lot of class-specific business logic.
I would simply add a MergeWith method to each and every class in my hierarchy, down to a level where "merging" means a simple repeatable generic operation.
class A
{
string Description;
B MyB {get; set;}
public void MergeWith(A other)
{
// Simple local logic
Description = string.IsNullOrWithSpace(Description) ? other.Description : Description;
// Let class B do its own logic
MyB = MyB.MergeWith(other.MyB);
}
}
I think that in ~70% of use cases, someone will have a large hierarchical structure of many classes in a class library and will wish to be able to merge the whole hierarchy at once. For that purpose I think the code should iterate across the properties of the object and nested properties of subclasses, but only ones defined in the assembly you've created. No merging the internals of System.String, who knows what could happen. So only internal types to this assembly should be dug into for further merging
var internalTypes = Assembly.GetExecutingAssembly().DefinedTypes;
We also need a way to define custom code on a given class, there are always edge cases. I believe that this is what interfaces were created for, to generically define functionality for several classes and have a specific implementation available to the specific class. But I found that if merging requires knowledge of the data hierarchically above this class, such as the key it is stored with in a dictionary or perhaps an enum indicating the types or modes of data present, a reference to the containing datastructure should be available. So I defined a quick interface, ICombinable
internal interface ICombinable
{
/// <summary>
/// use values on the incomingObj to set correct values on the current object
/// note that merging comes after the individual DO has been loaded and populated as necessary and is the last step in adding the objects to the central DO that already exists.
/// </summary>
/// <param name="containingDO"></param>
/// <param name="incomingObj">an object from the class being merged in.</param>
/// <returns></returns>
ICombinable Merge(object containingDO, ICombinable incomingObj);
}
Bringing this together into a functional piece of code basically requires a little bit of property reflection, a little bit of recursion, and a little bit of logic, all of which is nuanced, so I just commented my code isntead of explaining it before hand. Since the goal is to affect a central object and not to create a new, merged copy, this is an instance method in the base class of the datastructure. but you could probably convert it to a helper method pretty easily.
internal void MergeIn(Object singleDO)
{
var internalTypes = Assembly.GetExecutingAssembly().DefinedTypes;
var mergableTypes = internalTypes.Where(c => c.GetInterfaces().Contains(typeof(ICombinable)));
MergeIn(this, this, singleDO, internalTypes, mergableTypes);
}
private void MergeIn(Object centralDORef, object centralObj, object singleObj, IEnumerable<TypeInfo> internalTypes, IEnumerable<TypeInfo> mergableTypes)
{
var itemsToMerge = new List<MergeMe>();
//all at once to open up parallelization later.
IterateOver(centralObj, singleObj, (f, t, i) => itemsToMerge.Add(new MergeMe(t, f, i)));
//check each property on these structures.
foreach (var merge in itemsToMerge)
{
//if either is null take non-null
if (merge.From == null || merge.To == null)
merge.Info.SetValue(centralObj, merge.To ?? merge.From);
//if collection merge
else if (merge.Info.PropertyType.IsGenericType && merge.Info.PropertyType.GetGenericTypeDefinition().IsAssignableFrom(typeof(IList<>)))
foreach (var val in (IList)merge.From)
((IList)merge.To).Add(val);
//if dictionary merge
else if (merge.Info.PropertyType.IsGenericType && merge.Info.PropertyType.GetGenericTypeDefinition().IsAssignableFrom(typeof(IDictionary<,>)))
{
var f = ((IDictionary)merge.From);
var t = ((IDictionary)merge.To);
foreach (var key in f.Keys)
if (t.Contains(key))
{
//if conflicted, check for icombinable
if (merge.Info.PropertyType.GenericTypeArguments[1].IsAssignableFrom(typeof(ICombinable)))
t[key] = ((ICombinable)t[key]).Merge(centralDORef, (ICombinable)f[key]);
}
else
t.Add(key, f[key]);
}
//if both non null and not collections, merge.
else if (merge.From != null && merge.To != null)
{
//check for Icombinable.
if (merge.Info.PropertyType.IsAssignableFrom(typeof(ICombinable)))
merge.Info.SetValue(centralObj, ((ICombinable)merge.To).Merge(centralDORef, (ICombinable)merge.From));
//if we made the object, dig deeper
else if (internalTypes.Contains(merge.Info.PropertyType))
{
//recurse.
MergeIn(centralDORef, merge.To, merge.From, internalTypes, mergableTypes);
}
//else do nothing, keeping the original
}
}
}
private class MergeMe{
public MergeMe(object from, object to, PropertyInfo info)
{
From = from;
To = to;
Info = info;
}
public object From;
public object To;
public PropertyInfo Info;
}
private static void IterateOver<T>(T destination, T other, Action<object, object, PropertyInfo> onEachProperty)
{
foreach (var prop in destination.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
onEachProperty(prop.GetValue(destination), prop.GetValue(other), prop);
}
If i generate my entities through Entity Framework Database First, and i want to use a function like that:
AuditManager.DefaultConfiguration.Exclude<T>();
considering that the number of times i want to call it should be equal to the number of entities
ex:
AuditManager.DefaultConfiguration.Exclude<Employee>();
AuditManager.DefaultConfiguration.Exclude<Department>();
AuditManager.DefaultConfiguration.Exclude<Room>();
Now how to Loop through selected number of entities and pass every one to the Exclude function ?
The obvious solution would be to call the method for every entity-type you want to hide. Like this:
AuditManager.DefaultConfiguration.Exclude<Employee>();
AuditManager.DefaultConfiguration.Exclude<Department>();
AuditManager.DefaultConfiguration.Exclude<Room>();
You can add conditional statements (ifs) around them to do it dynamically.
Howevery, if you want a fully flexible solution, where you call the Exclude method based on metadata, you need something else. Something like this:
var types = new[] { typeof(Employee), typeof(Department), typeof(Room) };
var instance = AuditManager.DefaultConfiguration;
var openGenericMethod = instance.GetType().GetMethod("Exclude");
foreach (var #type in types)
{
var closedGenericMethod = openGenericMethod.MakeGenericMethod(#type);
closedGenericMethod.Invoke(instance, null);
}
This assumes that the Exclude<T> method is an instance method on whatever instance DefaultConfiguration points to.
An alternative to looping through your entity types is to make the entities you don't want audited implement the same interface and exclude that. For example:
public interface IExcludeFromAudit
{ }
And your entities:
public class Order : IExcludeFromAudit
{
//snip
}
And now just exclude the interface:
AuditManager.DefaultConfiguration.Exclude<IExcludeFromAudit>();
The benefit of this is that it's now easy to control which ones are excluded.
I have a base class (abstract) with multiple implementations, and some of them contain collection properties of other implementations - like so:
class BigThing : BaseThing
{
/* other properties omitted for brevity */
List<SquareThing> Squares { get; set; }
List<LittleThing> SmallThings { get; set;}
/* etc. */
}
Now sometimes I get a BigThing and I need to map it to another BigThing, along with all of its collections of BaseThings. However, when this happens, I need to be able to tell if a BaseThing in a collection from the source BigThing is a new BaseThing, and thus should be Add()-ed to the destination BigThing's collection, or if it's an existing BaseThing that should be mapped to one of the BaseThings that already exist in the destination collection. Each implementation of BaseThing has a different set of matching criteria on which it should be evaluated for new-ness. I have the following generic extension method to evaluate this:
static void UpdateOrCreateThing<T>(this T candidate, ICollection<T> destinationEntities) where T : BaseThing
{
var thingToUpdate = destinationEntites.FirstOrDefault(candidate.ThingMatchingCriteria);
if (thingToUpdate == null) /* Create new thing and add to destinationEntities */
else /* Map thing */
}
Which works fine. However I think I am getting lost with the method that deals in BigThings. I want to make this method generic because there are a few different kinds of BigThings, and I don't want to have to write methods for each, and if I add collection properties I don't want to have to change my methods. I have written the following generic method that makes use of reflection, but it is not
void MapThing(T sourceThing, T destinationThing) where T : BaseThing
{
//Take care of first-level properties
Mapper.Map(sourceThing, destinationThing);
//Now find all properties which are collections
var collectionPropertyInfo = typeof(T).GetProperties().Where(p => typeof(ICollection).IsAssignableFrom(p.PropertyType));
//Get property values for source and destination
var sourceProperties = collectionPropertyInfo.Select(p => p.GetValue(sourceThing));
var destinationProperties = collectionPropertyInfo.Select(p => p.GetValue(destinationThing));
//Now loop through collection properties and call extension method on each item
for (int i = 0; i < collectionPropertyInfo.Count; i++)
{
//These casts make me suspicious, although they do work and the values are retained
var thisSourcePropertyCollection = sourceProperties[i] as ICollection;
var sourcePropertyCollectionAsThings = thisSourcePropertyCollection.Cast<BaseThing>();
//Repeat for destination properties
var thisDestinationPropertyCollection = destinationProperties[i] as ICollection;
var destinationPropertyCollectionAsThings = thisDestinationPropertyCollection.Cast<BaseThing>();
foreach (BaseThing thing in sourcePropertyCollectionAsThings)
{
thing.UpdateOrCreateThing(destinationPropertyCollectionAsThings);
}
}
}
This compiles and runs, and the extension method runs successfully (matching and mapping as expected), but the collection property values in destinationThing remain unchanged. I suspect I have lost the reference to the original destinationThing properties with all the casting and assigning to other variables and so on. Is my approach here fundamentally flawed? Am I missing a more obvious solution? Or is there some simple bug in my code that's leading to the incorrect behavior?
Without thinking too much, I'd say you have fallen to a inheritance abuse trap, and now trying to save yourself, you might want to consider how can you solve your problem while ditching the existing design which leads you to do such things at the first place. I know, this is painful, but it's an investment in future :-)
That said,
var destinationPropertyCollectionAsThings =
thisDestinationPropertyCollection.Cast<BaseThing>();
foreach (BaseThing thing in sourcePropertyCollectionAsThings)
{
thing.UpdateOrCreateThing(destinationPropertyCollectionAsThings);
}
You are losing your ICollection when you use Linq Cast operator that creates the new IEnumerable<BaseThing>. You can't use contravariance either, because ICollectiondoes not support it. If it would, you'd get away with as ICollection<BaseThing> which would be nice.
Instead, you have to build the generic method call dynamically, and invoke it. The simplest way is probably using dynamic keyword, and let the runtime figure out, as such:
thing.UpdateOrCreateThing((dynamic)thisDestinationPropertyCollection);
My Problem
I have a Problem which i can not solve my self. I dont want to use so much code, because i have multiple Classes which extend another class (in my case its called "Data").
I have a log file, where each Data Group is beginning with a specific Group Name, for example "MitarbeiterSet". The abstract Data-Class is used to prefent to much code, where I implemented variables like "String[] data" (for the data beeing parsed from the log file e.g. < 101 4 3 6 3 30 80 2 0 0 1 300 >) or "static String parseInduction", which is used to determin, if this Class is the right one to create Objects from.
I have another Class, called ParseMonitor, which creates the StreamReader to parse the log-file. So if the right Class is found, i induct the setDataArray(StreamReader sr) function from the right Class, to parse the Data Array. (At this point i have to tell you, that i need those different Classes, because i need to upload them to a sql server specificly.)
This static function creates an object of it self and uses the parseLine(String line) Function to fill the object with data from the given line.
WHAT I NEED.
I want to call the static function of any class, just by having the name of this class. So i dont have to use that much code and be able to add more classes.
Later on i want to call every class and use the uploadToServer() to Upload it to the server.
Is this possible?
Since your static method is creating an instance of its class anyway, I suggest a different approach:
Create an interface that all classes that contain ParseLine can implement. (Change out the return type for the correct one):
public interface IParseLine
{
string ParseLine(string line);
}
Have all of the classes that contain ParseLine() implement IParseLine.
Create an instance of the class, cast it to an IParseLine, and execute the method:
IParseLine pl = Activator.CreateInstance(Type.GetType(className)) as IParseLine;
if (pl != null)
{
string parsedString = pl.ParseLine(line);
// ...
}
Edit From comments:
I want to create a while loop, which can be stated as followed:
while{!sr.EndofStream){ line = sr.ReadLine(); for(int i = 0; i <
classNames.length; i++){ if(line.Contains(classNames[i].MYINDICATOR){
CALL classNames[i] STATIC METHOD TO PARSE THE FOLLOWING LINES AND
CREATE DATA Objects of its Class } }
I didn't test this, but you can change the code to something like this (caching the reflection required to get MYINDICATOR):
IList<KeyValuePair<string, Type>> typeIndicators = classNames.Select(x => {
Type t = Type.GetType(x);
string indicator = (string)t.GetField("MYINDICATOR", BindingFlags.Public | BindingFlags.Static).GetValue(null);
return new KeyValuePair(indicator, t);
});
while (!sr.EndOfStream)
{
line = sr.ReadLine();
foreach (var types in typeIndicators)
{
if (line.Contains(types.Key))
{
IParseLine pl = Activator.CreateInstance(types.Value) as IParseLine;
if (pl != null)
{
string parsedString = pl.ParseLine(line);
}
}
}
}
I want to call the static function of any class, just by having the name of this class.
Well, you can use Type.GetType(className) to get a Type (note that the name needs to at least be fully qualified including the namespace, and may also need the assembly name depending on your exact scenario), then Type.GetMethod to get a MethodInfo. Finally, call MethodBase.Invoke to invoke the method.
If you could use typeof(Foo) instead of using a string, it would make the code simpler and more robust.
(Side-note: if your methods are really called parseLine, parseInduction, setDataArray etc, you should consider renaming them to follow .NET naming conventions :)
I think I see where you're coming from. In this simple exmaple below, I have a static class with a method in it (nothing amazing about that).
public static class MyStaticClass
{
public static DateTime GetTime()
{
return DateTime.Now;
}
}
If I want to invoke that method using reflection, I can just use the following code, but it does assume that the MyStaticClass class is available via a reference or inthe same project etc.
MethodInfo method = typeof(MyStaticClass).GetMethod("GetTime");
object result = method.Invoke(null, null);
if (result is DateTime)
{
Console.WriteLine(((DateTime)result).ToLongTimeString());
}
What you seem ot be asking for is a moethod of doing this when you don't have a reference to the class. In which case, try something like this:
MethodInfo method = Type.GetType("PocStaticReflect.MyStaticClass, PocStaticReflect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null").GetMethod("GetTime");
object result = method.Invoke(null, null);
if (result is DateTime)
{
Console.WriteLine(((DateTime)result).ToLongTimeString());
}
Notice the fully qualified class name!
If you get that working, then you can simply loop though your class names and call the method you desire. Obviously, you'll probably want more error checking and more detail in the GetMethod() calls, but this shlud give you the gist of it. I've done something similar before looping though assemblies in a folder to pickup plug-ins for an application. That time, each of the classes implemented an interface to make them easier to locate, which may be helpful path to follow.
Or try this:
private static object GetResultFromStaticMethodClass(string qualifiedClassName, string method)
{
Type StaticClass = Type.GetType(qualifiedClassName);
MethodInfo methodInfo = StaticClass.GetMethod(method);
object result = methodInfo.Invoke(null, null);
return result;
}
Use:
object result = GetResultFromStaticMethodClass(
"Utilities.StringHelper,DaProject",
"ToList"
);
This call the static method ToList in the StringHelper class, in the Utilities namespace, in the DaProject project (same assembly and project name).
If you need parameters, add them in the second parameter in the methodInfo.Invoke(null, null) call
In my business layer, I need many, many methods that follow the pattern:
public BusinessClass PropertyName
{
get
{
if (this.m_LocallyCachedValue == null)
{
if (this.Record == null)
{
this.m_LocallyCachedValue = new BusinessClass(
this.Database, this.PropertyId);
}
else
{
this.m_LocallyCachedValue = new BusinessClass(
this.Database, this.Record.ForeignKeyName);
}
}
return this.m_LocallyCachedValue;
}
}
I am still learning C#, and I'm trying to figure out the best way to write this pattern once and add methods to each business layer class that follow this pattern with the proper types and variable names substituted.
BusinessClass is a typename that must be substituted, and PropertyName, PropertyId, ForeignKeyName, and m_LocallyCachedValue are all variables that should be substituted for.
Are attributes usable here? Do I need reflection? How do I write the skeleton I provided in one place and then just write a line or two containing the substitution parameters and get the pattern to propagate itself?
EDIT: Modified my misleading title -- I am hoping to find a solution that doesn't involve code generation or copy/paste techniques, and rather to be able to write the skeleton of the code once in a base class in some form and have it be "instantiated" into lots of subclasses as the accessor for various properties.
EDIT: Here is my solution, as suggested but left unimplemented by the chosen answerer.
// I'll write many of these...
public BusinessClass PropertyName
{
get
{
return GetSingleRelation(ref this.m_LocallyCachedValue,
this.PropertyId, "ForeignKeyName");
}
}
// That all call this.
public TBusinessClass GetSingleRelation<TBusinessClass>(
ref TBusinessClass cachedField, int fieldId, string contextFieldName)
{
if (cachedField == null)
{
if (this.Record == null)
{
ConstructorInfo ci = typeof(TBusinessClass).GetConstructor(
new Type[] { this.Database.GetType(), typeof(int) });
cachedField = (TBusinessClass)ci.Invoke(
new object[] { this.Database, fieldId });
}
else
{
var obj = this.Record.GetType().GetProperty(objName).GetValue(
this.Record, null);
ConstructorInfo ci = typeof(TBusinessClass).GetConstructor(
new Type[] { this.Database.GetType(), obj.GetType()});
cachedField = (TBusinessClass)ci.Invoke(
new object[] { this.Database, obj });
}
}
return cachedField;
}
Check out CodeSmith. They have a free trial and it's not too expensive if you want to purchase it. I've used it and it's great for generating code based on databases (which is what I'm guessing you're doing). Once you have your template setup, you can regenerate the code at any time. You can have it read the property names right from the database schema or you can enter the values you want to use. I'm sure you could even get it to read the values from a file if you wanted to generate a whole batch of classes at once.
You could check out using T4 Templates. I am not quite sure which is "the" resource for T4, but I found a good article on it in VisualStudioMagazine.
It is free, has an easy to use syntax and is actually used by a lot of projects (e.g. Subsonic) for code generation, so you should be able to find some real-world scenarios.
You can code-gen using CodeSmith or MyGeneration or the like. You'd probably store a list of classes and properties somewhere and then pass that data to the code generator. You may want to investigate using pre-build events to re-gen those classes prior to compiling the solution.
Or, you could bake this functionality into a base class or helper method.
public BusinessClass MyProperty
{
get { return GetCached("MyProperty", "PropertyId", "FKName", "LocalValue"); }
}
I'll leave the body of GetCached() up to you, but it's basically the same as what you posted with the variables passed in as arguments.
If any of those values are the same for all properties in a class then you could of course pull them from instance variables, and only pass to GetCached() those things that vary on a per-property basis.
Bottom line: if there's a way to abstract the logic of what you're doing into a base method, so that using that logic becomes a one-liner, then that's probably the best way to go because it's easier to override when you have special cases. If you can't do that, code generation can do the grunt work for you, but you'll need to work out things like when do I re-gen, how do I regen, etc.