Get to underlying SQL for Cosmos DB Query generated via LINQ - linq

I am creating a query to cosmos using Linq
This is converted into SQL which is then run to do the search
var modelName = "Mondeo";
var baseQuery = client.CreateDocumentQuery<Car>(StaticSettings.ProjectionsCollectionUri,
new FeedOptions { MaxItemCount = maxItemCount, PartitionKey = new PartitionKey(partitionKey) })
.Where(order => car.ModelName == modelName);
If I run this code and put a breakpoint after this statement, I can see the raw SQL query generated
This is shown in the first line of the inspector
{{"query":"SQL HERE"}}
How can I get to this via code?
I am looking to get to this SQL to ensure that it is what I want it to be and I can use it in my tests
Paul

Assuming you're using Visual Studio, the debugger by default would be showing you the output of ToString() in the inspector for any given object.
With that knowledge, you could retrieve that same query string object with the following code.
var serializedQuery = baseQuery.ToString(); // "{{\"query\":\"SQL HERE\"}}"
The result appears to be a serialized JSON object that wraps the actual SQL query. You can easily extract the SQL with Newtonsoft.Json using the following code.
var sql = JObject.Parse(serializedQuery)["query"].Value<string>(); \\ "SQL HERE"
EDIT: In current versions of the SDK, the solution would look like the following instead.
var baseQuery = container
.GetItemLinqQueryable<Car>(
requestOptions: new QueryRequestOptions
{
MaxItemCount = maxItemCount,
PartitionKey = new PartitionKey(partitionKey)
}
)
.Where(order => car.ModelName == modelName);
var sql = baseQuery.ToQueryDefinition().QueryText; // "SQL HERE"

Related

How to use anonymous types in Cosmos Linq query

I am strugging with using an anonymous type in Azure Cosmos Linq syntax. This is the SQL I am trying to emit:
// #"SELECT c.Profile.Customer.CompanyName,
// c.Profile.Customer.CustomerID,
// c.CustomFields.Status
// FROM c"
// );
My Linq query looks like this but will not compile because the anonymous type doesn't match the type of the IQueryable
IQueryable<CustomerDocument> query = container.GetItemLinqQueryable<CustomerDocument>()
.Select(c => new
{
c.Profile.Customer.CompanyName,
c.Profile.Customer.CustomerID,
c.CustomFields.Status
}).ToFeedIterator<CustomerDocument>();
If I omit the anonymous type the query runs fine, but it emits SELECT * FROM c which is very slow due to the size of the schema in the container. If I run the SQL statement it is very fast, but obviously I don't want to maintain SQL text in my code if I can avoid it.
Any ideas what I am doing wrong here? Not using Entity Framework in this project.
Any ideas what I am doing wrong here?
Try to use the var or FeedIterator <CustomerDocument> & .ToFeedIterator()
Agree with the #Svyatoslav Danyliv comments.
Below is the sample code from the Microsoft Documentation,
using (FeedIterator<CustomerDocument> query = container.GetItemLinqQueryable<CustomerDocument>().Select(c => new {
// Required columns here...
}).ToFeedIterator()) {
while (query.HasMoreResults)
{
foreach(var item in await query.ReadNextAsync())
{
Console.WriteLine(item.ColumnX);
}
}
}
References:
https://github.com/Azure/azure-cosmos-dotnet-v3/issues/893#issuecomment-559760853
https://stackoverflow.com/a/57171695/18935775

How to get all items of container in cosmos db with dotnet core

Hi I would like to get all items of a container in a database in cosmos DB. I can see that there are already a lot of methods available but I don't see any getAllItems or a lookalike.
Is there an easy way to do it like using LINQ or something?
Thanks
If you want to do this using Linq, you can do the following (As suggested in this answer here: How can I use LINQ in CosmosDB SDK v3.0 async query?):
var db = Client.GetDatabase(databaseId);
var container = db.GetContainer(containerId);
var q = container.GetItemLinqQueryable<Person>();
var iterator = q.ToFeedIterator();
var results = await iterator.ReadNextAsync();
If you want a non-Linq solution, use this (taken from the v3 SDK samples: https://github.com/Azure/azure-cosmos-dotnet-v3/blob/master/Microsoft.Azure.Cosmos.Samples/Usage/ItemManagement/Program.cs#L259:
QueryDefinition query = new QueryDefinition(
"select * from sales s where s.AccountNumber = #AccountInput ")
.WithParameter("#AccountInput", "Account1");
FeedIterator<SalesOrder> resultSet = container.GetItemQueryIterator<SalesOrder>(
query,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey("Account1"),
MaxItemCount = 1
});
while (resultSet.HasMoreResults)
{
FeedResponse<SalesOrder> response = await resultSet.ReadNextAsync();
// Work through iterator list here
}
Hope this helps!
You can use LINQ like described in Microsoft Azures documentation.
Something like this should be the solution of your problem:
var db = Client.GetDatabase(databaseId);
var container = db.GetContainer(containerId);
//Get iterator or use some LINQ queries here
var iterator = container.GetItemLinqQueryable<YourType>().GetEnumerator();

