Is it Possible to use reflection on LINQ to Entities to query a dynamic table? - linq

There are many tables in the database that are used as "lookup" tables. All the tables have the same structure, other than the ID column name.
I have found that I can use reflection to open a table and enumerate through the records. The method takes a string (tableName).
Uri serviceUri = new Uri("http://localhost/MyDataService/WcfDataService.svc");
var context = new MyEntities(serviceUri);
var eTable = typeof(MyEntities).GetProperty(tableName).GetValue(context, null) as IEnumerable<object>
foreach (object o in eTable)
...
This works fine, but I want to add a WHERE clause to the query. For example, where InactiveDate == null.
Can I do this? I have been unable to figure this one out.

How about this?
var eTable = (typeof(MyEntities).GetProperty(tableName).GetValue(context, null) as IEnumerable<object>).Where(obj => obj.GetType().GetProperty("InactiveDate").GetValue(obj) == null);
foreach (object o in eTable)

I would suggest to use generics and maybe an interface over reflection.
public function Xyz<TEntity>(Func<MyEntities, IDbSet<TEntity>> dbSetGetter, Expression<Func<TEntity, Boolean>> filter)
{
var serviceUri = new Uri("http://localhost/MyDataService/WcfDataService.svc");
using (var context = new MyEntities(serviceUri))
{
foreach (var entity in dbSetGetter(context).Where(filter))
{
DoSomethingWith(entity);
}
}
}
Usage would be like this
Xyz(context => context.Foo, foo => foo.Bar == 42);
assuming you have an entity Foo with an integer property Bar. The obvious difference to your code is that you have to know the entity type a compile time and I am not sure if you know it then.

Related

Converting an entity model to a dataset - can this be done?

Very frustrated here ...
I can usually find an answer of some kind to complex issues in .Net somewhere on the net, but this one eludes me.
I'm in a scenario where I have to convert the result of a LINQ to Entity query into a DataSet so the data can then be processed by existing business logic, and I can't find a single working solution out ther for this.
I've tried basic approaches like the EntityCommand generating a reader, but this one does not work because DataTable.Load() thorws an excpetion (the reader generated by EntityCommand does not support GetSchemaTable() ).
I've also tried more [supposedly] friendly approaches like Entity to IDataReader(http://l2edatareaderadapter.codeplex.com/), but this one throws exceptions, has very little docs, and hasn't been touched since 2008.
Another approach I found is here (http://blogs.msdn.com/b/alexj/archive/2007/11/27/hydrating-an-entitydatareader-into-a-datatable-part-1.aspx), but does not have a working copy of the code; only snippets.
I find it hard to believe that first of all MS would not have offered this backwards-compatibility item out of the box, and second, that it would not have been created by the community either.
I'm willing to look at commercial solutions as well if any are available.
Thx!
You can convert the result into a list and use the following to convert the list to a datatable.
public DataTable ConvertToDataTable<T>(IList<T> data)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
http://social.msdn.microsoft.com/Forums/br/csharpgeneral/thread/6ffcb247-77fb-40b4-bcba-08ba377ab9db
Hope this helps
Preetam
This might not be the greatest solution, but if your scenario have only one or two table that you need to add to the DataSet, why not build them directly manually.
var result = db.YourTable; // get your Linq To Entities result.
DataSet ds = new DataSet();
DataTable tbl = new DataTable();
tbl.Columns.Add("col1", typeof(string));
tbl.Columns.Add("col2", typeof(int));
foreach (var r in result)
{
var row = tbl.NewRow();
row[0] = r.Col1;
row[1] = r.Col2;
tbl.Rows.Add(r);
}
ds.Tables.Add(tbl);
The Col1 and Col2 comes from your Linq To Entity objects, you can create all the table you need like this and return your DataSet.
This is a flexible code and should handle most of your needs:
public DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}

IQueryable to IQueryable<T>

