I'm trying to create a base class for my POCO objects in .NET 4, which will have an Include(string path) method, where path is a "." delimited navigation path of nested ICollection properties of the inheriting class to be enumerated.
For example, given the following classes;
public class Region
{
public string Name { get; set; }
public ICollection<Country> Countries { get; set; }
}
public partial class Region : EntityBase<Region> {}
public class Country
{
public string Name { get; set; }
public ICollection<City> Cities { get; set; }
}
public partial class Country : EntityBase<Country> {}
public class City
{
public string Name { get; set; }
}
public partial class City : EntityBase<City> {}
I want to be able to do something like this;
Region region = DAL.GetRegion(4);
region.Include("Countries.Cities");
So far I have the following;
public class EntityBase<T> where T : class
{
public void Include(string path)
{
// various validation has been omitted for brevity
string[] paths = path.Split('.');
int pathLength = paths.Length;
PropertyInfo propertyInfo = type(T).GetProperty(paths[0]);
object propertyValue = propertyInfo.GetValue(this, null);
if (propertyValue != null)
{
Type interfaceType = propertyInfo.PropertyType;
Type entityType = interfaceType.GetGenericArguments()[0];
// I want to do something like....
var propertyCollection = (ICollection<entityType>)propertyValue;
foreach(object item in propertyCollection)
{
if (pathLength > 1)
{
// call Include method of item for nested path
}
}
}
}
}
Clearly, the "var list = ...>" line doesn't work but you hopefully get the gist, and the foreach will not work unless is the propertyCollection is enumerable.
So it's the last bit, i.e. how do I enumerate an ICollection property of a class when I do not know the type of T until runtime?
Thanks
You don’t need Reflection for this. In order to enumerate it, you only need an IEnumerable. ICollection<T> inherits IEnumerable, so all of your collections will be enumerables. Therefore,
var propertyCollection = (IEnumerable) propertyValue;
foreach (object item in propertyCollection)
// ...
will work.
Generics are normally used when the client can resolve the generic type at compile-time.
Leaving that aside, since all you need to do is enumerate the propertyCollection (viewing each element of the sequence simply as a System.Object) all you need to do is:
var propertyCollection = (IEnumerable)propertyValue;
foreach(object item in propertyCollection)
{
...
}
This is perfectly safe since ICollection<T> extends IEnumerable<T>, which in turn extends IEnumerable. What T actually ends up being at run-time is irrelevant since the loop only requires object.
The real question is: Is System.Object sufficient for your purposes inside the loop?
Related
I have 2 types :
public class Type1
{
public string Name { get; set; }
}
public class Type2
{
public string Name { get; set; }
}
I have a list of elements (each element is an object type).
Some elements could be an array. (an array could be a type1[] or a type2[])
My goal is to :
1-iterate on my list of elements
2-determine which are type1[]array pr type2[] array
3-get the Name value property for element of those previous array
This is what I have done :
foreach (var Myobject in MyList)
{
if (myObject.GetType().IsArray)
{
var elementType = myObject.GetType().GetElementType()// should me return the element type, ie Type1 or Type2
//This is where I am stuck, I know that my object is an array but I cannot cast if in type1[] or type2[] array by using elementType
//The following is not working
elementType[] myArrat = (elementType[])myObject;
// And I don't want to hardwrite each case for each possible type like this :
Type1[] myArrat = (Type1[])myObject;
Type2[] myArrat = (Type2[])myObject;
// I want to use the elementType that I got previously
}
}
Thanks in advance for your help.
You can't do what you are trying to do. And quite frankly, you probably don't need to do it either. If you are expecting different types it means you are going to do different things with each type. What you can do is to change Type1 and Type2 to extend the same base class and use the base class instead:
public class TypeBase
{
public virtual string Name { get; set; }
}
public class Type1 : TypeBase
{
}
public class Type2 : TypeBase
{
}
foreach (var myobject in MyList)
{
if (myObject.GetType().IsArray)
{
object[] array = (object[]) myObject;
TypeBase[] yourArray = array.Cast<TypeBase>();
//use the properties and methods of TypeBase instead of Type1 and Type2
//mark the methods and properties in TypeBase as virtual and
//override them on Type1 and Type2 if needed
}
}
elementType[] myArrat = (elementType[])myObje
elementTypr is not type name then it won't compile. However there are few other issues here. First of all you may want to loop through MyList elements to flat array items:
private static IEnumerable<object> Flat(IEnumerable<object> items)
{
foreach (var item in items)
{
var array = item as Array;
if (array != null)
{
foreach (var arrayItem in array)
yield return arrayItem;
}
else
yield return item;
}
}
Now you can enumerate through your list without worrying if some items are an array or not:
foreach (var item in Flat(Mylist))
{
}
What you should really do is to add an interface (or an abstract base class) to abstract concrete type:
interface INamedObject
{
string Name { get; set; }
}
class Type1 : INamedObject { ... }
class Type2 : INamedObject { ... }
Now you can replace object with INamedObject in Flat() return type (don't forget to add a cast for yield return). You will then use it like this:
foreach (var item in Flat(Mylist))
{
item.Name = ""; // It doesn't matter if it's Type1 or Type2!
}
Note that if you don't want (!!!) to add a base class/interface then you may still perform a type checking. It's a very bad practice and you should really consider to change your design to avoid that. What I'd suggest is to - at least - to do not use GetType() but directly as casting operator.
Suppose we have a NodeData class:
public class NodeData<T>
{
public string Name;
public T Value;
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
And a base Node class and child classes that have several properties with type NodaData:
public class Node
{
public List<NodeData<T>> listOutputs<T>()
{
var fieldInfos = GetType().GetFields();
var list = new List<NodeData<T>>();
foreach (var item in fieldInfos)
{
Type t = item.FieldType;
string name = item.Name;
if (t == typeof(NodeData<T>))
{
var output = new NodeData<T>(name, default(T));
list.Add(output);
}
}
return list;
}
}
public class TestNode : Node {
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode ()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
As you can see there is a method which lists all outputs with type T in the Node class So I can find what are the output fields of the child class in runtime:
TestNode node = new TestNode ();
var list = node.listOutputs<int>(); // this returns data
But I need to know how to use this method to list all NodeOutputs of any type T. In this example int and double. Do I need to add a method with this signature public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3. Is it possible to have method like this? return type is generic but there is no type argument for method.
Even after your edit(s) it is not entirely clear what you are trying to achieve but here are my assumptions:
-You want to have some kind of Node object that acts as a container for different types of NodeData elements.
-You want to be able to return one list from this Node object that contains all NodeData elements stored in the Node container, regardless of the NodeData objects' type.
Instead of returning a List> object from the listOutputs methods, just return the non-generic version of the List object. Then you don't have to deal with T in the method call.
The logic that loops through the objects in the non-generic list can then examine the type to process the contained NodeData objects correctly.
Important note: My proposed solution is by no means pretty but I think it answers the question. In my opinion something is already seriously flawed from an OO point of view in the presented code (e.g. use of reflection) and a better solution would have to start by changing the underlying data structures. But that can only be done if we have more information how this is to be used, e.g. what kind of logic consumes the returned list.
You can create a base interface that will be used to return the generic data.
public interface INodeData
{
string Name { get; }
}
public class NodeData<T> : INodeData
{
public string Name { get; private set; }
public T Value { get; private set; }
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
I modified the function to return a list of the interface. Doing this you won't depend on T.
public class Node
{
public List<INodeData> listOutputs()
{
var fieldInfos = GetType().GetFields();
var list = new List<INodeData>();
foreach (var item in fieldInfos)
{
INodeData data = GetType().GetField(item.Name).GetValue(this) as INodeData;
list.Add(data);
}
return list;
}
}
If you test the method, it should return the fields in a list. To work with a specific type, you can make use of is before using the type you search for.
public class TestNode : Node
{
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
private static void Main(string[] args)
{
TestNode node = new TestNode();
var list = node.listOutputs(); // this returns data
}
This may well be an XY problem, in that you probably want to rethink how you are designing your classes because using reflection in this way doesn't seem right. But give the problem you've presented, I'd tackle it like this:
public abstract class NodeDataBase
{
public string Name { get; set; }
public NodeData(string name)
{
this.Name = name;
}
// this isn't actually needed, but might be helpful
public abstract object GetValue();
}
public class NodeData<T> : NodeDataBase
{
public T Value { get; set; }
public NodeData(string name, T value) : base(name)
{
this.Value = value;
}
public override object GetValue()
{
return Value;
}
}
And now your method signature would be:
public List<NodeDataBase> listOutputs()
And with the list returned, you can use the GetValue method to get the actual values without needing to cast to the right generic type to be able to get at the Value property.
You could also just have a return type of List<object>, but then you'll have to cast each member of that list to the right generic type before you can access it's properties.
You can also avoid that nasty reflection code, instead of having data, data1, and data2, you could simply do this in your Node class:
public class Node
{
public List<NodeDataBase> Data { get; protected set; }
public Node()
{
Data = new List<NodeDataBase>();
}
}
And now you don't even need your listOutputs method because you can just get the list from the node (unless you actually wanted a copy, but that's fairly trivial to implement).
And you TestNode would be just:
public class TestNode : Node {
public TestNode ()
{
Data.Add(new NodeData<int>("test", 111));
Data.Add(new NodeData<double>("test", 113));
}
}
What Utility or Pattern can be used to solve this Issue? I don't know what can be used to assist with this. Can we use some type of pattern?
If you have the following abstract class:
abstract class Foo
{
function void Something()
{
// Get the media type
}
}
And the following classes that derive from that class:
class Foo1 : Foo
{
public string MyId {get;set}
public string MyFile {get;set}
public TxtFile MyTextFile {get;set}
function void myFooFunction()
{
// Save File to Txt
}
}
class Foo2 : Foo
{
public string MyId {get;set}
public string MyFile {get;set}
public XMLFile MyXMLFile {get;set}
function MyOtherFunction()
{
// Save to XML
}
}
Then in Linq (or Similar) within the repository you do something like this:
var a = (from e in db.myTable
where e.myFileType == "XML"
Select e);
Then we have to map this to the correct object. Like this:
Foo newFoo = FooFactory.CreateFooFor(a.myFileType.ToString())
newFoo.MyId = a.id;
newFoo.MyFile = a.myfile;
newFoo.MyXMLFile = a.xml;
The Factory certainly helps, but how do you do this for multiple "FileTypes" like txt for example? The Fields wouldn't match up!
Do I have to write more code that does the same thing??
I feel like there has to be something that can do this.
First, if myFooFunction and MyOtherFunction are both used to save, you can use the strategy pattern and just define and abstract Save() method to implement in derived classes. You might also look at the Template Method pattern.
Although this isn't exactly a pattern, you might also want to apply the "Pull Up Field" refactoring here and put the MyId and MyFile properties in the parent class.
For creation...
The Builder pattern is similar to the factory, but allows for more complex object creation. I don't know how well it fits this simplified example, but it might fit what you are actually trying to do in your real code. Probably not. I just mention it first because it is the closest to factory in my mind.
There are also the Mapper Pattern and the Data Mapper Pattern. You might encapsulate the mapping in an object and have the factory return a mapper:
FooMapper mapper = FooMapperFactory.CreateFooMapperFor(a.myFileType);
Foo newFoo = mapper.CreateFoo(a);
I believe you could solve your problem using generics. I took the liberty of changing around some code. Would this work?
public abstract class Foo
{
public abstract void Save();
public void Something()
{
// Get the media type
}
}
public class FooText : Foo
{
public string MyId { get; set; }
public string MyFile { get; set; }
public string MyTextFile { get; set; }
public override void Save()
{
// Save File to Txt
}
}
public class FooXml : Foo
{
public string MyId { get; set; }
public string MyFile { get; set; }
public string MyXMLFile { get; set; }
public override void Save()
{
// Save to XML
}
}
public class FooFactory<T> where T : Foo, new()
{
public static T CreateFoo()
{
return new T();
}
}
If you consider using reflection on the data that's returned from the database, or perhaps the Adapter pattern you can set up a dynamic way to map fields to each other. Using reflection (the following is pseudo logic as reflection is kind of messy code to provide):
Get a list of PropertyInfo objects for all public properties from the target type
Get a list of PropertyInfo objects for all public properties from the database type
Compare their names/types to create the mapping
Assign values from the database type, using reflection, to the target type
Something like this will do the trick:
public void AssignAndMapTypes<DatabaseType, TargetType>(DatabaseType db, ref TargetType target)
{
var dbType = db.GetType();
var dbTypeProperties = dbType.GetProperties(System.Reflection.BindingFlags.Public);
var targetType = target.GetType();
var targetTypeProperties = targetType.GetProperties(System.Reflection.BindingFlags.Public);
foreach (var prop in targetTypeProperties)
{
var matchingProp = dbTypeProperties.Where(e => { return (string.Compare(e.Name, prop.Name, true) == 0) && (e.PropertyType == prop.PropertyType) }).FirstOrDefault();
if(matchingProp != null)
{
prop.SetValue(target, matchingProp.GetValue(db, null), null);
}
}
}
I have got three classes as follows:
public class TestA
{
public string Str1 { get; set; }
public string Str2 { get; set; }
public List<TestB> LstTestBs { get; set; }
public TestC ObjTestC { get; set; }
}
public class TestB
{
public string Str3 { get; set; }
public string Str4 { get; set; }
}
public class TestC
{
public string Str5 { get; set; }
}
I have tried:
var prop = typeof (TestA).GetProperties();
But, it is giving only the PropertyInfo for the four members inside TestA. I need to get the PropertyInfo for all the members in the TestA, TestB and TestC classes.
Please help...
Thanks in advance,
San
If you put all your classes in the same namespace, you can collect the properties by enumerating the classes in the namespace, instead of mining the property structure:
Getting all types in a namespace via reflection
Thanks for the help everyone.
I have got the answer.
var prop = typeof (TestA).GetProperties();
for (int i=0;i<prop.Count();i++)
{
var propertyInfo = prop[i];
if (propertyInfo.PropertyType.Namespace != "System")
{
if (propertyInfo.PropertyType.IsGenericType &&
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof (List<>))
{
Type itemType = propertyInfo.PropertyType.GetGenericArguments()[0];
var listObjectProperties = itemType.GetProperties();
prop = prop.Union(listObjectProperties).ToArray();
}
else
{
var childProp = propertyInfo.PropertyType.GetProperties();
prop = prop.Union(childProp).ToArray();
}
}
}
SLaks is right. You should do this recursively. See wikipedia's article on Recursion for more information on the concept. For example, in your case, this is the general idea:
public void AddPropertiesAndChildPropertiesToList(Type type, List<PropertyInfo> list)
{
var properties = type.GetProperties();
list.AddRange(properties);
foreach (var property in properties)
{
// recursive methods are ones that call themselves, like this...
AddPropertiesAndChildPropertiesToList(property.PropertyType, list);
}
}
Note that this example is lacking several things:
Most importantly, it has no guard against infinite recursion. You could fix this by keeping track of where you'd already been with a Stack<Type> alreadyVisited parameter. If you find you've been asked to add the list of properties for a type you've already visited, just return out of the method instead, or throw an exception.
As I mentioned in your other related question, for your purposes you really need to be keeping track of property chains, not just properties. The alreadyVisited stack would be useful here, too.
It won't handle your List<TestB> in any useful way. For that, you probably need to figure out whether the type has an indexer, and then the properties of the type that is returned by that indexer.
I have a class MyDatabaseContext that has a series of DbSet collection properties:
public DbSet<EntityA> EntitiesA { get; set; }
public DbSet<EntityB> EntitiesB { get; set; }
public DbSet<EntityC> EntitiesC { get; set; }
I need to get the name of the collection given the type of the entity.
For example, I have "EntityB" and want to get as a result "EntitiesB".
I really wanted to avoid switch-case statements, since MyDatabaseContext is generated automatically (T4 templates).
if you just want the name of the property here you go. I would just refine the answer given by hunter. You can use the same method with string as return type.
public string GetEntitiName<T>() where T : class
{
PropertyInfo propInfo = typeof(MyDatabaseContext).GetProperties().Where(p => p.PropertyType == typeof(DbSet<T>)).FirstOrDefault();
string propertyName = propInfo.Name; //The string has the property name ..
return propertyName;
}
I tried a sample similar to your situation. Try replacing List with DbSet.
class Program
{
public static void GetEntities<T>() where T : class
{
var info = typeof(TestClass1).GetProperties().Where(p => p.PropertyType == typeof(List<T>));
Console.WriteLine(info.FirstOrDefault().Name);
}
static void Main(string[] args)
{
GetEntities<int>();
Console.ReadLine();
}
}
public class TestClass1
{
public List<int> IntTest { get; set; }
public List<double> DoubleTest { get; set; }
public List<string> IStringTest { get; set; }
}
This sample works.
I know this is old page, But my answer maybe useful for other guys referring here. (like me)
I think you want to accessing EntitiesB to run a query on it, like EntitiesB.Where(a=>a.bla=="blabla"). If I'm right or another visitor of this page needs something like this, just easily use the following code:
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>()
Description:
_dbContext is Context class inherting from DbContext.
EntitiesB is DbSet<EntityB> defined in Context class.
Example:
Ilist result = ((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>().Where(b=>b.bla=="blabla").ToList();
Your generated file is a partial class, you could create a new file and declare a class with same name using the keyword partial, then make a method which will return the desired Collection...
I haven't actually done this myself, but it sounds like what you want to do is to use reflection to locate the property of type "DbSet" that has the appropriate generic type parameter. The following pseudo-C# should get you started:
foreach ( FieldInfo field in this.GetType() )
{
if ( field.FieldType.IsGenericType )
{
foreach ( Type param in field.FieldType.GetGenericArguments() )
{
if ( param.Name == soughtType )
{
return field.Name;
}
}
}
}