Discussion:
LINQ to Objects Abfrage dynamisch zur Laufzeit generieren
(zu alt für eine Antwort)
janosch
2009-09-30 17:38:00 UTC
Permalink
Hallo,
das Beispiel aus http://msdn.microsoft.com/de-de/library/bb882637.aspx
habe ich wie folgt abgeändert:

Type globObj = ssb.ssbGlobObj.Typ; //enthalten Angaben zum
Type der zur Laufzeit verwendeten Klasse
Dictionary<int, StayReference> dictStRF = qRF.DictSTRF; //
enthält die Objekte vom Typ StayReference und soll abgefragt werden

IQueryable<StayReference> queryableData =
dictStRF.Values.ToList().AsQueryable<StayReference>();

ParameterExpression pe = Expression.Parameter(globObj,
"stayRef");

Expression left = Expression.Property(pe,
globObj.GetProperty("CaseID", typeof(int)));
Expression right = Expression.Constant(13554, typeof
(int));
Expression e1 = Expression.Equal(left, right);

MethodCallExpression whereCallExpression = Expression.Call
(
typeof(Queryable),
"Where",
new Type[] { queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<StayReference, bool>>(e1, new
ParameterExpression[] { pe }));

IQueryable<StayReference> results =
queryableData.Provider.CreateQuery<StayReference>
(whereCallExpression);

foreach (StayReference company in results)
listBox1.Items.Add(company.CaseID.ToString());

Soweit, so gut. Wie schaffe ich es jetzt aber, die Anfrage auf eine
mir vorher unbekannte Klasse zu starten? Also so, dass nicht mehr
überall StayRefence drin steht, sondern ich das erst zur Laufzeit
übergebe?
Frank Dzaebel
2009-09-30 17:50:52 UTC
Permalink
Hallo Janosch,

der volle Vor- und Zuname als Username wird hier gern gesehen.
Post by janosch
das Beispiel aus http://msdn.microsoft.com/de-de/library/bb882637.aspx
habe ich wie folgt abgeändert [...]
Soweit, so gut. Wie schaffe ich es jetzt aber, die Anfrage auf eine
mir vorher unbekannte Klasse zu starten?
Zum Beispiel generisch mit Interface formulieren:

private void Form1_Load(object sender, EventArgs e)
{
Test<StayReference>();
}

void Test<T>() where T : ICaseable
{
Type globObj = ssb.ssbGlobObj.Typ; //enthalten Angaben zum
//Type der zur Laufzeit verwendeten Klasse
Dictionary<int, T> dictStRF = qRF.DictSTRF; //
// enthält die Objekte vom Typ StayReference und soll abgefragt
werden

IQueryable<T> queryableData =
dictStRF.Values.ToList().AsQueryable<T>();

ParameterExpression pe = Expression.Parameter(globObj,
"stayRef");

Expression left = Expression.Property(pe,
globObj.GetProperty("CaseID", typeof(int)));
Expression right = Expression.Constant(13554, typeof(int));
Expression e1 = Expression.Equal(left, right);
MethodCallExpression whereCallExpression = Expression.Call
(
typeof(Queryable), "Where",
new Type[] { queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<T, bool>>(e1,
new ParameterExpression[] { pe }));

IQueryable<T> results =
queryableData.Provider.CreateQuery<T>
(whereCallExpression);

foreach (T result in results)
listBox1.Items.Add(result.CaseID.ToString());
}

interface ICaseable
{
/// <summary>Fall-Nummer</summary>
int CaseID { get; set; }
}

class StayReference : ICaseable
{
public int CaseID { get; set; }
}


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
janosch
2009-09-30 18:40:26 UTC
Permalink
Post by Frank Dzaebel
Hallo Janosch,
der volle Vor- und Zuname als Username wird hier gern gesehen.
das Beispiel aushttp://msdn.microsoft.com/de-de/library/bb882637.aspx
habe ich wie folgt abgeändert [...]
Soweit, so gut. Wie schaffe ich es jetzt aber, die Anfrage auf eine
mir vorher unbekannte Klasse zu starten?
private void Form1_Load(object sender, EventArgs e)
{
  Test<StayReference>();
}
void Test<T>() where T : ICaseable
{
  Type globObj = ssb.ssbGlobObj.Typ; //enthalten Angaben zum
         //Type der zur Laufzeit verwendeten Klasse
  Dictionary<int, T> dictStRF = qRF.DictSTRF; //
         // enthält die Objekte vom Typ StayReference und soll abgefragt
werden
  IQueryable<T> queryableData =
    dictStRF.Values.ToList().AsQueryable<T>();
  ParameterExpression pe = Expression.Parameter(globObj,
    "stayRef");
  Expression left = Expression.Property(pe,
    globObj.GetProperty("CaseID", typeof(int)));
  Expression right = Expression.Constant(13554, typeof(int));
  Expression e1 = Expression.Equal(left, right);
  MethodCallExpression whereCallExpression = Expression.Call
    (
      typeof(Queryable), "Where",
      new Type[] { queryableData.ElementType },
      queryableData.Expression,
      Expression.Lambda<Func<T, bool>>(e1,
        new ParameterExpression[] { pe }));
  IQueryable<T> results =
    queryableData.Provider.CreateQuery<T>
    (whereCallExpression);
  foreach (T result in results)
    listBox1.Items.Add(result.CaseID.ToString());
}
interface ICaseable
{
  /// <summary>Fall-Nummer</summary>
  int CaseID { get; set; }
}
class StayReference : ICaseable
{
  public int CaseID { get; set; }
}
ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]http://Dzaebel.NET
Hallo Frank,

vielen Dank für die Antwort.
Ein Problem gibt es noch dabei.
qRF.DictSTRF ist vom Typ StayReference und kann somit nicht
umgewandelt werden. Oder habe ich etwas übersehen?

Gruß Jens
Frank Dzaebel
2009-09-30 19:20:58 UTC
Permalink
Hallo Janosch,
Post by janosch
Ein Problem gibt es noch dabei.
qRF.DictSTRF ist vom Typ StayReference und kann somit nicht
umgewandelt werden. Oder habe ich etwas übersehen?
Das ist simpel:
Dictionary<int, T> dictStRF = qRF.DictSTRF as Dictionary<int, T>;
______

Ein paar Links noch, die ich diesbzgl. schon gepostet habe:

[LinQ to SQL dynamisch zusammensetzen]
http://groups.google.com/group/microsoft.public.de.german.entwickler.dotnet.datenbank/msg/c6001958be1d4241


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
Jens Meyer
2009-09-30 19:54:21 UTC
Permalink
Post by Frank Dzaebel
Hallo Janosch,
Post by janosch
Ein Problem gibt es noch dabei.
qRF.DictSTRF ist vom Typ StayReference und kann somit nicht
umgewandelt werden. Oder habe ich etwas übersehen?
    Dictionary<int, T> dictStRF = qRF.DictSTRF as Dictionary<int, T>;
______
[LinQ to SQL dynamisch zusammensetzen]http://groups.google.com/group/microsoft.public.de.german.entwickler....
ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]http://Dzaebel.NET
Ja, damit klappt es. Besten Dank!

