I have the following:
using (var dsProperties = GetDataset(SP_GET_APPLES, arrParams))
{
var apples= dsProperties.Tables[0].AsEnumerable()
.Select(r => new Apple()
{
Color = r[0].ToString(),
Year = r[1].ToString(),
Brand= r[2].ToString()
});
return apples.ToList();
}
Now, I would like to have an extension method on Dataset to which I can pass the needed Type as a parameter and get the intended List back... something like
dsProperties.GetList(Apple);
which can also be used for
using (var dsProperties = GetDataset(SP_GET_ORANGES, arrParams)){
dsProperties.GetList(Orange); }
Is there a way to accomplish this?
How about this?
static IEnumerable<T> GetList<T>(this DataSet dataSet, Func<DataRow, T> mapper) {
return dataSet
.Tables[0]
.AsEnumerable()
.Select(mapper);
}
And usage:
dsProperties.GetList<Apple>(r =>
new Apple {
Color = r[0].ToString(),
Year = r[1].ToString(),
Brand= r[2].ToString()
});
This mapping can well be put in another place as well.
Something like the (untested) following, but it would need a lot of error handling added (if a field is missing, wrong data type, nulls).
public static IEnumerable<T> GetEnumeration<T>(this DataSet dataset) where T: new()
{
return dataset.Tables[0].AsEnumerable()
.Select(r => {
T t = new T();
foreach (var prop in typeof(T).GetProperties())
{
prop.SetValue(t, r[prop.Name]);
}
return t;
});
}
You would use it like dataset.GetEnumeration<Apple>().ToList(). Note that this uses reflection and could be slow for large data sets, and makes a lot of assumptions, such as each field in the type matching the columns in the data table. Personally I use a repository for each business object which explicitly constructs the object from a data row. More work to set up but in the long run I have a bit more control. You could probably look at an ORM framework like NHibernate as well.
I think your best (and cleanest, as in "reflection-less") bet will be to create a constructor for each involved class (Apple, Orange, etc.) that takes a DataRow and initializes the object based on the row. Then, your code simplifies to dsProperties.Tables[0].AsEnumerable().Select(r => new Apple(r)). Simplifying it further into a generic extension method will be difficult because you cannot have type constraints that specify the existence of a constructor that takes certain parameters.
If you really want a generic extension method for this, I think you'll have to use the factory pattern, so that the extension method can instantiate a factory that can convert DataRows into the desired type. That's gonna be quite a bit of code for (I think) quite little benefit, but I can create an example if you'd like to see it.
Edit: I'd advise you to rather create an extension method that lets you do this: dsProperties.CreateFruits(r => new Apple(r)). That's about as short as it would be with the extension method you requested. You'll still have to create the constructors, though, so if what you're really after is to save coding on the object constructions, then you'll probably need reflection-based approaches as described in the other answers.
Edit again: #MikeEast beat me to my last suggestion.
Is there a standard naming convention between your stored procedures and your types? If so then you can reflect on the type and retrieve its name then convert that to its stored procedure.
Otherwise, you could have a static dictionary of the type name as a key and the value being the associated stored procedure. Then in your method you would look up the stored procedure after reflecting on the type.
Also I believe you will need to use generics. Something like (it's been a while since i've done generics):
public static IEnumerable<T> GetList<T>(this DataSet ds)
The conversion of columns to properties on your object would also be achieved through reflection. You would loop over the properties on the object and find a matching column to insert the value.
Hope this helps to get you started.
I like MikeEast's approach, but don't like the idea that you have to pass the mapping to every call to GetList. Try this variation instead:
public static class DataSetEx
{
private static Dictionary<Type, System.Delegate> __maps
= new Dictionary<Type, System.Delegate>();
public static void RegisterMap<T>(this Func<DataRow, T> map)
{
__maps.Add(typeof(T), map);
}
public static IEnumerable<T> GetList<T>(this DataSet dataSet)
{
var map = (Func<DataRow, T>)(__maps[typeof(T)]);
return dataSet.Tables[0].AsEnumerable().Select(map);
}
}
Now, given the classes Apple & Orange call the following methods to register the maps:
DataSetEx.RegisterMap<Apple>(r => new Apple()
{
Color = r[0].ToString(),
Year = r[1].ToString(),
Brand= r[2].ToString(),
});
DataSetEx.RegisterMap<Orange>(r => new Orange()
{
Variety = r[0].ToString(),
Location = r[1].ToString(),
});
Then you can just call GetList without the map like so:
var ds = new DataSet();
// load ds here...
// then
var apples = ds.GetList<Apple>();
// or
var oranges = ds.GetList<Orange>();
That gives some nice code separation without the need for reflection or repetition.
This also doesn't stop you using a hybrid approach of using reflection in cases where a map hasn't explicitly been defined. You kind of can get the best of both worlds.
Related
I would like to make my code convention-based by using Types and keeping things simple, but generics has it's own complexity with it's own learning curve.
I have a bunch of POCOs (Plain Old CLR Objects) in a List that I'd like to iterate through later in the code.
var models = new List<Type>();
models.Add(typeof(Person));
models.Add(typeof(Company));
Would like to cycle through each list item:
models.ForEach(m =>
{
var label = m.FullName;
// var data = JsonConvert.DeserializeObject<List<typeof(m)>>(""); // doesn't work
var data = JsonConvert.DeserializeObject<List<m>>(""); // doesn't work either
...
}
The issue is that the "m" in the Deserialize line isn't working. What would be the best way to pass that through, i.e. making the 'List<m>' a 'List<T>' that we can use?
To use generics, you really need to know the Type (T) at compile time, you don't - you know it at run time. (Caveat: Its possible with reflection, but theres no need to use it when there's an overload as described below)
There is an overload of DeserializeObject which takes a Type rather than use generics. So your code would be
models.ForEach(m =>
{
var label = m.FullName;
var data = JsonConvert.DeserializeObject("",m);
...
}
However, as you've pointed out in comments you actually need a List<T> not a single T. You'll need a little bit of reflection, just to create the right type to pass to the above DeserializeObject call.
var tList = typeof(List<>); // Type of open List
models.ForEach(m =>
{
var label = m.FullName;
var tConvert = = tList.MakeGenericType(m);
var data = JsonConvert.DeserializeObject("",tConvert);
...
}
The answer to your question is above, but the more I look at it the harder it is to see what you can actually do with data. all you'll ever know about data is that it is an object. You cant cast it to anything - you wont know if its a list of Person or a list of Company.
Perhaps this was an overly contrived example you've used for a real-life problem. If not I forsee you're next problem is what to do with data!!
If you don't know the type at compile time you can do this with Reflection. Consider the following code:
models.ForEach(m =>
{
var mi = JsonConvert.GetType()
.GetMethod("DeserializeObject");
var m = mi.MakeGenericMethod(new[] { m });
// you pass [null] for the object because it's a [static] method
// and you don't have to declare [args] if you can express it simply
// but keep in mind that it's simply an object[]
m.Invoke(null, args);
}
Another solution is to call the generic method using reflection (if there isn't any overload that takes the type as parameter)
models.ForEach(m =>
{
MethodInfo method = typeof(JsonConvert).GetMethod("DeserializeObject");
MethodInfo generic = method.MakeGenericMethod(m);
generic.Invoke(null, "");
}
I have a method, which takes data received from database, and it takes only data which has a specific day:
private static **return type** FilterDataByDay(DataTable dt, string nameOfTheDay)
{
var data = dt.AsEnumerable().Select(x => new
{
code = x.Field<string>("Code"),
day= x.Field<string>("Day"),
name= x.Field<string>("Name"),
startTime= x.Field<int>("StartTime"),
endTime= x.Field<int>("EndTime")
})
.Where(x => x.day.Equals(nameOfTheDay))
.ToList();
return data ;
}
The problem is I don't know how to return data from this method, that other method could take it ant print it out, every element like of each row, it should let me print something like:
foreach(var variable in "the retuned data from method")
{
Console.WriteLine(variable.code +" "+ variable.day + ..etc...
}
The thing about anonymous types is that they're not named, e.g. anonymous.
The best course of action is to define your own class with the appropriate properties after which you can return IEnumerable<SomeClass>.
You could create a Wrapper class and then you'll have IEnumerable<MyClass>
or you can use IEnumerable<dynamic> , but this way - you wont have intellisence.
Defining a proper class that would encapsulate the loaded data is probably the best way to go. But if you are dealing with data you will not need later and you don't pass them to much around, you can leverage the Tuple class. It will look slightly awkward, but if it is just your code and it is not a part of a public API then it is also usable.
Your return type would then be IEnumerable<Tuple<string, string, string, int, int>>
Code example:
void Foo(params object[] objects)
{
var entries = new List<IEntry>();
foreach(var o in objects)
{
var entry = new Entry<o.GetType()>(); // this doesn't work
entries.Add(entry);
}
...
}
Foo("hello", 5); // should fill entries with Entry<string> and Entry<int>
Why is that not possible? I guess I need to work with reflection instead? How to do that properly AND performant?
You just can't use C# generics the way you're trying to do in your snippet.
In order to use [C#] generics, the actual object type must be known at compile time.
You're trying to dynamically pass the object type as a type parameter. This is simply not possible.
Edit
Yes, it is possible to dynamically create generic objects using reflection. After all, generics is implemented both as a compile-time C# construct and as a .NET framework feature (as opposed to, say, Java, where it is only a compile-time feature based on Type Erasure). So, in .NET, through reflection, it is possible to implement the latter "bypassing" the former (which, again, would be impossible in Java).
But the OP clearly does not need that.
After all, entries is a List<IEntry>. IOW, the entries container does not "know" the concrete type of its elements (since it is bound to an interface). So, if each element to be add already implements IEntry, then this would be enough:
void Foo(params IEntry[] objects)
{
var entries = new List<IEntry>();
foreach(var o in objects)
{
entries.Add(o);
}
...
}
OTOH, if those objects do not implement IEntry, then the OP just need a pure, ordinary, old-school list of untyped objects:
void Foo(params object[] objects)
{
var entries = new List<object>();
foreach(var o in objects)
{
entries.Add(o);
}
...
}
So using reflection in order to dynamically create a generic container, even if possible, seems to be overkill for this particular use case.
You can do it with reflection
var entryType = typeof(Entry<>);
Type[] typeArgs = { o.GetType() };
var genericType = entryType.MakeGenericType(typeArgs);
IEntry entry = (IEntry)Activator.CreateInstance(genericType);
You need a function of the form:
Func<Type, IEntry>
I would suggest adding a static function to the parent of Foo like this:
public static IEntry Make(Type type)
Inside that function, feel free to add whatever code makes sense to you:
if (type == typeof(string))
{
return new StringEntry(); //Obviously some special logic based on the type.
}
else
{
//Default logic
return (IEntry) Activator.CreateInstance(typeof(Entry<>).MakeGenericType(type));
}
I'm looking for a way to pass in a list of strongly typed property names into a method that I can then dissect and get the properties that the caller is interested in. The reason I want to do this is to have a copy method that only copies the fields the user specifies. Right now, the method takes a list of strings to use with the Getvalues and get properties methods in reflection, but I want to guard against refactoring of properties and the strings not being updated by the developer.
I found this article Here, but unfortunately, it does not do a list. I can do something like:
public static void Copy(Expression<Func<TObject>> propertiesToCopy )
{
}
And then have the caller do
PropertyCopier<List<string>>.Copy(() => data);
But then I have to specify how many properties the caller can have like this:
public static void Copy(Expression<Func<TObject>> propertiesToCopy,Expression<Func<TObject>> propertiesToCopy2, Expression<Func<TObject>> propertiesToCopy3 )
{
}
This would allow for three properties. Is there anyway to add it to a List or Queryable<> to allow as many properties as the caller wants? I tried using the Add in List and having the Expression
Thanks in advance
Edit: I did find a few articles this evening that refer to using the C# param keyword to accomplish this. Are there any better or more efficient ways, or is this the best way to do it?
Use the params keyword to define a method that takes a variable number of arguments:
public static void PrintPropertyNames<T>(params Expression<Func<T, object>>[] properties)
{
foreach (var p in properties)
{
var expression = (MemberExpression)((UnaryExpression)p.Body).Operand;
string memberName = expression.Member.Name;
Console.WriteLine(memberName);
}
}
For instance, you could call the PrintPropertyNames method passing two expressions:
PrintPropertyNames<FileInfo>(f => f.Attributes, f => f.CreationTime);
This example displays the following output to the console:
Attributes
CreationTime
Consider the following:
// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
// or create a new object
var subset = new {
Id = 1,
Name = "something random",
Email = "name#domain.tld"
};
Is there any way to use the subset variable as a parameter to a method, without it being cast as a plain Object? Can you somehow carry the auto-generated type of the variable?
I am trying to avoid having to create new classes every time I want to pass LINQ subsets to methods.
Random generic approaches are welcome.
No, passing anonymous types about isn't generally a good idea because you lose the type information*. You should create a concrete type and use that instead.
var subset = DataTable.Where(...).Select(row => new SomeType
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
Alternatively you can use the Tuple type if you are using .NET 4. This is a simple way to create "disposable" types and still get some type-safety.
*Actually there is a workaround, but I regard it is an ugly hack and would advise that you don't do it.
If I need to do this, I use resharper's "Replace Anonymous Type With Named Class" refactoring option. Then you have an appropriate named type to expose over the API, and you haven't had to do any work. This also gives you options to create it immutable (like anonymous types) or mutable, nested vs top-level, etc.
BTW, I don't recommend struct here (from the question).
Another option is to pass the behaviour into the method - i.e. an Action<int,string,string> callback - then do something like:
foreach(item in query) callback(item);
However, I don't like this as it is not obvious that there is a likely error in:
DoSomething(args, (id, email, name) => Email(To: email, Subject: name));
(the error being that it should probably be (id, name, email), if you see what I mean)
You can use a generic method:
public static void Foo<T>(T item)
{
// Do whatever
}
Then if you call
Foo(subset);
the compiler will infer T for you. Whether or not that actually helps you is another matter... it depends on what the method is meant to do. Obviously Foo can't refer to Id, Name, Email etc.
In general, if multiple methods should know about the same members, then you should use a named type. The usual case for passing them to generic methods is where the method really doesn't care about what type is involved, such as in LINQ.
I've made a feature request for C# 5 that we should be able to create types which have all the same features as anonymous types (immutability, equality, hash code generation, ToString dumping) but for simple named types. We'll see if it actually happens...
Anonymous Types don't provide much help outside of the context they where created.
If you need to pass an Anonymous Type to a method, either this method is very generic like (Example)
void PrintAllObjectProperties(object obj);
witch you would use reflection to do the work, or you are doing something wrong.
Here's what I came up with...
Extension method on Object:
public static class ObjectExtensions
{
/// <summary>
/// Cast Object to anonymous type.
/// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
/// </summary>
public static T ToAnonymousType<T>(this Object o, T t)
{
return (T)o;
}
}
Usage:
public void HandleAnonymousTypeAsParameter(Object o)
{
var anonymousType = o.ToAnonymousType(new
{
Id = new Int32(),
Foo = new String(),
Bar = new String()
});
// ... You can do this in even less characters:
var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}
HandleAnonymousTypeAsParameter(new
{
Id = 1,
Foo = "foo",
Bar = "bar"
});
Credits goes to John Skeet and Thomas P.