I'm seeing a lot of exceptions in the collectionSelfLink when making DocumentDb call -- see image below.
I'm able to connect to DocumentDb and read data but these exceptions concern me -- especially in something that's pretty straight forward like a collectionSelfLink.
Any idea what may be causing them and how to fix them?
Here's the function that's using the selfLink
public async Task<IEnumerable<T>> ReadQuery<T>(string dbName, string collectionId, SqlQuerySpec query)
{
// Prepare collection self link
// IMPORTANT: This is where I'm seeing those exceptions when I inspect the collectionLink. Otherwise, I'm NOT getting any errors.
var collectionLink = UriFactory.CreateDocumentCollectionUri(dbName, collectionId);
var result = _client.CreateDocumentQuery<T>(collectionLink, query, null);
_client.CreateDocumentQuery<T>(collectionLink);
return await result.QueryAsync();
}
And here's the QueryAsync() extension method
public async static Task<IEnumerable<T>> QueryAsync<T>(this IQueryable<T> query)
{
var docQuery = query.AsDocumentQuery();
var batches = new List<IEnumerable<T>>();
do
{
var batch = await docQuery.ExecuteNextAsync<T>();
batches.Add(batch);
}
while (docQuery.HasMoreResults);
var docs = batches.SelectMany(b => b);
return docs;
}
So SelfLink is an internal property that is set by DocumentDB. It cannot be set by the user and will only be populated on resources that have been returned from a call to the server.
The UriFactory code that you are using is construction a link that can be used to execute operations, but it is not a SelfLink.
If you are looking at a SelfLink property on a newly initialized DocumentCollection() object the SelfLink will be null as it has not been persisted on the server yet. This would explain all those errors in debug watch.
Related
In Cosmos DB v3, I'm getting an IOrderedQueryable<T> using GetItemLinqQueryable<T>. This allows me to write custom queries. The problem is I'd like to track request charges whenever a query is materialized. How can this be accomplished?
When I execute methods like ReadItemAsyncand ExecuteStoredProcedureAsync, the returned object has a RequestCharge property, but I need to detect charges with linq queries.
You can use the extension method ToFeedIterator on your IOrderedQueryable.
using Microsoft.Azure.Cosmos.Linq;
var query = container.GetItemLinqQueryable<MyClass>()
.Where(c => true)
.ToFeedIterator();
while (query.HasMoreResults)
{
var response = await query.ReadNextAsync();
Console.WriteLine(response.RequestCharge);
foreach (var myClassInstance in response)
{
// do stuff
}
}
edit: if you need count or any aggregate function:
var query = container.GetItemLinqQueryable<MyClass>()
.Where(c => true);
Response<int> x = await query.CountAsync();
Console.WriteLine(x.RequestCharge);
int count = x; // Autoboxing
You can find the full list of available extension functions on GitHub.
i tried to connect documentdb from my asp.net website but i am getting this error.
Entity with the specified id does not exist in the system
DocumentClientException: Entity with the specified id does not exist in the system
code as follows in aspx code behind
protected async void Page_Load(object sender, EventArgs e)
{
Response.Write("Page Load<br/>");
await GetData();
}
public async Task GetData()
{
try
{
Response.Write("<br/> Get Data function Start<br/><br/>");
using (var client = new DocumentClient(new Uri(ConfigurationManager.AppSettings["endpoint"]), ConfigurationManager.AppSettings["authKey"]))
{
//await client.OpenAsync();
RequestOptions reqOpt = new RequestOptions { PartitionKey = new PartitionKey(209) };
var parameters = new dynamic[] { 1 };
StoredProcedureResponse<object> result = await client.ExecuteStoredProcedureAsync<object>(
UriFactory.CreateStoredProcedureUri(ConfigurationManager.AppSettings["database"], ConfigurationManager.AppSettings["pcsd"], "GetMemberbyId"), reqOpt, parameters);
Response.Write(result.Response.ToString());
}
Response.Write("<br/><br/> Get Data function End");
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}
stored procedure as follows
function GetMemberbyId(memId) {
var collection = getContext().getCollection();
//return getContext().getResponse().setBody('no docs found');
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT * FROM root m where m.memberId='+memId,
function (err, feed, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found',
// else take 1st element from feed
if (!feed || !feed.length) getContext().getResponse().setBody('no docs found');
else getContext().getResponse().setBody(feed);
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
in my localhost it's working fine but website published to azure web apps and running i am getting above error
I just spent a couple of hours troubleshooting this, only to find that I had firewalled my instance to a point where I could not connect locally. Keep in mind that the Azure portal document query will obviously still work even when you have no direct access via the API / C# client.
Try setting the firewall to allow All Networks temporarily to check access.
I would check in the portal that the "GetMemberbyId" is the name of the stored procedure for the collection you are trying to run it on. Could be the stored procedure is on a different collection or that the stored procedure is named something else.
If that all checks out.. I have had more luck with the __.filter() way of querying documents on the server. See:
http://azure.github.io/azure-documentdb-js-server/
My Stored Procedure: (I created it via Azure Script Explorer)
function GetAllResources() {
var collection = getContext().getCollection();
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT * FROM MultiLanguage as m',
function (err, docs, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found',
// else take 1st element from feed
if (!docs || !docs.length) getContext().getResponse().setBody('no docs found');
else getContext().getResponse().setBody(JSON.stringify(docs));
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
The sproc can be executed successfully from script explorer.
My C# code to call the sproc:
public async Task<IHttpActionResult> GetReources() {
client = new DocumentClient(new Uri(ConfigurationManager.AppSettings["endpoint"]), ConfigurationManager.AppSettings["authKey"]);
var collectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
//var docs = await client.ReadDocumentFeedAsync(collectionLink, new FeedOptions { MaxItemCount = 10 });
//var docs = from d in client.CreateDocumentQuery<Models.Resource>(collectionLink)
// select d;
StoredProcedure storedProcedure = client.CreateStoredProcedureQuery(collectionLink).Where(c => c.Id == "GetAllResources").AsEnumerable().FirstOrDefault();
Models.Resource docs = await client.ExecuteStoredProcedureAsync<Models.Resource>(storedProcedure.SelfLink);
foreach (var d in docs) {
Models.Resource a = new Models.Resource();
a = docs;
//a.id = d.id;
//a.Scenario = d.Scenario;
//a.Translations = d.Translations;
//a.LastModified = d.LastModified;
//a.ModifiedBy = d.ModifiedBy;
//a.LastAccessed = d.LastAccessed;
resources.Add(a);
}
return Ok(resources);
}
First, there is an error for the "foreach..." like said
foreach cannot operate on variables of type Models.Resource because it
doesn't contain a public definition of GetEnumerator.
Then I tried to modify my sproc to only return 1 result and remove the foreach line, then I got error said
Failed to deserialize stored procedure response or convert it to type
'Models.Resource'
I just want to return the result of the stored procedure as my defined class (Models.Resource). How to do this?
It can be simpler to get sproc by name using CreateStoredProcedureUri, like this:
const string endpoint = "https://your.service.azure.com:443/";
const string authKey = "<your magic secret master key>==";
var client = new DocumentClient(new Uri(endpoint), authKey);
Uri sprocUri = UriFactory.CreateStoredProcedureUri("databaseName", "collectionName", "GetAllResources");
var result = await client.ExecuteStoredProcedureAsync<string>(sprocUri);
The stored procedure above serializes results of the query (docs array) to string, if you keep it this way, the result of sproc would be string, which I guess you would need to manually deserialize to objects. You can do this simpler, just return docs from sproc and have result as objects (like Models.Resource[]), serialization would happen automatically.
If you change the sproc to return just one doc (e.g. do __.response.setBody(docs[0]) and Models.Resource represent one item, then the call is correct:
Models.Resource doc = await client.ExecuteStoredProcedureAsync<Models.Resource>(sprocUri);
Also, to // Query documents and take 1st item, I wouldn't recommend to use script as script has overhead of running JavsScript engine. Scripts kick in when you have bulk operations (to optimize for network traffic) or have business logic which makes sense to run on the server. To take 1st item you can do query from client like this: SELECT TOP 1 * FROM c. Typically you would WHERE and ORDER BY clause to that.
There is a number of docdb samples on github, for instance, https://github.com/Azure/azure-documentdb-dotnet/tree/master/samples/code-samples/ServerSideScripts and https://github.com/Azure/azure-documentdb-dotnet/tree/master/samples/code-samples/Queries.
Thanks,
Michael
All right, let's make sure we are on the same page.
I am using the sproc same as above.
I am using client code like this:
class Models
{
// This would have more properties, I am just using id which all docs would have.
public class Resource
{
[JsonProperty("id")]
public string Id { get; set; }
}
}
public async Task<IHttpActionResult> GetResources()
{
const string endpoint = "https://myservice.azure.com:443/";
const string authKey = "my secret key==";
var client = new DocumentClient(new Uri(endpoint), authKey);
Uri sprocUri = UriFactory.CreateStoredProcedureUri("db", "c1", "GetAllResources");
var serializedDocs = await client.ExecuteStoredProcedureAsync<string>(sprocUri);
Models.Resource[] resources = JsonConvert.DeserializeObject<Models.Resource[]>(serializedDocs);
return Ok(resources);
}
It works fine. Is this what you are doing?
I am making a small App that should list the number of items in my Azure queues.
When I use FetchAttributesAsync and ApproximateMessageCount in a Console App, I get the expected result in ApproximateMessageCount after a call to FetchAttributesAsync (or FetchAttributes).
When I use the same in a Universal Windows app, ApproximateMessageCount remains stuck at null after a call to FetchAttributesAsync (FetchAttributes is not available there).
Console code:
CloudStorageAccount _account;
if (CloudStorageAccount.TryParse(_connectionstring, out _account))
{
var queueClient = _account.CreateCloudQueueClient();
Console.WriteLine(" {0}", _account.QueueEndpoint);
Console.WriteLine(" ----------------------------------------------");
var queues = (await queueClient.ListQueuesSegmentedAsync(null)).Results;
foreach (CloudQueue q in queues)
{
await q.FetchAttributesAsync();
Console.WriteLine($" {q.Name,-40} {q.ApproximateMessageCount,5}");
}
}
Universal App code:
IEnumerable<CloudQueue> queues;
CloudStorageAccount _account;
CloudQueueClient queueClient;
CloudStorageAccount.TryParse(connectionstring, out _account);
queueClient = _account.CreateCloudQueueClient();
queues = (await queueClient.ListQueuesSegmentedAsync(null)).Results;
foreach (CloudQueue q in queues)
{
await q.FetchAttributesAsync();
var count = q.ApproximateMessageCount;
// count is always null here!!!
}
I have tried all kinds of alternatives, like Wait()'s and such on the awaitables. Whatever I try, the ApproximateMessageCount stays a null with dertermination :-(.
Am I missing something?
I think you have discovered a bug in the storage client library. I looked up the code on Github and essentially instead of reading the value for Approximate Message Count header, the code is reading the value for Lease Status header.
In QueueHttpResponseParsers.cs class:
public static string GetApproximateMessageCount(HttpResponseMessage response)
{
return response.Headers.GetHeaderSingleValueOrDefault(Constants.HeaderConstants.LeaseStatus);
}
This method should have been:
public static string GetApproximateMessageCount(HttpResponseMessage response)
{
return response.Headers.GetHeaderSingleValueOrDefault(Constants.HeaderConstants.ApproximateMessagesCount);
}
I have submitted a bug for this: https://github.com/Azure/azure-storage-net/issues/155.
In trying to integrate RavenDB usage with Service Stack, I ran across the following solution proposed for session management:
A: using RavenDB with ServiceStack
The proposal to use the line below to dispose of the DocumentSession object once the request is complete was an attractive one.
container.Register(c => c.Resolve<IDocumentStore>().OpenSession()).ReusedWithin(ReuseScope.Request);
From what I understand of the Funq logic, I'm registering a new DocumentSession object with the IoC container that will be resolved for IDocumentSession and will only exist for the duration of the request. That seemed like a very clean approach.
However, I have since run into the following max session requests exception from RavenDB:
The maximum number of requests (30) allowed for this session has been
reached. Raven limits the number of remote calls that a session is
allowed to make as an early warning system. Sessions are expected to
be short lived, and Raven provides facilities like Load(string[] keys)
to load multiple documents at once and batch saves.
Now, unless I'm missing something, I shouldn't be hitting a request cap on a single session if each session only exists for the duration of a single request. To get around this problem, I tried the following, quite ill-advised solution to no avail:
var session = container.Resolve<IDocumentStore>().OpenSession();
session.Advanced.MaxNumberOfRequestsPerSession = 50000;
container.Register(p => session).ReusedWithin(ReuseScope.Request);
Here is a sample of how I'm using the resolved DocumentSession instance:
private readonly IDocumentSession _session;
public UsersService(IDocumentSession session)
{
_session = session;
}
public ServiceResponse<UserProfile> Get(GetUser request)
{
var response = new ServiceResponse<UserProfile> {Successful = true};
try
{
var user = _session.Load<UserProfile>(request.UserId);
if (user == null || user.Deleted || !user.IsActive || !user.IsActive)
{
throw HttpError.NotFound("User {0} was not found.".Fmt(request.UserId));
}
response.Data = user;
}
catch (Exception ex)
{
_logger.Error(ex.Message, ex);
response.StackTrace = ex.StackTrace;
response.Errors.Add(ex.Message);
response.Successful = false;
}
return response;
}
As far as I can see, I'm implementing SS + RavenDB "by the book" as far as the integration point goes, but I'm still getting this max session request exception and I don't understand how. I also cannot reliably replicate the exception or the conditions under which it is being thrown, which is very unsettling.