Die Links schau ich mir morgen genauer an (auch hierfür nochmals
Danke).

Gruß Jens
Jens Meyer
2009-10-01 07:24:07 UTC
Permalink
Post by Frank Dzaebel
  foreach (T result in results)
    listBox1.Items.Add(result.CaseID.ToString());
Guten Morgen,
bin noch auf ein Problem gestossen. Hatte ich gestern in meinem Code
übersehen. Wie schaffe ich es die CaseID auszulesen, ohne das
da .CaseID steht, sondern ganz dynamisch mit "CaseID"?
Gruß Jens
Jens Meyer
2009-10-01 13:55:55 UTC
Permalink
Post by Jens Meyer
Post by Frank Dzaebel
  foreach (T result in results)
    listBox1.Items.Add(result.CaseID.ToString());
Guten Morgen,
bin noch auf ein Problem gestossen. Hatte ich gestern in meinem Code
übersehen. Wie schaffe ich es die CaseID auszulesen, ohne das
da .CaseID steht, sondern ganz dynamisch mit "CaseID"?
Gruß Jens
Ok, habe das Problem mit einem DataGridView gelöst.

Mit den, von Elmar verlinkten, DynamicQuery hab ich mich auch näher
beschäftigt. Bin aber nicht so recht schlau geworden, wie man sich
Listenobjekte in einem Objekt anzeigen lassen kann.
Folgendes Codebeispiel:

