MongoDB FilterDefinition and interfaces in C# - c#

I have a MongoDB collection contains differents entity with same interface IDocument.
public class BarDocument: IDocument
{
public string BarField { get; set; }
}
Now I need to find inside this collection doing something like this:
var fooDoc = new FooDocument() { FooField = "test Foo Field" };
var barDoc = new BarDocument() { BarField = "test Bar Field" };
var barDoc1 = new BarDocument() { BarField = "test Bar Field 2" };
await _documentsCollection.InsertManyAsync(new List<IDocument>() { fooDoc, barDoc, barDoc1 });
var filterBar = Builders<BarDocument>.Filter.Where(x => x.BarField.Contains("Bar")) ;
var result = await _documentsCollection.Find(filterBar).ToListAsync();
But this Find statement ends in a compilation error:
Error CS1503 Argument 2: cannot convert from 'MongoDB.Driver.FilterDefinition<WebApi.Models.Entities.Documents.BarDocument>' to 'System.Linq.Expressions.Expression<System.Func<WebApi.Models.Entities.Documents.IDocument, bool>>'
Can someone give me a hint?

The _documentsCollection variable is defined as ICollection<IDocument>>, thus you can insert documents that are defined both as FooDocument and BarDocument and it works - MongoDB knows how to store them in one collection and preserve their original type (_t field).
Once they're inserted to the same collection, you're trying to query ICollection<IDocument>> using filter that is defined on the derived type, which won't be allowed by the compiler, since BarField is unknown to IDocument.
What you can do is to define another variable which targers BarDocument specifically and then run your query on such collection:
var barCollection = database.GetCollection<BarDocument>("yourcol");
var filterBar = Builders<BarDocument>.Filter.
Where(x => x.BarField.Contains("Bar"));
var result = await barCollection.Find(filterBar).ToListAsync();
This should work as long as your query returns only instances of BarDocument class.

Related

Check if a List contains an object with a certain value using Nest ElasticSearch

I'm trying to build a filter to my BoolQuery.
the response object contians an object whic contains 2 properties, a bool and a list of another object like this :
responsObject : {
innerObject : {
myinnerObjectBoolProperty: Bool,
myinnerObjectListProperty : [
{
innerObjectType: myEnum,
// otherProperties
},
{
innerObjectType: myEnum,
// otherProperties
}
]
} ,
// other properties
}
I have a method that returns an IEnumerable<QueryContainer> to build a filter for a query
after i build the filters i assign them t a BoolQuery's Must BoolQuery bQuery property and I assign that BoolQuery to a SeachRequest's Query property and pass it to the ElasticClient's Search method
var searchRequest = new SearchRequest()
{
Query = bQuery,
// other assignments
};
var response = client.Search<responsObject >(searchRequest);
inside the method mentioned above the
filtering through the bool property is working fine like this:
if (filterValuesList.Contains("myBoolProperty", StringComparer.OrdinalIgnoreCase))
{
var termQuery = new TermQuery()
{
Field = "innerObject.myBoolProperty",
Value = "true"
};
queries.Add(termQuery);
}
but what i'm trying to do is to filter objects that have a certain vaule in myEnum.
var termQuery = new TermQuery()
{
Field = "innerObject.myinnerObjectListProperty.innerObjectType",
Value = "certainType"
};
but it doesn't work when I do it like that.
simply what I'm truing to achieve is an equivalent to this LINQ statment:
var result = responsObjects.Where(i =>i.innerObject.myinnerObjectListProperty.Any(p => p.innerObjectType == innerObjectType.CertainType));
I figured out what the problem was.
I had an attribute on myEnum property to convert the enum to string like : [JsonConverter(typeof(StringEnumConverter))]
so I thought that I should pass the string value of the enum.
I tried passing the underlying type of the enum (which is int) so it worked like a charm

Invalid anonymous type member declarator c# linq?

