I fail to get GridFSFileInfo by ObjectID, but succeed by filename,
and the error message is:
Unable to determine the serialization information for x=>x.Id
string objectID = ObjectIDTxt.Text.Trim();
GridFSBucketOptions bucketOptions = new GridFSBucketOptions();
bucketOptions.BucketName = "myBucket";
ObjectId gridfsObjectID = new ObjectId(objectID);
//by filename will succeed
//var filter = Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, "myfilename.pdf");
//by ObjectID will fail
var filter = Builders<GridFSFileInfo>.Filter.Eq(x=>x.Id,gridfsObjectID);
var findOptions = new GridFSFindOptions();
findOptions.Limit = 1;
var myBucket = new GridFSBucket(_database, bucketOptions);
using (var taskOfCursor = Task.Run(() => myBucket.FindAsync(filter, findOptions)))
{
var taskOfList = Task.Run(() => taskOfCursor.Result.ToListAsync());
GridFSFileInfo fileInfo = taskOfList.Result.FirstOrDefault();
if (fileInfo != null)
{
FileNameLbl.Text = fileInfo.Filename;
}
}
I'm using Mongodb 3.0,c# driver 2.1,wird tiger storage engine.
Forgive me about the use of many 'Task.Run()',because for some reason I need to sync call async mongo methods.
Any suggestions will be appreciated...
thx
Unable to determine the serialization information for x=>x.Id
As the error suggests, you can't use x.Id inside your query in this way. The lambda expression provided is used to retrieve the name of the property and it doesn't understand what x.Id is.
You may try this:
var filter = Builders<GridFSFileInfo>.Filter.Eq("_id", gridfsObjectID);
which uses this overload of the Eq method and performs the implicit conversion from String to FieldDefinition.
Expressions seem a bit puzzling for me as well, but you may find more information related to Expression in the answers to this question: Why would you use Expression> rather than Func?
You can add the lambda syntax directly in Find method:
myBucket.FindAsync(x => x.Id == new MongoDB.Bson.ObjectId(objectID), findOptions)
Related
I am currently having a text index in my data. I am performing a regex search and I want to make sure that the correct index is used. In the mongo shell I just used explain but how could I use explain in the C# driver?
Here's how I perform my query:
public async Task<IEnumerable<GridFSFileInfo>> Find(string fileName)
{
var filter = Builders<GridFSFileInfo>.Filter.Regex(x => x.Filename, $"/.*{fileName}.*/i");
var options = new FindOptions
{
Modifiers = new BsonDocument("$hint", "filename_text")
};
var result = new List<GridFSFileInfo>();
using (var cursor = await Collection.Find(filter, options).ToCursorAsync())
{
while (await cursor.MoveNextAsync())
{
var batch = cursor.Current;
result.AddRange(batch);
}
}
return result;
}
Is there something like Collection.Find(filter, options).Explain() that returns a string or something? I searched on the web and found this: Is there an "Explain Query" for MongoDB Linq? However is seems there is no Explain method...
The modern mongo c# drivers support only one way to configure $explain. You can do it via FindOptions.Modifiers in the same was as you configured $hint above.
var options = new FindOptions
{
Modifiers = new BsonDocument
{
{ "$hint", "filename_text" },
{ "$explain", 1 }
}
};
var cursor = coll.Find(filter, options).As<BsonDocument>().ToCursor();
Pay attention, since $explain completely changes the server response, you should change your output type to BsonDocument(or a type with fields that matches to what explain returns) via As (if it's already not BsonDocument)
If I use:
private List<string> GetDataFrom()
{
var result = new List<string>();
using (var context = new mainEntities())
{
var matches = context.data.Where(s => s.Width == 500).ToList();
result.AddRange(matches.Select(t => t.Key));
}
return result;
}
It is giving me perfect results, but I want to use a method where I can use column name and value, like this:
private List<string> GetDataFrom(string columnName, int valToMatch)
{
var result = new List<string>();
using (var context = new mainEntities())
{
var propertyInfo = typeof(data).GetProperty(columnName).Name;
var matches = context.data
.Where(p => p.propertyInfo == valToMatch);
result.AddRange(matches.Select(t => t.Key));
}
return result;
}
This Method obviously doesn't work, so how can I do the same?
I am using SqlLite, so some answers given do not apply.
The whole problem is using propertyInfo the wrong way.
I tried various different approaches but no success.
This question is not a duplicate, because the suggested questions and their answers do not help much.
I like this question to be reopened.
I have found an answer myself I like to share.
ToString() method can not be translated into relevant SQL.
Try to use SqlFunctions.StringConvert(valToMatch) instead of valToMatch.ToString()
.ToString()
Doesn't works inside the Linq Query.
Inststed of .ToString() Function Use the following
SqlFunctions.StringConvert()
SEE THE FOLLOWING ANSWER
https://stackoverflow.com/a/3292773/3736442
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 need to find methods that match certain rules such as
has to be have return type void
has to be named "Set"
has to accept only one parameter
parameter type needs to match the type provided
I started going down the following route, but is just seems too much code. I wonder if there is a better way?
//find type name of the property
foreach (var propertySymbol in propertiesSymbols)
{
var propertyTypeSyntax =
((PropertyDeclarationSyntax) propertySymbol.DeclaringSyntaxNodes.First()).Type;
var propertyTypeInfo = semanticModel.GetTypeInfo(propertyTypeSyntax);
//find method with a name Set that accepts this type of the property
var allSetMethodsSymbols = classSymbol.GetMembers()
.Where(m => m.Kind == CommonSymbolKind.Method && m.Name.Equals("Set"))
.ToList();
foreach (var setMethodSymbol in allSetMethodsSymbols)
{
var methodDeclarationSyntax =
((MethodDeclarationSyntax) setMethodSymbol.DeclaringSyntaxNodes.First());
var expressionSyntax =
methodDeclarationSyntax.DescendantNodes().OfType<ExpressionSyntax>().First();
var typeInfo = semanticModel.GetTypeInfo(expressionSyntax);
var typeName = typeInfo.Type.Name;
if (typeName == "Void")
{
//now we know it is a method named "Set" and has return type "Void"
//let's see if parameter matches
var parameterSymbols =
methodDeclarationSyntax.DescendantNodes().OfType<ParameterSyntax>()
.ToList();
if (parameterSymbols.Count() == 1)
{
//this one has one parameter
//now let's see if it is of the type needed
var exprSyntax = ((ParameterSyntax) parameterSymbols.First())
.DescendantNodes().OfType<ExpressionSyntax>().First();
var parameterTypeInfo = semanticModel.GetTypeInfo(exprSyntax);
if (parameterTypeInfo.Type.Equals(propertyTypeInfo.Type))
{
//execute method rewriter
}
}
}
}
}
Solution as suggested by Jason:
var propertyTypeInfo = propertySymbol.Type;
//find method with a name Set that accepts this type of the property
IEnumerable<MethodSymbol> allSetMethodsSymbols = classSymbol
.GetMembers()
.Where(m =>m.Kind == CommonSymbolKind.Method && m.Name.Equals("Set"))
.Cast<MethodSymbol>();
var setMethod = allSetMethodsSymbols
.Single(x => x.ReturnsVoid
&& x.Parameters.Count == 1
&& x.Parameters.First().Type == propertyTypeInfo);
Yes, you're switching back and forth between our symbol model and syntax, which is making this more difficult than it needs to be. Cast those symbol objects you are getting from GetMembers to MethodSymbol (once checking that they're a method). Once you've got the MethodSymbol, you can just check the .ReturnType property to get the return type -- don't go to syntax and re-get it that way. Or, just use the handy .ReturnsVoid property for your scenario. Similarly, MethodSymbol has a .Parameters property you can use to get the parameters -- don't go back to syntax for that either.
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 });