is it possibile convert an IQueryable object to IQueryable where T is a mapped entity? (T will be a POCO class).
Thanks in advance.
Just Cast<T>() it. Assuming it is a queryable of the same type. Otherwise you could use the OfType<T>() filtering method to filter out items of a certain type.
IQueryable query = ...;
IQueryable<MyType> x = query.Cast<MyType>(); // assuming the queryable is of `MyType` objects
IQueryable<MyDerivedType> y = query.OfType<MyDerivedType>(); // filter out objects derived from `MyType` (`MyDerivedType`)
However in your case, you say that you are using Dynamic LINQ and doing a dynamic projection. Consider this completely made up query:
var query = dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select("new (SomeProperty, AnotherProperty)");
It results in a query of type IQueryable. You cannot cast this to a query of a specific type IQueryable<T> after all, what is T? What the Dynamic LINQ library does is creates a type that derives from DynamicCass. You could cast to IQueryable<DynamicClass> (query.Cast<DynamicClass>()) but you will not have access to the properties so it's moot.
Really the only nice option you have is to use dynamic to access these properties in this case.
foreach (dynamic x in query)
{
string someProperty = x.SomeProperty;
int anotherProperty = x.AnotherProperty;
// etc...
}
If you want to convert this to a query of your POCO objects, you'll have to do the conversion as a separate step but using LINQ to Objects.
IEnumerable<SomePoco> query =
dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select("new (SomeProperty, AnotherProperty)")
.Cast<DynamicObject>().AsEnumerable().Cast<dynamic>()
.Select(x => new SomePoco
{
SomeProperty = x.SomeProperty,
AnotherProperty = x.AnotherProperty,
});
If you must have an IQueryable<T>, then you should not use dynamic projections in the first place.
IQueryable<SomePoco> query =
dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select(x => new SomePoco
{
SomeProperty = x.SomeProperty,
AnotherProperty = x.AnotherProperty,
});
Seeing as how the cast is not working for LINQ to Entities, then I suppose the only option you have to get a strongly type collection of your POCO objects is to break this out into a loop.
var query = dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select("new (SomeProperty, AnotherProperty)");
var result = new List<SomePoco>();
foreach (dynamic x in query)
{
result.Add(new SomePoco
{
SomeProperty = x.SomeProperty,
AnotherProperty = x.AnotherProperty,
});
}

How to return the result set with columns with Linq

I have a function inside a class that will run a Linq to Entities query (or any type of Linq query actually), and it's gonna return 2 columns in the resultset. I would like to return an object to whoever is calling my function that will allow Intellisense to know what I have returned.
Let me explain. If I have a function like this:
public static IQueryable GetInfo(MyEntityModel oEntityModel)
{
var query =
(from t in oEntityModel.Table1
from u in t.Table2
where t.Status == true &&
u.Status == true
select new
{
t.Column1,
u.Column2
})
return query;
}
What can (should) I put instead of IQueryable so that whoever calls my GetInfo function, will get Intellisense from the resultset, and show that it has a Column1 and Column2?
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
if (linqresult.Column1 == 1)
{
foreach (var oItem in linqresult)
{
.. do stuff...
}
}
Tks
You cannot return an anonymous type from a function, they are strictly "inline" classes. When you return it, the foreach loop will only be able to interpret the result as an plain object. I guess you could use reflection to query the property names and values, however it seems much more straight forward to define a data transfer type to hold the results.
See this question, and this blog post.
So you could create a simple struct or class:
public class MyDataResult
{
public object Column1 { get; set; }
public object Column2 { get; set; }
}
Then modify your query in the function:
public static IQueryable<MyDataResult> GetInfo(MyEntityModel oEntityModel)
{
var query =
(from t in oEntityModel.Table1
from u in t.Table2
where t.Status == true &&
u.Status == true
select new MyDataResult
{
Column1 = t.Column1,
Column2 = u.Column2
})
return query;
}
Something like that should work. Note that I used "object" for the properties in MyDataResult. I don't know the types of the columns you are returning, you should use the actual types in order to get full intellisense.
You are returning a collection of anonymous types, they will be casted to objects, so when you try to iterate over them, altough they will be your objects (and they will contain your properties) at compile time they will be casted to objects:
foreach (var x in ClsLinqTeste.GetInfo(oEntityModel))
{
//x is an Object
}
You can read more about it here.
If you want to have intellisense, I suggest you create a custom class they will hold your properties and return not an anonymous type (using new {}) but object of your class (new MyClass(prop1, prop2)). You also need to change signature of your method, so it returns IQueryable<YourClass> and not just plain non-generic IQueryable.
As others have said, creating a new type to hold the two columns is usually the best option.
But if, for some reason, you don't want to do that and you are using .Net 4.0, you can use Tuple:
public static IQueryable<Tuple<Column1Type, Column2Type>>
GetInfo(MyEntityModel oEntityModel)
{
return from …
select Tuple.Create(t.Column1, u.Column2);
}
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
foreach (var oItem in linqresult)
Console.WriteLIne(oItem.Item1, oItem.Item2);
When you return your resultset AsQueryable, the app is already able to give you intellisense, however in your example, you must specify either .FirstOrDefault if you know your collection will only have a single row, or iterate over your collection to get the items from it, like so:
This is what you're doing:
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
if (linqresult.Column1 == 1)
{
..do stuff...
}
This is how you should do it:
var linqresult = ClsLinqTeste.GetInfo(oEntityModel);
foreach(var item in linqresult)
{
if (item.Column1 == 1)
{
..do stuff...
}
}
You must iterate over linqresult because when you query with link, it returns a result set, even if it just has one column. As with any collection, your data columns aren't available on the whole result set, only with individual items.
If you want to strongly typed enumerate a non-generic IEnumerable (IEnumerable.GetEnumerator() instead of IEnumerable<T>.GetEnumerator<T>()) you can use the Cast<>() extension, like so
var myquery = GetQueryable();
for (var item in myquery.Cast<MyDataType>())
{
// use item.Column1 directly and strongly typed with intellisense
}

