With Dynamic LINQ, what changes need to be done to have fields of the given class?
For example, how can the following C# query be reproduced in DLinq:
var carsPartial = cars.Select(c => new {c.year, c.name, make = new maker() {name = c.make.name} }).ToList();
I have applied the changes mentioned in this answer https://stackoverflow.com/a/1468357/288747 to allow the return type to be the calling type rather than an anonymous type.
With the class definition is as follows (if it helps):
class car
{
public int vid;
public int odo;
public int year;
public string name;
public maker make;
}
class maker
{
public string name;
public int firstYear;
}
The following doesn't work (but I think is close, but still doesn't work as I don't have the changes necessary to the dynamic linq library, which is what I need):
var carsPartial = cars.Select("new(name, year, new maker() {name = make.name})").ToList();
But it fails at the new maker() { (as expected).
I'm sure I need to change the DynamicLibrary.cs to get this working and could do with some direction on how to alter it to achieve this.
UPDATE: I have turned my answer into a little bit more extensive blog post.
I have not really ever used Dynamic Linq library, but I have taken a look at the DynamicLibrary.cs code and the change to support generating type classes provided in another stackoverflow question you provided link to in your question. Analyzing them all, it seems that the nested new-s should work out of the box in your configuration.
However, it seems your query is not the correct Dynamic Linq's language query. Note, that the query string for DLinq is not equivalent to C# and has its own grammar.
The query should read out, I believe, the following:
var carsPartial = cars.Select("new(name, year, new maker(make.name as name) as make)").ToList();
EDIT:
Rereading this stackoverflow question more carefully, I realizes, that it actually does not extend the Dynamic Linq's language with the possibility for creating new strong-typed classes. They just put the result to the class specified as a generic parameter of Select() instead of specifying it in the query string.
To obtain what you need you will need to revert their changes (get generic DLinq) and apply my changes, I have just verified to work:
Locate the ParseNew method of ExpressionParser class and change it to the following:
Expression ParseNew() {
NextToken();
bool anonymous = true;
Type class_type = null;
if (token.id == TokenId.Identifier)
{
anonymous = false;
StringBuilder full_type_name = new StringBuilder(GetIdentifier());
NextToken();
while (token.id == TokenId.Dot)
{
NextToken();
ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
full_type_name.Append(".");
full_type_name.Append(GetIdentifier());
NextToken();
}
class_type = Type.GetType(full_type_name.ToString(), false);
if (class_type == null)
throw ParseError(Res.TypeNotFound, full_type_name.ToString());
}
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
NextToken();
List<DynamicProperty> properties = new List<DynamicProperty>();
List<Expression> expressions = new List<Expression>();
while (true) {
int exprPos = token.pos;
Expression expr = ParseExpression();
string propName;
if (TokenIdentifierIs("as")) {
NextToken();
propName = GetIdentifier();
NextToken();
}
else {
MemberExpression me = expr as MemberExpression;
if (me == null) throw ParseError(exprPos, Res.MissingAsClause);
propName = me.Member.Name;
}
expressions.Add(expr);
properties.Add(new DynamicProperty(propName, expr.Type));
if (token.id != TokenId.Comma) break;
NextToken();
}
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
NextToken();
Type type = anonymous ? DynamicExpression.CreateClass(properties) : class_type;
MemberBinding[] bindings = new MemberBinding[properties.Count];
for (int i = 0; i < bindings.Length; i++)
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
return Expression.MemberInit(Expression.New(type), bindings);
}
Then, find the class Res and add the following error message:
public const string TypeNotFound = "Type {0} not found";
Et voilĂ , you will be able to construct queries like:
var carsPartial = cars.Select("new(name, year, (new your_namespace.maker(make.name as name)) as make)").ToList();
Make sure, you include the full type name including the whole namespace+class path.
To explain my change, it just checks if there is some identifier between new and opening parenthesis (see the added "if" at the begging). If so we parse full dot-separated class name and try to get its Type through Type.GetType instead of constructing own class in case of anonymous news.
If I've understood you correctly you want to make a plain anonymous class that contains fields from both class car and class maker. If it's the case you can just provide new names in that class, something like the following:
var carsPartial = cars.Select(c => new { year = c.year, name = c.name, make_name = c.make.name });
Or even provide names only to conflicting fields:
var carsPartial = cars.Select(c => new { c.year, c.name, make_name = c.make.name });
Related
I've been looking into this for quite some time now and cannot figure out a resolution. I Originally Tried formatting A dynamic linq Statement as you can see here in this post
I declared a class:
public class DynamicHelper
{
public string FormattedLink(string DisplayText, string ID)
{
return "" + DisplayText + "";
}
public string FormattedLink(string DisplayText, int ID)
{
return "" + DisplayText + "";
}
}
After I inserted a new type in DynamicLinq into the predefinedTypes
,typeof(DynamicHelper) //around line 635
I have a program which is attempting to Invoke the FormattedLink inside of a dynamic linq select:
using (var Model = new MK3Entities())
{
DynamicHelper Dh = new DynamicHelper();
var TOrigin = (Model.Titles.Where("ID > 19632")
.Select("new(ID, #0.FormattedLink(ExtTitleID, ID) as ExtTitleID )", Dh) as System.Collections.IEnumerable)
.Cast<dynamic>().Take(10).ToList();
Console.ReadKey();
}
When I execute this program I get a runtime exception "LINQ to Entities does not recognize the method 'System.String FormattedLink(System.String, Int32)' method, and this method cannot be translated into a store expression."
Any Ideas on how to fix this... I just need simple formatting from Dynamic Select.
The error message is pretty self explanatory. The database doesn't know how to translate that method into SQL. You need to fetch the information that the method needs in your database query and then call that function on the results, rather than in the query.
I'm not sure why you need it to be dynamic, it seems the solution you present is very overly complicated. I would write it as:
using (var Model = new MK3Entities())
{
DynamicHelper Dh = new DynamicHelper();
var TOrigin = Model.Titles
.Where("ID > 19632")
.Select(t => new { ID = t.ID, ExtTitleID = t.ExtTitleId })
.Take(10)
.ToList() // Execute SQL Statement
.Select(t => new {ID = t.ID, Link = nh.FormattedLink(ExtTitleID, ID)})
.ToList();
Console.ReadKey();
}
I'm returning an List<anonymous'1> object instead of a dynamic object (because I've never had the need for dynamic objects) so you can adjust it accordingly.
I just solved similiar problem few hours back.
YOu need ToList() that works with Dynamic linq. Check out this thread: Can't find property or field on a dynamic object
Just copy paste those to your project, and later:
var TOrigin = (Model.Titles.Where("ID > 19632")
.ToAnonymousList()
.Select("new(ID, #0.FormattedLink(ExtTitleID, ID) as
ExtTitleID )", Dh) as System.Collections.IEnumerable);
I'm having trouble figuring out what I'm doing wrong here. I have some LINQ that returns an IQuery object, and later in the code, I'm attempting to list out the attributes returned. This is best explained by this abbreviated code (the actual LINQ is a lot more complex and involves joins - the LINQ itself works fine):
public IQueryable<Object> FindAll()
{
var query = from user in _db.Users
select new
{
id = user.Id,
is_active = user.IsActive,
email = user.Email,
dob = user.Dob,
user.user_type,
};
return query;
}
Elsewhere in the code I have:
query.ConvertToCsv();
Although I have attempted to insert a .ToList() in that call as well.
The ConvertToCsv has:
public static string ConvertToCSV<TSource>(this IEnumerable<TSource> source)
{
StringBuilder sb = new StringBuilder();
var properties = typeof(TSource).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
var enumerable = source as IList<TSource> ?? source.ToList();
if (!enumerable.Any()) return "";
string headerString = "";
foreach (var prop in properties)
{
headerString += (headerString.Length > 0 ? "," : "") + prop.Name;
}
sb.AppendLine(headerString);
foreach (TSource item in enumerable)
{
string line = string.Join(",", properties.Select(p => p.GetValue(item).ToCsvValue()).ToArray());
sb.AppendLine(line);
}
return sb.ToString();
}
Note I have also tried to pull out the property names with this code:
PropertyInfo[] pi = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var properties = pi.OrderBy(x => x.MetadataToken);
foreach (PropertyInfo p in properties)
{ etc etc }
In all cases, the property or field list returns an empty list, and as such, I can't iterate through the object to spit out a header row or data rows. Tracing through all the code and inspecting the variables indicates that everything is fine until I get to the GetProperties/GetFields line and the code fails.
What rookie mistake am I making? Should I be replacing <Object> with something else?
To pass an anonymous type, or a collection that contains anonymous
types, as an argument to a method, you can declare the parameter as
type object. However, doing this defeats the purpose of strong typing.
If you must store query results or pass them outside the method
boundary, consider using an ordinary named struct or class instead of
an anonymous type.
by Anonymous Types (C# Programming Guide)
Create your own class and change method declaration to be IQueryable<MyClass> instead of object
Did you consider doing something like: db.Users.Select(u => new UserDto() { Id = user.Id, Name = ..., where UserDto is dedicated class that has all the properties you'll need in the future? I think you lose information about properties when you cast from anonymous class to an Object. Although, I never tried to obtain member info from anonymous class
I am trying to cast an array of anonymous objects, where each object looks like this:
new {type="internal",title="Linktitle",target="_blank",link="http://www.google.se"}
I have declared a Class "Link", to which the anonymous objects should be casted
class Link{
public string type {get;set;}
public string target {get;set;}
public string title {get;set;}
public string link {get;set;}
}
Now i am trying to cast the objects, like this
List<Link> links = Model.relatedLinks.Select(l => new Link{type=l.type,target=l.target,title=l.title,link=l.link}).ToList();
Then i get the error
Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type
I found this page on how to cast anonymous objects, but im doing it the same way. Or have i missed something?
If relatedLinks itself is a dynamic value, you've got two problems:
The lambda expression part as already reported
Extension methods can't be called on dynamic values (as extension methods). This affects both the Select and ToList methods.
You can work round the first by casting the lambda expression. You can work round the second by calling Enumerable.Select directly:
// Note: don't use var here. We need the implicit conversion from
// dynamic
IEnumerable<Link> query = Enumerable.Select(Model.relatedLinks,
(Func<dynamic, Link>) (l => new Link {
type = l.type,
target = l.target,
title = l.title,
link = l.link } );
var links = query.ToList();
Or for the sake of formatting:
Func<dynamic, Link> projection = l => new Link {
type = l.type,
target = l.target,
title = l.title,
link = l.link };
IEnumerable<Link> query = Enumerable.Select(Model.relatedLinks, projection);
var links = query.ToList();
If Model.relatedLinks is already IEnumerable<dynamic> (or something similar) then you can call Select as an extension method instead - but you still need to have a strongly-typed delegate. So for example, the latter version would become:
Func<dynamic, Link> projection = l => new Link {
type = l.type,
target = l.target,
title = l.title,
link = l.link };
IEnumerable<Link> query = Model.relatedLinks.Select(projection);
var links = query.ToList();
When running my application I have to write to screen raw query used.
Is any method/extension method available, to get from this:
IQueryable alldata = hr.GetCollection"EventsReceiver").AsQueryable().Where(q => q.UserId == "123");
something similar to:
db.EventsReceiver.find({ "userid" : "123" });
From FluentMongo v1.2.0.0, there's no public way to expose the query (so sad). Here's a dirty extension method to get at it.
BUT since this is using reflection to get at non-public members don't expect that it will necessarily work in the future.
public static class MongoQueryableExtensions
{
public static BsonDocument GetMongoQuery<T>(this IQueryable<T> query)
{
if(query == null) throw new ArgumentNullException("query");
Assembly fluentMongoAssembly = typeof(FluentMongo.Linq.MongoCollectionExtensions).Assembly;
Type mongoQueryableType = fluentMongoAssembly.GetType("FluentMongo.Linq.IMongoQueryable");
BsonDocument queryDocument = null;
if(mongoQueryableType.IsAssignableFrom(query.GetType()))
{
MethodInfo m = mongoQueryableType.GetMethod("GetQueryObject");
object queryObject = m.Invoke(query, null);
PropertyInfo queryProperty = fluentMongoAssembly.GetType("FluentMongo.Linq.MongoQueryObject").GetProperty("Query");
queryDocument = (BsonDocument)queryProperty.GetValue(queryObject, null);
}
return queryDocument;
}
}
For anyone having same question I'll repost here Craig's answer on github:
var queryObject = ((IMongoQueryable)alldata).GetQueryObject();
this should give you back the object used to generate the query.
I have seen the reverse of this question quite a few times, but have not seen how to do what I would like.
Suppose I have the following code:
var myNewData = from t in someOtherData
select new
{
fieldName = t.Whatever,
fieldName2 = t.SomeOtherWhatever
};
If I wish to data bind to this class, my column definition would have to include hard-coded strings like "fieldName" and "fieldName2".
Is there any way to call reflection or something else so that I can do something equivelent to the code below (I know the code below is not valid, but am looking for a valid solution).
string columnName = GetPropertyName(myNewData[0].fieldName);
My goal is that if the variable name changes on the anonymous class, a compile-time error would come up until all references were fixed, unlike the current data binding which relies on strings that are not checked until runtime.
Any help would be appreciated.
string columnName = GetPropertyName(() => myNewData[0].fieldName);
// ...
public static string GetPropertyName<T>(Expression<Func<T>> expr)
{
// error checking etc removed for brevity
MemberExpression body = (MemberExpression)expr.Body;
return body.Member.Name;
}
You get your property names like this:
using System.Reflection;
var myNewData = from t in someOtherData
select new
{
fieldName = t.Whatever,
fieldName2 = t.SomeOtherWhatever
};
foreach (PropertyInfo pinfo in myNewData.FirstOrDefault()
.GetType().GetProperties())
{
string name = pinfo.Name;
}
// or if you need all strings in a list just use:
List<string> propertyNames = myNewData.FirstOrDefault()
.GetType().GetProperties().Select(x => x.Name).ToList();