var test = listStayRef.AsQueryable().Where("CaseID >= @0 and and
Transfers.Any(Target = \"ABS\")", 13554).Select("New(CaseID)");

Bei Transfers handelt es sich um eine Liste vom Typ Transfer. Das hat
unter anderem das Property "Target". Die Abfrage funktioniert, aber
wie kann ich mir noch Transfers.Target anzeigen lassen? Mit Select("New
(CaseID, Transfers.Target)"); klappt es nicht. Jemand eine Idee?

Gruß Jens
Frank Dzaebel
2009-10-01 17:13:05 UTC
Permalink
Hallo Jens,
Post by Jens Meyer
bin noch auf ein Problem gestossen. Hatte ich gestern in meinem Code
übersehen. Wie schaffe ich es die CaseID auszulesen, ohne das
da .CaseID steht, sondern ganz dynamisch mit "CaseID"?
Das geht wieder auf mehrere Arten, aber wenn Du alles komplett
dynamisch machen willst, würde ich eher meinen Link:
"Part 8: Executing Custom SQL Expressions" benutzen.

Hier auch noch ein Beispiel:

[Linq und dynamisches SQL in C#]
http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.de.german.entwickler.dotnet.csharp&mid=17046ba3-f44b-4d9e-a3fc-c49f7c953a41

Aber gut, Du hast ja jetzt Dein Problem mit dem
DataGridView lösen können. Schön.


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
Jens Meyer
2009-10-03 14:35:03 UTC
Permalink
Post by Elmar Boye
Hallo Jens,
Post by Jens Meyer
bin noch auf ein Problem gestossen. Hatte ich gestern in meinem Code
übersehen. Wie schaffe ich es die CaseID auszulesen, ohne das
da .CaseID steht, sondern ganz dynamisch mit "CaseID"?
Das geht wieder auf mehrere Arten, aber wenn Du alles komplett
"Part 8: Executing Custom SQL Expressions" benutzen.
[Linq und dynamisches SQL in C#]http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg...
Aber gut, Du hast ja jetzt Dein Problem mit dem
DataGridView lösen können. Schön.
ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]http://Dzaebel.NET
Hallo Frank,

das mit dem ExecuteQuery würde gut passen, aber leider führe ich die
Abfragen auf ein Objekt durch (also LINQ 2 Objects). Und dort gibt es
scheinbar kein ExecuteQuery. Also könnte ich es höchstens nur noch mit
den LINQ-Expressions probieren. Oder gibts noch weitere Möglichkeiten?

Gruß Jens
Frank Dzaebel
2009-10-03 16:05:32 UTC
Permalink
Hallo Jens,
Post by Jens Meyer
Post by Jens Meyer
bin noch auf ein Problem gestossen. Hatte ich gestern in meinem Code
übersehen. Wie schaffe ich es die CaseID auszulesen, ohne das
da .CaseID steht, sondern ganz dynamisch mit "CaseID"?
[...]
das mit dem ExecuteQuery würde gut passen, aber leider führe ich die
Abfragen auf ein Objekt durch (also LINQ 2 Objects). Und dort gibt es
scheinbar kein ExecuteQuery. Also könnte ich es höchstens nur noch mit
den LINQ-Expressions probieren.
Wenn Du Dir mal die Linq-Samples herunterlädst ...

[C# Samples for Visual Studio 2008 - Release: CSharp Samples January 2008]
http://code.msdn.microsoft.com/csharpsamples/Release/ProjectReleases.aspx?ReleaseId=8

... und Dir das Reflector-Beispiel anschaust, siehst Du
dass es umfangreiche Möglichkeiten dafür bei "Linq to Objects" gibt.
____________

In Deinem Beispiel suchst Du nach "CaseID" als
dynamische Eingabe. Ich empfahl zunächst über
saubere Interfaces zu gehen. Es ist natürlich auch
immer Reflection möglich, also etwa:

void Test<T>(string propertyName) // mit: "CaseID" aufrufen
{
Type globObj = ssb.ssbGlobObj.Typ; //enthalten Angaben zum
// Type der zur Laufzeit verwendeten Klasse
Dictionary<int, T> dictStRF = qRF.DictSTRF as Dictionary<int, T>; //
// enthält die Objekte vom Typ StayReference und soll abgefragt
werden

IQueryable<T> queryableData =
dictStRF.Values.ToList().AsQueryable<T>();

ParameterExpression pe = Expression.Parameter(globObj,
"stayRef");

Expression left = Expression.Property(pe,
globObj.GetProperty(propertyName, typeof(int)));
Expression right = Expression.Constant(13554, typeof(int));
Expression e1 = Expression.Equal(left, right);
MethodCallExpression whereCallExpression = Expression.Call
(
typeof(Queryable), "Where",
new Type[] { queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<T, bool>>(e1,
new ParameterExpression[] { pe }));

IQueryable<T> results =
queryableData.Provider.CreateQuery<T>
(whereCallExpression);

foreach (T result in results)
listBox1.Items.Add(
typeof(T).GetProperty(propertyName).GetValue(
result, null).ToString()); // <<==== !!
}
__________
Post by Jens Meyer
Oder gibts noch weitere Möglichkeiten?
ja, Reflection kannst Du genauso in Deinem Linq to Objects -
Beispiel anwenden, wenn der Type erst zur Laufzeit bekannt ist:

var res = queryableData.Where(c=>c.GetType().GetProperty(
propertyName).GetValue(c,null).Equals(13554));

[Enumerable.Where(TSource)-Methode (IEnumerable(TSource), Func(TSource,
Boolean)) (System.Linq)]
http://msdn.microsoft.com/de-de/library/bb534803.aspx


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
Jens Meyer
2009-10-12 16:30:20 UTC
Permalink
Post by Frank Dzaebel
Post by Jens Meyer
Oder gibts noch weitere Möglichkeiten?
ja, Reflection kannst Du genauso in Deinem Linq to Objects -
    var res = queryableData.Where(c=>c.GetType().GetProperty(
          propertyName).GetValue(c,null).Equals(13554));
[Enumerable.Where(TSource)-Methode (IEnumerable(TSource), Func(TSource,
Boolean)) (System.Linq)]http://msdn.microsoft.com/de-de/library/bb534803.aspx
ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]http://Dzaebel.NET
Hallo Frank,

ist es auch möglich ein SelectMany mit Hilfe von Reflection bei Linq
to Objects zu verwenden?

Also so in etwa in der Form?
var rest = queryableData.Where(c => c.GetType().GetProperty
(propertyName).GetValue(c, null) != null)
.SelectMany(c=>c.GetType().GetProperty
(propertyName).GetValue(c,null));
//propertyName für eine Sequenz von Objekten

Die Fehlermeldung dazu verstehe ich nicht:
Die Typargumente der System.Linq.Enumerable.SelectMany<TSource,TResult>
(System.Collections.Generic.IEnumerable<TSource>,
System.Func<TSource,int,System.Collections.Generic.IEnumerable<TResult>>)-
Methode können nicht per Rückschluss aus der Syntax abgeleitet werden.
Geben Sie die Typargumente explizit an.

Gruß Jens
Frank Dzaebel
2009-10-13 19:06:45 UTC
Permalink
Hallo Jens,
Post by Jens Meyer
ist es auch möglich ein SelectMany mit Hilfe von
Reflection bei Linq to Objects zu verwenden?
Also so in etwa in der Form?
var rest = queryableData.Where(c => c.GetType().GetProperty
(propertyName).GetValue(c, null) != null)
.SelectMany(c=>
c.GetType().GetProperty(propertyName).GetValue(c,null)
);
Die vorletzte Zeile sollte eine "Liste" ergeben, dann wäre
sie zumindest syntaktisch korrekt.
Aber in diesem Fall hast Du ja nicht mehrere Listen, die
zusammengeführt werden sollen, deswegen
ist SelectMany ggf. gar nicht das, was Du willst, sondern Select.
Dennoch, um mal was syntaktisch korrektes aufzuschreiben:

var rest = queryableData.Where(c => c.GetType().
GetProperty(propertyName).GetValue(c, null) != null);
var rest2 = rest.SelectMany(c => Eigenschaft(c, propertyName));
rest2.ToList(); // erwirkt Auswertung, für BreakPoint in Eigenschaft(...)
__________

private object[] Eigenschaft(object c, string propertyName)
{
object[] objekte = new object[] {c.GetType().
GetProperty(propertyName).GetValue(c,null)};
return objekte;
}


Ich habe es bewusst mal getrennt und ausführlich aufgeschrieben,
da man so bessere Debugging-Möglichkeiten hat. Du kannst es
natürlich auch hintereinander schreiben.
Durch SelectMany kann man ja zum Beispiel *mehrere Listen* in eine
projezieren:

[Enumerable.SelectMany(TSource, TResult)-Methode (IEnumerable(TSource),
Func(TSource, IEnumerable(TResult))) (System.Linq)]
http://msdn.microsoft.com/de-de/library/bb534336.aspx


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
Jens Meyer
2009-10-14 12:40:14 UTC
Permalink
Post by Elmar Boye
Hallo Jens,
Post by Jens Meyer
ist es auch möglich ein SelectMany mit Hilfe von
Reflection bei Linq to Objects zu verwenden?
Also so in etwa in der Form?
      var rest = queryableData.Where(c => c.GetType().GetProperty
          (propertyName).GetValue(c, null) != null)
          .SelectMany(c=>
                c.GetType().GetProperty(propertyName).GetValue(c,null)
         );
Die vorletzte Zeile sollte eine "Liste" ergeben, dann wäre
sie zumindest syntaktisch korrekt.
Aber in diesem Fall hast Du ja nicht mehrere Listen, die
zusammengeführt werden sollen, deswegen
ist SelectMany ggf. gar nicht das, was Du willst, sondern Select.
Hallo,

beim Codebeispiel hab ich leider einen Fehler gemacht. Was natürlich
für die Lösung irreführend ist. Hier das "richtige" (syntaktisch
unkorrekte) Beispiel:

var rest = listCustomer.Where(c => c.GetType().GetProperty
("Name").GetValue(c, null) != null)
.SelectMany(c=>c.GetType().GetProperty("Orders").GetValue
(c,null) );

Dazu ein Beispiel Klasse:
public class Customer { public string Name; ... ; public Order[]
Orders; }
public class Order { public int OrderID; public int ProductID; ... ; }

Die Objekte werden dann erstellt und landen in List<Customer>
listCustomer.
Und nun möchte ich mir alle "Orders" von allen "Customers" in einer
Liste auswählen lassen.
Typsicher geschrieben: var orders = listCustomer.Where(c => c.Name !=
null).SelectMany(c => c.Orders);

Wie schaff ich das?

Gruß Jens
Frank Dzaebel
2009-10-14 17:49:11 UTC
Permalink
Hallo Jens,
Post by Jens Meyer
beim Codebeispiel hab ich leider einen Fehler gemacht.
kein Problem.
Post by Jens Meyer
[...] Wie schaff ich das?
a) zunächst sind das dann keine Eigenschaften, sondern
Felder, so, wie Du die Klassen definiert hast.

b) Solltest Du, wie ich bereits vorhin gesagt habe,
im zweiten Teil eben eine Liste zurückgeben.
Also zum Beispiel hier als (Order[]) oder
(IEnumerable<Order>) casten:

var rest = listCustomer.Where(c => c.GetType().
GetField("Name").GetValue(c) != null).SelectMany(
a => (Order[])a.GetType().GetField("Orders").GetValue(a));


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
Jens Meyer
2009-10-14 19:34:21 UTC
Permalink
Post by Frank Dzaebel
b) Solltest Du, wie ich bereits vorhin gesagt habe,
    im zweiten Teil eben eine Liste zurückgeben.
    Also zum Beispiel hier als (Order[]) oder
    var rest = listCustomer.Where(c => c.GetType().
        GetField("Name").GetValue(c) != null).SelectMany(
        a => (Order[])a.GetType().GetField("Orders").GetValue(a));
Nun steht mir der Typcast (also Order[] bzw. IEnumerable<Order>) zur
Laufzeit nicht zur Verfügung. Dafür steht mir aber der Type von
"Order" (mittels Reflection) in einem Objekt zur Verfügung. Kann ich
damit irgendwie den Ausdruck hier " a.GetType().GetField
("Orders").GetValue(a)) " casten?
Gruß Jens
Frank Dzaebel
2009-10-15 03:14:09 UTC
Permalink
Hallo Jens,
Post by Jens Meyer
[...] muss eine Liste sein [...]
var rest = listCustomer.Where(c => c.GetType().
GetField("Name").GetValue(c) != null).SelectMany(
a => (Order[])a.GetType().GetField("Orders").GetValue(a));
Nun steht mir der Typcast (also Order[] bzw. IEnumerable<Order>)
zur Laufzeit nicht zur Verfügung.
Du kannst ggf. auch in (object[]) casten.