I have this json structure :
[
{
"code": "T01",
"stock": 0
},
{
"code": "T02",
"stock": 1
}
]
How can I achieve his structure :
[{
T01: {
stock: 0
}
}, {
T02: {
stock: 1
}
}]
using linq in C#.
Currently I have this line of code :
var inv = inventory.Select(i => new { code = i.SiteCode, stock = i.Stock).ToList().ToArray();
The value of inv will get the first structure result.
I am trying to do something like this :
inv = inventory.Select(i => new { i.SiteCode = new { stock = i.Stock} }).ToList().ToArray();
I am getting the following compile error :
Error 1 Invalid anonymous type member declarator. Anonymous type
members must be declared with a member assignment, simple name or
member access.
Any Help ?
You cannot create property names dynamically like that with linq. You must give a specific name. If what you want to achieve is like you described then better convert it to a dictionary. Something like this:
var inv = inventory.ToDictionary(key => key.SiteCode, value => new { Stock = value.Stock});
In the case that you might have duplicate SiteCodes then maybe for the value you can use a collection or otherwise use ToLookup.
About the usage of the ToArray/ToList - most of linq's methods are differed executed and are more like instructions on what to do (queries). When adding a method like ToList/ToArray/FirstOrDefault/Sum... - only at that point the query is actually executed and returns the result. in the case above you use .ToList().ToArray() - the linq is executed and returned for the List and then you convert it into an array. Use only one of the two according to the output you want (or as my suggestion above convert to a dictionary or lookup
Unfortunately you can't do that, because even though the type is anonymous, it is still a static type (not a dynamic one) and it must be known at compile time.
For this structure (I have made a class to represent yours):
class A
{
public string code { get; set; }
public int stock { get; set; }
}
I have made the following algorithm, not so nice but gives a kind of what are you asking:
var objects = new List<A> { new A { code = "T01", stock = 1 }, new A { code = "T02", stock = 2 } };
string s = "[";
objects.ForEach(x => s += "{" + x.code + ": {stock: " + x.stock + "}},");
s = s.Remove(s.Length - 1, 1);
s += "]";
You can complete the formatting by adding blank spaces where you need, but again, what are you looking for is not a valid json format.

How to get the Id of the selectedvalue?

I have DropDownList with the following values:
ddl.SelectedValue = { Id = 234, Name = "ABC Name" }
How can I get the value of the Id?
I use WinForms and RadDropDownList
Try this one:
public int GetId(object obj)
{
var anon = new { Id = 0, Name = string.Empty };
var obj2 = MakeSameType(obj, anon);
return obj2.Id;
}
public static T MakeSameType<T>(object obj, T anonymous)
{
return (T)obj;
}
use it like:
int id = GetId(ddl.SelectedValue);
If it works, it is thanks to how the equivalent anonymous types are "condensed" in single types by the C# compiler.
Note that this solution is as brittle as you can have
If you add/remove/rename a property of the anonymous type, the GetId will break (you'll have to keep the var anon = new {...} perfectly aligned). If you move the method that creates the collection of anonymous types to another assembly it will break too (because only anonymous types inside the same assembly are "unified" by the compiler).
In general you shouldn't have anonymous types that "escape" a method. An anonymous type should remain in the method that it is defined. Assigning it directly as the DataSource of a control is asking for problems.
If you feel lazy and don't want to create a type for a key-value, use the Tuple:
var tuple = Tuple.Create(something, somethingelse, somethingstillelse);
var value1 = tuple.Item1;
var value2 = tuple.Item2;
var value3 = tuple.Item3;
and so on.
As your are using anonymous types, it gets little complicated. But, you can cast SelectedValue to dynamic, and exstract 'Id' from there:
dynamic selectedValue = ddl.SelectedValue;
int id = selectedValue.Id;
But i would recommend to declare your own class or struct for such cases.
I have changed the LINQ query
from:
var query = (from q in tableq where ...
select new {Id = q.Id, Name = q.Name});
to:
var query = (from q in tableq where ...
select q);
... and then change it to:
table1.Id = (ddl.SelectedValue as tableq).Id == null ? table1.Id : (ddl.SelectedValue as tableq).Id;

Error when trying to cast anonymous object, Razor

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();

Dynamic LINQ: Specifying class name in new clause

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 });

Categories

Resources