Is there a way to view LINQ Generated query expressions in CRM Dynamics?

Considering the fact LINQ queries in CRM Dynamics are translated into query expressions (source):
[...] The OrganizationServiceContext class contains an underlying LINQ
query provider that translates LINQ queries from Microsoft Visual C#
or Microsoft Visual Basic .NET syntax into the query API used by
Microsoft Dynamics CRM. [...]
Is there a way to see the generated query expressions (As it's possible to see the generated SQL query in Linq-to-Sql or Linq-to-Entities)?
You can use reflection to get the query object and then convert that to a FetchXML query to get a printable query. This will work with both early-bound and late-bound queries.
From: https://pogo69.wordpress.com/2012/04/05/crm-linq-provider-converting-expressions-to-queryexpression-andor-fetchxml/
var connectionString = #"SET YOUR CONNECTION STRING";
var service = new CrmServiceClient(connectionString);
using (var xrm = service.OrganizationServiceProxy)
{
OrganizationServiceContext orgContext =
new OrganizationServiceContext(xrm);
var query = from c in orgContext.CreateQuery("contact")
join a in orgContext.CreateQuery("account")
on c["contactid"] equals a["primarycontactid"]
where (String)c["lastname"] == "Wilcox" ||
(String)c["lastname"] == "Andrews"
where ((String)a["address1_telephone1"]).Contains("(206)")
|| ((String)a["address1_telephone1"]).Contains("(425)")
select new
{
Contact = new
{
FirstName = c["firstname"],
LastName = c["lastname"]
},
Account = new
{
Address1_Telephone1 = a["address1_telephone1"]
}
};
IQueryProvider queryProvider = query.Provider;
MethodInfo translateMethodInfo = queryProvider.GetType().GetMethod("Translate");
QueryExpression queryEx = (QueryExpression)translateMethodInfo.Invoke(queryProvider, new object[] { query.Expression });
QueryExpressionToFetchXmlRequest reqConvertToFetchXml = new QueryExpressionToFetchXmlRequest { Query = queryEx };
QueryExpressionToFetchXmlResponse respConvertToFetchXml = (QueryExpressionToFetchXmlResponse)xrm.Execute(reqConvertToFetchXml);
Console.WriteLine("To FetchXML:" + Environment.NewLine + Environment.NewLine);
Console.WriteLine(respConvertToFetchXml.FetchXml);
Alternatively you could use Fiddler to capture the actual query text sent in the SOAP message. I've done this before and haven't found it any more valuable than the FetchXml.

LINQ to Entities does not recognize the method 'Boolean CheckMeetingSettings(Int64, Int64)' method

I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.

Convert Loop To Linq - Model Creation

I'm converting an entity object to a model that can be passed around my application without the extra overhead (As well as generating a couple of extra fields for the view etc.
public IEnumerable<PageModel> GetAllPages()
{
var AllPageO = _session.All<Page>();
IList<PageModel> RetO = new List<PageModel>();
foreach (var AP in AllPageO)
{
RetO.Add(new PageModel(AP));
}
return RetO.AsEnumerable();
}
Can this be converted to a Linq Query, the below does work I get the error
Server Error in '/' Application. Only
parameterless constructors and
initializers are supported in LINQ to
Entities.
public IEnumerable<PageModel> GetAllPages()
{
var AllPageO = _session.All<Page>();
var RetO = from EntityO in AllPageO select new PageModel(EntityO);
return RetO;
}
Resharper actually converts the firt loop into this, which also fails with the same error.
IList<PageModel> RetO = PageO.Select(AP => new PageModel(AP)).ToList();
Thats because entity framework is trying to convert optimize your projection expression into sql.
The easy fix is to enumerate the results before the projection:
var RetO = from EntityO in AllPageO.ToList() select new PageModel(EntityO);

Resources