BTW: Allgemein würde ansonsten gelten, *wenn* Du
den Typ kennst, mach das so typsicher wie möglich,
also etwa (IEnumerable<DeinTyp>).


ciao Frank
--
Dipl.Inf. Frank Dzaebel [MCP/MVP C#]
http://Dzaebel.NET
Jens Meyer
2009-10-15 10:25:27 UTC
Permalink
Post by Frank Dzaebel
Du kannst ggf. auch in (object[]) casten.
BTW: Allgemein würde ansonsten gelten, *wenn* Du
den Typ kennst, mach das so typsicher wie möglich,
also etwa (IEnumerable<DeinTyp>).
Hallo Frank,

das casten mit (object[]) klappt in meinem Fall leider nicht
(Fehlermeldung: Das Objekt des Typs "System.Collections.Generic.List`1
[TypSoUndSo]" kann nicht in Typ "System.Object[]" umgewandelt werden.)

Ich habe es nun folgerdermaßen gelöst:

var res = test.Where(cd => cd.GetType().
GetProperty(propertyName).GetValue(cd, null) != null)
.Select(cd => cd.GetType().GetProperty(propertyName2).
GetValue(cd, null));

List<object> listObjects = new List<object>();
foreach (object objs in res)
foreach (object obj in (IEnumerable)objs)
listObjects.Add(obj);

Ohne das ich nun den Type casten muß, kriege ich alle Objekte in eine
Liste. Einzige Bedingung: "objs" muß eine aufzählbare Menge von
Objekten darstellen.
Ich danke dir, ohne deine Hilfe wäre ich auf die Lösung nicht
gekommen.

Gruß Jens

Elmar Boye
2009-09-30 19:12:21 UTC
Permalink
Hallo Jens,
Post by janosch
das Beispiel aus http://msdn.microsoft.com/de-de/library/bb882637.aspx
Schau Dir mal DynamicQuery an:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Gruß Elmar
Loading...