Subsonic Single WHERE clause

Is it possible to apply a WHERE clause on a SubSonic query?
For example, I get get a single based on id...
db.Single<Storage>(id);
But how can I get a single based on a simple WHERE clause?
db.Single<Storage>(WHERE columnname == "value");
that's possible:
// Will return a Storage instance with property IsNew = true, if record does not exist
// since an object created with new never can be null
var storage1 = new Storage(1); // id = 1
var storage1 = new Storage(Storag.Columns.ColumnName, "value");
// Will return 0 if record not found (subsonic3 only)
var storage3 = (from s in Storage
where s.ColumnName == "value"
select s).SingleOrDefault();
// Will throw an exception if record not found (subsonic3 only)
var storage3 = (from s in Storage
where s.ColumnName == "value"
select s).Single();
Since db is a partial class you can extend it. Just create a new File within the same namespace (but another folder in your solution). This applies to subsonic 2 but will be similar to subsonic 3, I think.
public static partial class DB
{
public static T Single<T>(String columName, Object columnValue) where T: RecordBase<T>, new()
{
return Select().From<T>()
.Where(columnName).IsEqualTo(columnValue)
.ExecuteSingle<T>();
}
}
Thanks for the above, this was a help and eventually I simplified this to the below...
db.Single<Storage>(s => s.ColumnName == "value");

linq and object initialisation

If I have something like:
var query = from children in _data.Children
where children.ChildId == childId
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
};
return query.FirstOrDefault();
Where I want the resultant object to be my custom model.
Can I handle the custom model instantiation in a different method, which could be reused if I had multiple linq queries that all generated a custom child model?
For example,
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild([param ??]);
return query.FirstOrDefault();
This may well be impossible, I don't know, but what would the method signature be like if it is possible?
I'm only thinking reuse for when multiple linq queries contain duplicate object initialisation code.
Thanks
It really depends on what version of LINQ you're using. If you're using LINQ to SQL, I don't believe you can call arbitrary methods in the query. The query translator wouldn't know what to do with the method call
If you're using LINQ to Objects, you're absolutely fine to do it, like this:
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild(children)
return query.FirstOrDefault();
// Elsewhere
public CustomModel.MyChild CreateMyCustomChild(OtherChild child)
{
return new CustomModel.MyChild
{
ChildId = child.ChildId,
Name = child.ChildName
};
}
(Side note: I'd call the range variable in the query "child" rather than "children" as at any one time it only represents a single child.)
If you wanted you could write "select 1" or in your case "CreateMyCustomChild(children)" since "children" is containing all your info. In your case you aren't adding a lot of info to "children", so why not "select children"?
In other words, just try it out. The return type of your value will determine over which type your LINQ enumerates.
Suppose you had a method that did the transform for you.
public static class Conversions
{
public static CustomModel.MyChild ToCustomModel(this DataModel.MyChild source)
{
return new CustomModel.MyChild()
{
ChildId = source.ChildId,
Name = source.ChildName
}
}
}
You can use such a method to do the conversion of a single item.
DataModel.MyChild myResult = getResult();
CustomModel.MyChild myConvertedResult = myResult.ToCustomModel()
Such a method can also be used in a Enumerable.Select method call.
IEnumerable<DataModel.MyChild> myQueriedResults = getResult();
IEnumerable<CustomModel.MyChild> myConvertedResults =
myQueryiedResults.Select(c => c.ToCustomModel());
While you can do with expressions, I don't think it is worth the hassle. Instead I suggest you define an extension method like:
IQueryable<CustomModel.MyChild> ToModel(this IQueryable<Child> childs)
{
return childs.Select( c=>
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
}
);
}
You can then call:
return _data.Children
.Where(c=>c.ChildId == childId)
.ToModel()
.FirstOrDefault();

Resources