I'm trying to create objects dynamically but I don't know how to. What I need is, I have a class for that object, and objects properties are stored in the database. Then I'll need to compare the properties of each object to get the desired result.
So I need to dynamically create objects on the fly with the properties loaded from database.
I don't think you need to create objects dynamically, just create one statically that matches your db schema with the property details, then you can compare the values of the properties across rows, or within an instance of your object.
I have been working on something similar to this. There are several things:
Include the System.Reflection namespace
Create an object dynamically using Activator
Get the object properties using the myObjectType.GetProperties() method
Here is an example of a generic object creation function using the above methods:
using System.Reflection;
public static Item CreateItem<Item>(object[] constructorArgs, object[] propertyVals)
{
//Get the object type
Type t = typeof(Item);
//Create object instance
Item myItem = (Item)Activator.CreateInstance(t, constructorArgs);
//Get and fill the properties
PropertyInfo[] pInfoArr = t.GetProperties();
for (int i = 0; i < pInfoArr.Length; ++i)
pInfo.SetValue(myItem, propertyVals[i], null); //The last argument is for indexed properties
return myItem;
}
Of course the above example assumes that the values in the property value array are arranged correctly, which is not necessarily the case, but you get the idea.
With the PropertyInfo class you can get properties, get property names, get attributes associated with the properties, etc. Powerful technology. You should be able to do what you need with the above info, but if not let me know and I will add more info.
If you have a number of objects you want to instantiate from database values it can be done something like this.
//database code goes here, results go in results
List<ClassName> l = new List<ClassName>()
foreach(Row r in results){
l.Add(new ClassName(){ClassProperty1 = r.Property1,ClassProperty2 = r.Property2});
}
Are you talking about Dictionary?
var dict=new Dictionary<string, string>();
dict.Add("property1", "val1");
dict.Add("property2", "val2");
var prop2val=dict["property2"];
Maybe Activator is what your looking for?
http://msdn.microsoft.com/en-us/library/system.activator.aspx
Check this class, compile in the realtime. But it's performance is not quite good.
http://msdn.microsoft.com/zh-cn/library/microsoft.csharp.csharpcodeprovider(VS.80).aspx
You could use reflection to dynamically build your objects:
Reflection msdn reference
I think that you want to retrieve rows from the DB and directly assign them to object given that the properties of the object are equivalent to the columns of DB table. If that what you mean then I believe you can't :)
Rob Conery did a small project called Massive that pretty much does what you're trying to accomplish. It's essentially a small ORM, in 400 lines of Dynamic C# 4.0 code.
Rob has been doing this kind of thing for quite some time with SubSonic, so you might find his approach with Massive quite interesting.
http://blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive
Some of the code is explained here, with examples:
http://blog.wekeroad.com/microsoft/the-super-dynamic-massive-freakshow
Related
I am trying to get the actual object that is contained within a list that itself is contained within a task.
e.g. method has the following signature e.g.
public async Task<List<Dictionary<string,object>>> GetData()
i am currently using something like this:
var member = type.GetMembers()[0];
var returntype = member.ReturnType.GetGenericArguments();
var temp = member.ReturnType.GetGenericArguments()[0];
if (temp.GetGenericArguments().Count() > 0)
{
temp.GetTypeInfo().GetGenericArguments();
var innerttype = temp.GetGenericArguments()[0].FullName;
}
Currently the above code (which is not complete but just an extract from actual code) return system.object as fullname instead of Dictionary.
Any suggestions to solve this are welcome.
If you declare your dictionary to be of type <string, object> and and only ever insert objects inside it and not something higher up in the graph, you'll only ever get object typed objects out of it.
If you're storing all kinds of things in there and need to get their concrete type so you can interact with them, try to make them all conform to an interface first. Then, put the interface type in there replacing "object." If that doesn't work you can use generics. However, you'll still need to know the type ahead of time in order to be able to interact with it.
If you really have no idea what's in there and want to dig into it dynamically, that's precisely what Reflection was built for. You could also look into the dynamic type.
So here's my dilemma. I'm trying to utilize Dynamic LINQ to parse a search filter for retrieving a set of records from an Azure table. Currently, I'm able to get all records by using a GenericEntity object defined as below:
public class GenericEntity
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
Dictionary<string, object> properties = new Dictionary<string, object>();
/* "Property" property and indexer property omitted here */
}
I'm able to get this completely populated by utilizing the ReadingEntity event of the TableServiceContext object (called OnReadingGenericEvent). The following code is what actually pulls all the records and hopefully filter (once I get it working).
public IEnumerable<T> GetTableRecords(string tableName, int numRecords, string filter)
{
ServiceContext.IgnoreMissingProperties = true;
ServiceContext.ReadingEntity -= LogType.GenericEntity.OnReadingGenericEntity;
ServiceContext.ReadingEntity += LogType.GenericEntity.OnReadingGenericEntity;
var result = ServiceContext.CreateQuery<GenericEntity>(tableName).Select(c => c);
if (!string.IsNullOrEmpty(filter))
{
result = result.Where(filter);
}
var query = result.Take(numRecords).AsTableServiceQuery<GenericEntity>();
IEnumerable<GenericEntity> res = query.Execute().ToList();
return res;
}
I have TableServiceEntity derived types for all the tables that I have defined, so I can get all properties/types using Reflection. The problem with using the GenericEntity class in the Dynamic LINQ Query for filtering is that the GenericEntity object does NOT have any of the properties that I'm trying to filter by, as they're really just dictionary entries (dynamic query errors out). I can parse out the filter for all the property names of that particular type and wrap
"Property[" + propName + "]"
around each property (found by using a type resolver function and reflection). However, that seems a little... overkill. I'm trying to find a more elegant solution, but since I actually have to provide a type in ServiceContext.CreateQuery<>, it makes it somewhat difficult.
So I guess my ultimate question is this: How can I use dynamic classes or generic types with this construct to be able to utilize dynamic queries for filtering? That way I can just take in the filter from a textbox (such as "item_ID > 1023000") and just have the TableServiceEntity types dynamically generated.
There ARE other ways around this that I can utilize, but I figured since I started using Dynamic LINQ, might as well try Dynamic Classes as well.
Edit: So I've got the dynamic class being generated by the initial select using some reflection, but I'm hitting a roadblock in mapping the types of GenericEntity.Properties into the various associated table record classes (TableServiceEntity derived classes) and their property types. The primary issue is still that I have to initially use a specific datatype to even create the query, so I'm using the GenericEntity type which only contains KV pairs. This is ultimately preventing me from filtering, as I'm not able to do comparison operators (>, <, =, etc.) with object types.
Here's the code I have now to do the mapping into the dynamic class:
var properties = newType./* omitted */.GetProperties(
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public);
string newSelect = "new(" + properties.Aggregate("", (seed, reflected) => seed += string.Format(", Properties[\"{0}\"] as {0}", reflected.Name)).Substring(2) + ")";
var result = ServiceContext.CreateQuery<GenericEntity>(tableName).Select(newSelect);
Maybe I should just modify the properties.Aggregate method to prefix the "Properties[...]" section with the reflected.PropertyType? So the new select string will be made like:
string newSelect = "new(" + properties.Aggregate("", (seed, reflected) => seed += string.Format(", ({1})Properties[\"{0}\"] as {0}", reflected.Name, reflected.PropertyType)).Substring(2) + ")";
Edit 2: So now I've hit quite the roadblock. I can generate the anonymous types for all tables to pull all values I need, but LINQ craps out on my no matter what I do for the filter. I've stated the reason above (no comparison operators on objects), but the issue I've been battling with now is trying to specify a type parameter to the Dynamic LINQ extension method to accept the schema of the new object type. Not much luck there, either... I'll keep you all posted.
I've created a simple System.Refection.Emit based solution to create the class you need at runtime.
http://blog.kloud.com.au/2012/09/30/a-better-dynamic-tableserviceentity/
I have run into exactly the same problem (with almost the same code :-)). I have a suspicion that the ADO.NET classes underneath somehow do not cooperate with dynamic types but haven't found exactly where yet.
So I've found a way to do this, but it's not very pretty...
Since I can't really do what I want within the framework itself, I utilized a concept used within the AzureTableQuery project. I pretty much just have a large C# code string that gets compiled on the fly with the exact object I need. If you look at the code of the AzureTableQuery project, you'll see that a separate library is compiled on the fly for whatever table we have, that goes through and builds all the properties and stuff we need as we query the table. Not the most elegant or lightweight solution, but it works, nevertheless.
Seriously wish there was a better way to do this, but unfortunately it's not as easy as I had hoped. Hopefully someone will be able to learn from this experience and possibly find a better solution, but I have what I need already so I'm done working on it (for now).
i'd like to be able to query any number of databases with different table layouts, return a datatable then use that datatable to build a strongly typed object. Is there anything out there that does this or comes close without having to code for each different table layout?
Thank you in advance!
Zac
You could try subsonic.
It does exactly that, using code generation via T4 templates.
Get more information at http://subsonicproject.com/
Cheers,
André
May be you can use LINQ and return Anonymous Types http://msdn.microsoft.com/en-us/library/bb397696.aspx
eg:
var result = (from itm in list where itm.StateID==2 select new {Name = itm.Name, State=Itm.StateID});
I'm not sure this is exactly what you're looking for, but the datatable base type has a method called GetTypedTableSchema() that returns an XmlSchemaSet object. You may be able to use this as a roadmap to translate a typed datatable into a strongly typed object.
Assuming you're trying to get Intellisense benefits - go the code generation route against your tables.
No. This is not possible.
In case of typed-datasets, If you need typed-safety at run-time, the type needs to be defined at design-time or you are going to use plain datasets/datatables for your database operations.
I am currently working with LINQ and C#.
I have a DropDownList of the tables in my LINQ to SQL model.
I want the user to be able to select a LINQ table name from the DropDown. In code I want to create an instance of that LINQ class and then run a Select or it or anything else I want.
How would I accomplish creating the object based on what object name in string the user chose? Am I thinking incorrectly from the start?
You want Type.GetType(string) and Activator.CreateInstance(Type).
Note that Type.GetType(string) will only look in the currently executing assembly and mscorlib unless you specify the full type name including assembly. In either case, you need to specify the type name including namespace.
Another alternative is to use Assembly.GetType(string) to get the type directly from the string before calling Activator.CreateInstance.
(There are actually lots of alternatives here. If none of these help you, please post more info and I'm sure we can work out a way to go.)
Since you tagged the post with ASP.NET, I am assuming that the list is on the client side. If that is the case, you should be very careful about trusting that data, and I would not recommend creating types directly from user input. You could use the data as input to a factory that could then return the proper instance (and handle any illegal input as you see fit).
Elaborating on Brian Rasmussen's warning: The types should be restricted and require conscious design. Preferable the "user-instantiable" types should be marked with a specific custom attribute that can be verified with reflection.
With LINQ-to-SQL, there are specific ways of doing this from the data-context; basically, db.GetTable. This returns an ITable, but it is a little tricky to work with an untyped ITable. You can enumerate it, at least...
To get the ITable, you normally need the Type, which you can get with (for example) Assembly.GetType:
using (var ctx = new MyDataContext()) {
string name = "Customer"; // type name
Type ctxType = ctx.GetType();
Type type = ctxType.Assembly.GetType(
ctxType.Namespace + "." + name);
ITable table = ctx.GetTable(type);
foreach(var row in table) {
Console.WriteLine(row); // works best if ToString overridden...
}
}
Of course, once you have the Type, you use use Activator to create new entity instances:
object newObj = Activator.CreateInstance(type);
// TODO: set properties (with reflection?)
table.InsertOnSubmit(newObj);
but if you want to use the property-name, that can work too:
using (var ctx = new MyDataContext()) {
string name = "Customers"; // property name
ITable table = (ITable) ctx.GetType()
.GetProperty(name).GetValue(ctx, null);
foreach (var row in table) {
Console.WriteLine(row); // works best if ToString overridden...
}
}
Running filters (Where) etc is tricky with untyped data, as building the Expression would be tortuous. I'd probably start switching to a typed model at that point...
To follow on Marc Gravell's answer.
Doing as he suggested, I noticed a Cast<TResult> extension method (defined in System.Linq).
Unfortunately you can't seem to be able to do use the type instance to cast:
Type dcType = dc.GetType();
Type type = dcType.Assembly.GetType(String.Format("{0}.{1}", dcType.Namespace, name));
var row = dc.GetTable(type).Cast<type>().SingleOrDefault(i => i.ID == 123);
I am able to dynamically train and create my regression model just fine from a string[] of column names. However, when I try to pass in a dynamic object with the same Parameter names as Dictionary Key Pair properties it throw the error:
System.ArgumentOutOfRangeException: 'Could not find input column '<MyColumn>'' Where <MyColumn> is the first parameter that the model is looking for.
private static void TestSinglePrediction(MLContext mlContext, dynamic ratingDataSample, int actual)
{
ITransformer loadedModel;
using (var stream = new FileStream(_modelPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
loadedModel = mlContext.Model.Load(stream);
}
var predictionFunction = loadedModel.MakePredictionFunction<dynamic, RatingPrediction>(mlContext);
var prediction = predictionFunction.Predict(ratingDataSample);
Console.WriteLine($"**********************************************************************");
Console.WriteLine($"Predicted rating: {prediction.Rating:0.####}, actual rating: {actual}");
Console.WriteLine($"**********************************************************************");
}
I suspect this is because the dynamic object doesn't contain the [Column] attributes that the standard class object I normally would pass in has.
However, I will eventually have hundreds of columns that are auto generated from transposing SQL queries so manually typing each column isn't a feasible approach for the future.
Is there any way I could perhaps apply the attribute at run time? Or any other way I can generically approach this situation? Thanks!
This is a great question. The dynamic objects don't work at runtime because ML.NET needs something called a SchemaDefinition for the objects that you pass in so that it knowns where to get the columns it expects.
The simplest way to solve your problem would be to define an object holding only the columns you need at scoring-time, annotated with Column attributes, and manually cast your dynamic object at runtime. This has the main advantage that since you do the casting to the scoring object yourself, you can handle missing data cases yourself without the ML.NET runtime throwing. While your SQL query may give you a large assortment of columns, you won't need the majority of these columns for scoring your model, and therefor don't need to account for them in the scoring object; you only have to account for the columns the model expects.
See this sample from the ML.NET Cookbook for an example of how to score a single row. Behind the scenes, ML.NET is taking the class you defined, and using attributes like Column to construct the SchemaDefinition.