C# Linq Query Bad Translates to the RQL in RavanDB - c#

I have an old project that was developed by the .Net framework and RavenDB 3 ...
I'm trying to migrate the RavenDB to the 5.* version
there is weird behavior in translate a Linq query to the RQL query
The below codes in c#
var query = documentSession
.Query<Documents_ByOrgan.Result, Documents_ByOrgan>()
.Where(x => x.OrganisationId == request.OrganisationId);
query = (from result in query
let onboarding = RavenQuery.Load<IOnboarding>(result.OnboardingDocumentId)
let onboradGroup = RavenQuery.Load<IGroup>(onboarding.Group.DocumentId)
let invitedBy = RavenQuery.Load<User>(onboarding.InvitedByUser.DocumentId)
let siteDoc = RavenQuery.Load<Site>(((Team)onboradGroup).Site.DocumentId)
select new
{
OnboardingId = onboarding.Id,
...
}).ProjectInto<T>();
Translates to the RQL in this way :
declare function output(result, onboarding) {
var onboradGroup = load(id(onboarding.Group));
var invitedBy = load(id(onboarding.InvitedByUser));
var siteDoc = load(onboradGroup.Site.DocumentId);
return { ... };
}
from index 'Onboardings/All' as result
result.OnboardingDocumentId as onboarding
select output(result, onboarding)
Why
let siteDoc = RavenQuery.Load<Site>(onboradGroup.Site.DocumentId)
translates to :
var siteDoc = load ( onboradGroup.Site.DocumentId );
BUT
RavenQuery.Load<IGroup>(onboarding.Group.DocumentId)
translates to the:
var onboradGroup = load ( id ( onboarding.Group ) );
how can I prevent from generating id(..) in RQL?
id(..) doesn't load any document
there is a lot of these queries in my project and all of them works fine with the 3.x version
In addition, I have the below config :
documentStore.Conventions.FindIdentityProperty = memberInfo => memberInfo.Name == "DocumentId";
there is a discussion here that helps me to find the problem

This seems to be related:
https://github.com/ravendb/ravendb/pull/12475
It was released in 4.2.116/5.2.2.

Related

Linq - EntityFramework NotSupportedException

I have a query that looks like this:
var caseList = (from x in context.Cases
where allowedCaseIds.Contains(x => x.CaseId)
select new Case {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
Notifier = x.NotifierId.HasValue ? new Notifier { Name = x.Notifier.Name } : null // This line throws exception
}).ToList();
A Case class can have 0..1 Notifier
The query above will result in the following System.NotSupportedException:
Unable to create a null constant value of type 'Models.Notifier'. Only entity types, enumeration types or primitive types are supported
in this context.
At the moment the only workaround I found is to loop the query result afterwards and manually populate Notifierlike this:
foreach (var c in caseList.Where(x => x.NotifierId.HasValue)
{
c.Notifier = (from x in context.Notifiers
where x.CaseId == c.CaseId
select new Notifier {
Name = x.Name
}).FirstOrDefault();
}
But I really don't want to do this because in my actual scenario it would generate hundreds of additional queries.
Is there any possible solution for a situation like this?.
I think you need to do that in two steps. First you can fetch only the data what you need with an anonymous type in a single query:
var caseList = (from x in context.Cases
where allowedCaseIds.Contains(x => x.CaseId)
select new {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
NotifierName = x.Notifier.Name
}).ToList();
After that, you can work in memory:
List<Case> cases = new List<Case>();
foreach (var c in caseList)
{
var case = new Case();
case.CaseId = c.CaseId;
case.NotifierId = c.NotifierId;
case.NotifierName = c.NotifierId.HasValue ? c.NotifierName : null;
cases.Add(case);
}
You could try writing your query as a chain of function calls rather than a query expression, then put an .AsEnumerable() in between:
var caseList = context.Clases
.Where(x => allowedCaseIds.Contains(x.CaseId))
.AsEnumerable() // Switch context
.Select(x => new Case() {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
Notifier = x.NotifierId.HasValue
? new Notifier() { Name = x.Notifier.Name }
: null
})
.ToList();
This will cause EF to generate an SQL query only up to the point where you put the .AsEnumerable(), further down the road, LINQ to Objects will do all the work. This has the advantage that you can use code that cannot be translated to SQL and should not require a lot of changes to your existing code base (unless you're using a lot of let expressions...)

Mongodb c# Conditions in query LINQ

My query look like this:
var query = from p in collection
where p.MinStockQuantity >= p.StockQuantity
select p;
I can't run because I have exception: Unsupported filter: ([MinStockQuantity] >= [StockQuantity])
This query also does not work, the same bug.
var collection = database.GetCollection<Product>("Product");
var builder = Builders<Product>.Filter;
var filter = builder.Where(o => o.MinStockQuantity > o.StockQuantity);
var query = collection.Find(filter).ToListAsync().Result;
How can I compare 2 fields ?
I know I am quite late but this works perfectly,try this :
var collection = database.GetCollection<Product>("Product");
var builder = Builders<Product>.Filter;
var filter = builder.Gt(o => o.MinStockQuantity , o.StockQuantity);
var query = collection.Find(filter).ToListAsync().Result;
Gt here is greater than.
there are various other methods like Gte =greater than equal to etc .
So my previous answer was obviously wrong. You shoud use that query
db.products.find({ $where: "this.MinStockQuantity > this.StockQuantity" })
To run this query in c# world you need to use BSON documents:
var doc = MongoDB.Bson.Serialization.BsonSerializer
.Deserialize<BsonDocument>
("{$where: \"this.MinStockQuantity > this.StockQuantity\"}");
var result = collection.Find(new CommandDocument(doc));
I used that query in my test application and it yells proper results.

Entity Framework Query Nested Query

I am new to the entity framework and am trying to convert the following query into the correct function calls.
Select Distinct a.nodeId FROM
(SELECT *
FROM reportContents
Where fitId = '29' and reportId =
(select max(reportId)
from reportContents
where fitId = '29')
) a Where (a.nodeId IS NOT NULL)
I know this query does what i want, however i'm not sure how to translate that into the entitiy framework!
Here was my attempt.
var prevSelectedNodes = db.reportContents.Where(
f => f.fitId == id).Select(
f => f.nodeId).Distinct().ToList();
I need to somehow put a .Select() in the where call. However that kind of thing dosen't seem possible
Thank you in advance!
As you can't make two LINQ nested lambda expression. You can do it with two requests :
var maxReportId = db.reportContents.Where(r => r.fitId = "29").Max(r => r.RepordId);
var result = db.reportContents.Where(r => r.fitId == "29" && r.reportId == maxReportId && r.nodeId != null).Select(a => a.nodeId).Distinct().ToList() ;

PredicateBuilder returns all users predicate.Or

I'm developing a library with C#, Entity Framework Code First 4.4.0.0 and .NET Framework 4.0.
I have this code:
string[] keywords = null;
if (query_string != null)
keywords = query_string.Split(' ');
// Check parameter. It will throw an exception.
ParameterCheck.CheckObjectId(user_id);
// Convert parameter to int.
int userId = Int32.Parse(user_id);
try
{
using (var context = new AdnLineContext())
{
context.Configuration.ProxyCreationEnabled = false;
//IQueryable<User> query = context.Users;
IQueryable<User> query = context.Users.AsExpandable();
var predicate = PredicateBuilder.False<User>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or(u => u.Name.Contains(temp) ||
u.State.Contains(temp) ||
u.HashTags.Contains(temp));
}
query.Where(predicate);
query.Include("WhoHasBlockedMe").Include("Friends");
var users = query.ToList();
[ ... ]
}
}
if keywords = {"john", "happy"} I want to get this SQL:
select * from users where
users.name like '%john%' or
users.state like '%john%' or
users.hashtags like '%john%' or
users.name like '%happy%' or
users.state like '%happy%' or
users.hashtags like '%happy%';
How can I do that with PredicateBuilder?
By the way, I have downloaded LinqKit from http://www.albahari.com/nutshell/predicatebuilder.aspx
UPDATE
I have added a breakpoint at var users = query.ToList(); and query has this SQL:
{SELECT
[Extent1].[UserId] AS [UserId],
[...]
FROM [dbo].[Users] AS [Extent1]}
It doesn't have a Where clause.
Your problem is here:
query.Where(predicate);
query.Include("WhoHasBlockedMe").Include("Friends");
var users = query.ToList();
.Where and .Include don't modify the underlying query, they return a new query.
Use
query = query.Where(predicate);
query = query.Include("WhoHasBlockedMe").Include("Friends");
var users = query.ToList();
instead.
What's the result of true OR something Or SomethingElse?
Do you even need to know what something is?
It will always be true.
You need to start out with False, not True. False Or anything is equivalent to anything.
If you were AND-ing items, you'd start with true, given that true AND anything is equivalent to anything.

Is there an "Explain Query" for MongoDB Linq?

Is there a way to run .explain() or equivalent on Linq queries? I would want to know
The text of the actual JSON query
The output of .explain() (indexes used, etc)
It would also be nice to have the execution time of the query
You can get the Json easily enough if you have a query wrapper;
var qLinq = Query<T>.Where(x => x.name=="jim");
Console.WriteLine(qLinq.ToJson());
There's also an Explain() method on MongoCursor, so you could do this;
var exp = Collection.FindAs<T>(qLinq).Explain()
Console.WriteLine(exp.ToJson());
So if you want the time taken, "millis" is in there;
var msTaken = exp.First(x => x.Name == "millis").Value.AsInt32;
If you have an IQueryable, try something like this;
void Do(MongoCollection col, IQueryable iq)
{
// Json Mongo Query
var imq = (iq as MongoQueryable<Blob>).GetMongoQuery();
Console.WriteLine(imq.ToString());
// you could also just do;
// var cursor = col.FindAs(typeof(Blob), imq);
var cursor = MongoCursor.Create(typeof(Blob), col, imq, ReadPreference.Nearest);
var explainDoc = cursor.Explain();
Console.WriteLine(explainDoc);
}//Do()
If you want this functionality in a library, I just created a GitHub project entitled
MongoDB query helper for .NET
https://github.com/mikeckennedy/mongodb-query-helper-for-dotnet
It will:
Explain a LINQ query as a strongly typed object (does it use an index for example)
Convert a LINQ query to the JavaScript code run in MongoDB
Check it out and contribute if you find it interesting.
Yes, there is. It shows everything .explain does and has a boolean for verbosity (it includes the time it took to execute):
var database = new MongoClient().GetServer().GetDatabase("db");
var collection = database.GetCollection<Hamster>("Hamsters");
var explanation = collection.AsQueryable().Where(hamster => hamster.Name == "bar").Explain(true);
Console.WriteLine(explanation);
It doesn't show the query though. Here's an extension method for that:
public static string GetMongoQuery<TItem>(this IQueryable<TItem> query)
{
var mongoQuery = query as MongoQueryable<TItem>;
return mongoQuery == null ? null : mongoQuery.GetMongoQuery().ToString();
}
Usage:
var query = collection.AsQueryable().Where(hamster => hamster.Name == "bar").GetMongoQuery();
Console.WriteLine(query);
In mongodb 3 C# I used following:
var users = Mongo.db.GetCollection<User>("Users");
var r = users(m => m._id == yourIdHere)
.Project(m => new { m._id, m.UserName, m.FirstName, m.LastName })
.Limit(1);
Console.WriteLine(r.ToString());
Result:
find({ "_id" : ObjectId("56030e87ca42192008ed0955") }, { "_id" : 1, "UserName" : 1, "FirstName" : 1, "LastName" : 1 }).limit(1)

Categories