I am writing below code to retrieve all entities from Azure table. But I am kind of stuck up in passing entity resolver delegate. I could not find much reference on MSDN.
Can some one please point out, how to use EntityResover in below code?
public class ATSHelper<T> where T : ITableEntity, new()
{
CloudStorageAccount storageAccount;
public ATSHelper(CloudStorageAccount storageAccount)
{
this.storageAccount = storageAccount;
}
public async Task<IEnumerable<T>> FetchAllEntities(string tableName)
{
List<T> allEntities = new List<T>();
CloudTable table = storageAccount.CreateCloudTableClient().GetTableReference(tableName);
TableContinuationToken contToken = new TableContinuationToken();
TableQuery query = new TableQuery();
CancellationToken cancelToken = new CancellationToken();
do
{
var qryResp = await table.ExecuteQuerySegmentedAsync<T>(query, ???? EntityResolver ???? ,contToken, cancelToken);
contToken = qryResp.ContinuationToken;
allEntities.AddRange(qryResp.Results);
}
while (contToken != null);
return allEntities;
}
}
Here is a nice article describing the Table Storage in deep. It also includes couple of samples for EntityResolver.
Ideal would be to have one Generic Resolver, that produces the desired result. Then you can include it in your call. I will just quote here one example from the provided article:
EntityResolver<ShapeEntity> shapeResolver = (pk, rk, ts, props, etag) =>
{
ShapeEntity resolvedEntity = null;
string shapeType = props["ShapeType"].StringValue;
if (shapeType == "Rectangle") { resolvedEntity = new RectangleEntity(); }
else if (shapeType == "Ellipse") { resolvedEntity = new EllipseEntity(); }
else if (shapeType == "Line") { resolvedEntity = new LineEntity(); }
// Potentially throw here if an unknown shape is detected
resolvedEntity.PartitionKey = pk;
resolvedEntity.RowKey = rk;
resolvedEntity.Timestamp = ts;
resolvedEntity.ETag = etag;
resolvedEntity.ReadEntity(props, null);
return resolvedEntity;
};
currentSegment = await drawingTable.ExecuteQuerySegmentedAsync(drawingQuery, shapeResolver, currentSegment != null ? currentSegment.ContinuationToken : null);
Read the full article to better understand the deal with resolvers.
Related
My use case - I want to render the same template using different model instances from a Powershell script. I wrap Razorlight in an exe tool, but I do not want to call the tool with all the model objects at once - instead I want to be able to pass them as they become available.
This means I cannot use the MemoryCachingProvider. What I need is a file system caching provider and a way to reuse it between the calls to the tool.
Is there anything off-the-shelf?
EDIT 1
So I am banging the wall this whole day on this thing. I have the generated Assembly object, but it is not serializable. It is an in-memory assembly, so no file on disk. And there seems to be no way in RazorLight to instruct the compilation to save it on disk.
I must be missing something really obvious here, at least this is the sentiment I get from the first comment to this post. Please, put me out of my misery - share what am I missing here.
So I failed to find a way to do it in the current version of RazorLight. I am extremely curious as to the "too many ways to approach this problem".
Anyway, my solution to the problem is to modify the RazorLight code to enable such a feature. Here is the PR - https://github.com/toddams/RazorLight/pull/492
If merged and released, then it would be possible to write a cache like this:
public class FileSystemCachingProvider : ICachingProvider
{
private MemoryCachingProvider m_cache = new();
private readonly string m_root;
public FileSystemCachingProvider(string root)
{
m_root = root;
}
public void PrecreateAssemblyCallback(IGeneratedRazorTemplate generatedRazorTemplate, byte[] rawAssembly, byte[] rawSymbolStore)
{
var srcFilePath = Path.Combine(m_root, generatedRazorTemplate.TemplateKey[1..]);
var asmFilePath = srcFilePath + ".dll";
File.WriteAllBytes(asmFilePath, rawAssembly);
if (rawSymbolStore != null)
{
var pdbFilePath = srcFilePath + ".pdb";
File.WriteAllBytes(pdbFilePath, rawSymbolStore);
}
}
public void CacheTemplate(string key, Func<ITemplatePage> pageFactory, IChangeToken expirationToken)
{
}
public bool Contains(string key)
{
var srcFilePath = Path.Combine(m_root, key);
var asmFilePath = srcFilePath + ".dll";
if (File.Exists(asmFilePath))
{
var srcLastWriteTime = new FileInfo(srcFilePath).LastWriteTimeUtc;
var asmLastWriteTime = new FileInfo(asmFilePath).LastWriteTimeUtc;
return srcLastWriteTime < asmLastWriteTime;
}
return false;
}
public void Remove(string key)
{
var srcFilePath = Path.Combine(m_root, key);
var asmFilePath = srcFilePath + ".dll";
var pdbFilePath = srcFilePath + ".pdb";
if (File.Exists(asmFilePath))
{
File.Delete(asmFilePath);
}
if (File.Exists(pdbFilePath))
{
File.Delete(pdbFilePath);
}
}
public TemplateCacheLookupResult RetrieveTemplate(string key)
{
var srcFilePath = Path.Combine(m_root, key);
var asmFilePath = srcFilePath + ".dll";
if (File.Exists(asmFilePath))
{
var srcLastWriteTime = new FileInfo(srcFilePath).LastWriteTimeUtc;
var asmLastWriteTime = new FileInfo(asmFilePath).LastWriteTimeUtc;
if (srcLastWriteTime < asmLastWriteTime)
{
var res = m_cache.RetrieveTemplate(key);
if (res.Success)
{
return res;
}
var rawAssembly = File.ReadAllBytes(asmFilePath);
var pdbFilePath = srcFilePath + ".pdb";
var rawSymbolStore = File.Exists(pdbFilePath) ? File.ReadAllBytes(pdbFilePath) : null;
return new TemplateCacheLookupResult(new TemplateCacheItem(key, CreateTemplatePage));
ITemplatePage CreateTemplatePage()
{
var templatePageTypes = Assembly
.Load(rawAssembly, rawSymbolStore)
.GetTypes()
.Where(t => typeof(ITemplatePage).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface)
.ToList();
if (templatePageTypes.Count != 1)
{
throw new ApplicationException($"Code bug: found {templatePageTypes.Count} concrete types implementing {nameof(ITemplatePage)} in the generated assembly.");
}
m_cache.CacheTemplate(key, CreateTemplatePage);
return CreateTemplatePage();
ITemplatePage CreateTemplatePage() => (ITemplatePage)Activator.CreateInstance(templatePageTypes[0]);
}
}
}
return new TemplateCacheLookupResult();
}
}
And then one could use it like this:
string root = Path.GetDirectoryName(m_razorTemplateFilePath);
var provider = new FileSystemCachingProvider(root);
var engine = new RazorLightEngineBuilder()
.UseFileSystemProject(root, "")
.UseCachingProvider(provider)
.AddPrecreateAssemblyCallbacks(provider.PrecreateAssemblyCallback)
.Build();
I currently do have a working method which is based on the currently released example code at https://github.com/Azure-Samples/Azure-Time-Series-Insights/tree/master/csharp-tsi-preview-sample
The used types within the following method are created with AutoRest as guided in the GitHub sample: https://github.com/Azure/azure-rest-api-specs/tree/master/specification/timeseriesinsights/data-plane
My initial try is the following:
public async Task<T> GetLatestEventValue<T>(object[] timeSeriesId, string tsiPropertyName,
DateTimeRange searchSpan)
{
var client = await _tsiClientFactory.GetTimeSeriesInsightsClient();
var propertyType = GetPropertyType(typeof(T));
if (propertyType == null) throw new InvalidOperationException($"Unsupported property type (${typeof(T)})");
string continuationToken = null;
do
{
QueryResultPage queryResponse = await client.Query.ExecuteAsync(
new QueryRequest(
getEvents: new GetEvents(
timeSeriesId: timeSeriesId,
searchSpan: searchSpan,
filter: null,
projectedProperties: new List<EventProperty>()
{new EventProperty(tsiPropertyName, propertyType)})),
continuationToken: continuationToken);
var latestEventIndex = GetLatestEventPropertyIndex(queryResponse.Timestamps);
var lastValue = queryResponse.Properties
.FirstOrDefault()
?.Values[latestEventIndex];
if (lastValue != null)
{
return (T)lastValue;
}
continuationToken = queryResponse.ContinuationToken;
} while (continuationToken != null);
return default;
}
And usage of the method (timeSeriesId is the same as in Microsoft public example):
var repository = new TsiRepository(_factory);
object[] timeSeriesId = new object[] { "2da181d7-8346-4cf2-bd94-a17742237429" };
var today = DateTime.Now;
var earlierDateTime = today.AddDays(-1);
var searchSpan = new DateTimeRange(earlierDateTime.ToUniversalTime(), today.ToUniversalTime());
var result = await repository.GetLatestEventValue<double>(timeSeriesId, "data", searchSpan);
The approach presented above kinda works but does not feel optimal. Is there a simpler way to query the latest event and its value for a given time-series instance? Maybe to take advance of the Time Series Expression (Tsx) capabilities?
After some time spent searching for the answer, my approach is to take advance of the TSX query syntax for last value and add an additional parameter which decides that the query is run only on the warm storage of TSI. This seems to speed things up nicely. The default is cold storage.
public async Task<T> RunAggregateSeriesLastValueAsync<T>(object[] timeSeriesId, DateTimeRange searchSpan)
{
var interval = searchSpan.To - searchSpan.FromProperty;
string continuationToken = null;
object lastValue;
do
{
QueryResultPage queryResponse = await _tsiClient.Query.ExecuteAsync(
new QueryRequest(
aggregateSeries: new AggregateSeries(
timeSeriesId: timeSeriesId,
searchSpan: searchSpan,
filter: null,
interval: interval,
projectedVariables: new[] { "Last_Numeric" },
inlineVariables: new Dictionary<string, Variable>()
{
["Last_Numeric"] = new NumericVariable(
value: new Tsx("$event.value"),
aggregation: new Tsx("last($value)"))
})),
storeType: "WarmStore", // Speeds things up since only warm storage is used
continuationToken: continuationToken)
lastValue = queryResponse.Properties
.FirstOrDefault()
?.Values.LastOrDefault(v => v != null)
continuationToken = queryResponse.ContinuationToken;
} while (continuationToken != null)
return (T)lastValue;
}
In following code snippet I am retrieving notes related to an order. It works fine only if notetext does contain data. Now, while debugging I found that, in other case it throws the exception that Object reference not set to an instance of an object.
I think following snippet looks good, but not sure what is missing, any idea to sort out the problem?
private void fetchDocument(IOrganizationService service, Guid vOrderId)
{
EntityCollection results = null;
string tempNote = string.Empty;
string tempFileName = string.Empty;
ColumnSet cols = new ColumnSet("subject", "filename", "documentbody", "mimetype","notetext");
QueryExpression query = new QueryExpression {
EntityName = "annotation" ,
ColumnSet = cols,
Criteria = new FilterExpression
{
Conditions = {
new ConditionExpression("objectid",ConditionOperator.Equal,vOrderId)
}
}
};
results = service.RetrieveMultiple(query);
Entity defaultRecord = results.Entities.ElementAtOrDefault(0);
if(defaultRecord.Contains("notetext"))
{
tempNote = defaultRecord.GetAttributeValue<string>("notetext");
}
if (defaultRecord.Contains("filename"))
{
tempFileName = defaultRecord.GetAttributeValue<string>("filename");
}
}
You haven't guarded defaultrecord against null.
results = service.RetrieveMultiple(query);
if (results.Entities == null || !results.Entities.Any()) return;
Entity defaultRecord = results.Entities.ElementAt(0);
Extending the answer to backup result.Entities == null check.
Retrieve multiple EntityCollection is not foolproof.
EntityCollection property:
Decomplied SDK retrieve multiple core:
protected internal virtual EntityCollection RetrieveMultipleCore(QueryBase query)
{
bool? retry = new bool?();
do
{
bool forceClose = false;
try
{
using (new OrganizationServiceContextInitializer(this))
return this.ServiceChannel.Channel.RetrieveMultiple(query);
}
catch (MessageSecurityException ex)
{
..
}
finally
{
this.CloseChannel(forceClose);
}
}
while (retry.HasValue && retry.Value);
return (EntityCollection) null;
}
Decomplied SDK Cached Organization Serivce Context Retrieve multiple:
public override EntityCollection RetrieveMultiple(QueryBase query)
{
RetrieveMultipleRequest retrieveMultipleRequest = new RetrieveMultipleRequest();
retrieveMultipleRequest.Query = query;
RetrieveMultipleResponse multipleResponse = this.Execute<RetrieveMultipleResponse>((OrganizationRequest) retrieveMultipleRequest);
if (multipleResponse == null)
return (EntityCollection) null;
else
return multipleResponse.EntityCollection;
}
public EntityCollection EntityCollection
{
get
{
if (this.Results.Contains("EntityCollection"))
return (EntityCollection) this.Results["EntityCollection"];
else
return (EntityCollection) null;
}
}
Your issue is actually at this line:
Entity defaultRecord = results.Entities.ElementAtOrDefault(0);
There are no results found, meaning
there is no Annotation that exists with an objectid of "vOrderId", or the user that is performing the query, doesn't have rights to read that record.
Regardless, you should just check for defaultRecord being null or not, and exiting if it is.
This check of null is a common occurrence, which is why I've written this ExtensionMethod:
public Entity GetFirstOrDefault(this IOrganizationService service, QueryBase qb) {
return service.RetrieveMultiple(qb)?.Entities.FirstOrDefault();
}
This would simplify your code to this:
private void fetchDocument(IOrganizationService service, Guid vOrderId)
{
EntityCollection results = null;
string tempNote = string.Empty;
string tempFileName = string.Empty;
ColumnSet cols = new ColumnSet("subject", "filename", "documentbody", "mimetype","notetext");
QueryExpression query = new QueryExpression {
EntityName = "annotation" ,
ColumnSet = cols,
Criteria = new FilterExpression
{
Conditions = {
new ConditionExpression("objectid",ConditionOperator.Equal,vOrderId)
}
}
};
var defaultRecord = service.GetFirstOrDefault(query);
if(defaultRecord != null)
{
if(defaultRecord.Contains("notetext"))
{
tempNote = defaultRecord.GetAttributeValue<string>("notetext");
}
if (defaultRecord.Contains("filename"))
{
tempFileName = defaultRecord.GetAttributeValue<string>("filename");
}
}
}
I need to do page load on scroll down in my application. I am using couchdb as my back end and I found a pagination option in couchdb which I think would satisfy my issue.
The thing is I can't find any working examples for pagination anywhere. I need someone's help in making my application work with this one.
Take a look at this for reference: https://github.com/soitgoes/LoveSeat/blob/master/LoveSeat/PagingHelper.cs
This is my code. I am getting an error in the options = model.GetOptions(); line, saying "object reference not set to an instance of an object".
public List<newVO> Getdocs(IPageableModel model)
{
List<newVO> resultList = new List<newVO>();
var etag = "";
ViewOptions options = new ViewOptions();
options = model.GetOptions();
options.StartKeyDocId = lastId;
options.Limit = 13;
options.Skip = 1;
var result = oCouchDB.View<newVO>("GetAlldocs", options);
//model.UpdatePaging(options, result);
if (result.StatusCode == HttpStatusCode.NotModified)
{
response.StatusCode = "0";
return null;
}
if (result != null)
{
foreach (newVO newvo in result.Items)
{
resultList.Add(newvo );
}
}
return resultList;
}
Thanks in advance. All ideas are welcome.
public List<newVO> Getdocs(IPageableModel model)
{
List<newVO> resultList = new List<newVO>();
var etag = "";
ViewOptions options = new ViewOptions();
options = model.GetOptions();
options.StartKeyDocId = lastId;
options.Limit = 13;
options.Skip = 1;
var result = oCouchDB.View<newVO>("GetAlldocs", options);
//model.UpdatePaging(options, result);
if (result.StatusCode == HttpStatusCode.NotModified)
{
response.StatusCode = "0";
return null;
}
if (result != null)
{
foreach (newVO newvo in result.Items)
{
resultList.Add(newvo );
}
}
return resultList;
}
This is my code and i am getting error in "options = model.GetOptions();" line that object reference not set to an instance of an object...
I've not used the LoveSeat paging implementation, but you can use the Limit and Skip properties on the ViewOptions to achieve paging:
public static IEnumerable<T> GetPage(this ICouchDatabase couchDatabase,
string viewName,
string designDoc,
int page,
int pageSize)
{
return couchDatabase.View(viewName, new ViewOptions
{
Skip = page * pageSize,
Limit = pageSize
}, designDoc);
}
This simple extension method will get a page of data from a CouchDB view
I'm trying to get all entries in my table asynchronously but am unable to figure out how to work with the continuation token. I suspect I need to take my anonymous method and convert it to a delegate, then recursively call it with the continuation token.
How do I take the following code and perform an Async call and fetch all entries in the new API?
Task<string[]> GetAllTableEntries(CloudTable tbl, string[] urls, string name, CancellationToken token)
{
TableRequestOptions reqOptions = new TableRequestOptions() { };
OperationContext ctx = new OperationContext() { ClientRequestID = "" };
object state = null;
// Register Cancelation Token
ICancellableAsyncResult result = null;
TableQuery qry = new TableQuery();
TableContinuationToken tok = null;
result = tbl.BeginExecuteQuerySegmented(qry, tok, reqOptions, ctx, (o) =>
{
var response = (o.AsyncState as CloudTable).EndExecuteQuerySegmented(o);
Console.WriteLine("Found " + response.Results.Count + " records");
// The following code was used in the previous version of the SDK
//
//26: // add first segment of data
//27: pageData.CompletedList.AddRange(
//28: from wu in response.Results
//29: select new CompletedWorkUnit(wu));
//30:
//31: // continue fetching segments to complete page
//32: while (response.HasMoreResults)
//33: {
//34: response = response.GetNext();
//35: pageData.CompletedList.AddRange(
//36: from wu in response.Results
//37: select new CompletedWorkUnit(wu));
//38: }
//39:
//40: // set continuation token for next page request
//41: pageData.ContinuationToken = response.ContinuationToken;
//42: evt.Set();
}, state);
// Add cancellation token according to guidance from Table Client 2.0 Breaking Changes blog entry
token.Register((o) => result.Cancel(), state);
Please try this:
static void ExecuteQuery()
{
TableContinuationToken token = null;
TableRequestOptions reqOptions = new TableRequestOptions() { };
OperationContext ctx = new OperationContext() { ClientRequestID = "" };
long totalEntitiesRetrieved = 0;
while (true)
{
CloudTable table = cloudTableClient.GetTableReference("MyTable");
TableQuery<TempEntity> query = (new TableQuery<TempEntity>()).Take(100);
System.Threading.ManualResetEvent evt = new System.Threading.ManualResetEvent(false);
var result = table.BeginExecuteQuerySegmented<TempEntity>(query, token, reqOptions, ctx, (o) =>
{
var response = (o.AsyncState as CloudTable).EndExecuteQuerySegmented<TempEntity>(o);
token = response.ContinuationToken;
int recordsRetrieved = response.Count();
totalEntitiesRetrieved += recordsRetrieved;
Console.WriteLine("Records retrieved in this attempt = " + recordsRetrieved + " | Total records retrieved = " + totalEntitiesRetrieved);
evt.Set();
}, table);
evt.WaitOne();
if (token == null)
{
break;
}
}
}
One thing I noticed is that if I execute a query which returns a dynamic table entity, I'm getting an error related to DateTimeOffset. That's why I ended up creating a temporary entity.
Hope this helps.
Here is a another alternative, but this time in TPL / Task Parallel Library. Full source code is available here
/*
The following overloads of ExecuteQuerySegmentedAsync is executed like this)
*/
CloudTableClient client = acct.CreateCloudTableClient();
CloudTable tableSymmetricKeys = client.GetTableReference("SymmetricKeys5");
TableContinuationToken token = new TableContinuationToken() { };
TableRequestOptions opt = new TableRequestOptions() { };
OperationContext ctx = new OperationContext() { ClientRequestID = "ID" };
CancellationToken cancelToken = new CancellationToken();
List<Task> taskList = new List<Task>();
while (true)
{
Task<TableQuerySegment<DynamicTableEntity>> task3 = tableSymmetricKeys.ExecuteQuerySegmentedAsync(query, token, opt, ctx, cancelToken);
// Run the method
task3.Wait();
token = task3.Result.ContinuationToken;
Console.WriteLine("Records retrieved in this attempt = " + task3.Result.Count ());
if (token == null)
{
break;
}
else
{
// persist token
// token.WriteXml()
}
}
*/
// Overload #4
public static Task<TableQuerySegment<DynamicTableEntity>> ExecuteQuerySegmentedAsync(this CloudTable tbl, TableQuery query, TableContinuationToken continuationToken, TableRequestOptions opt, OperationContext ctx ,CancellationToken token )
{
ICancellableAsyncResult result = null;
if (opt == null && ctx == null)
result = tbl.BeginExecuteQuerySegmented(query, continuationToken, null, tbl);
else
result = tbl.BeginExecuteQuerySegmented(query, continuationToken, opt, ctx, null, tbl);
// Add cancellation token according to guidance from Table Client 2.0 Breaking Changes blog entry
var cancellationRegistration = token.Register(result.Cancel);
return Task.Factory.FromAsync(result, iAsyncResult =>
{
CloudTable currentTable = iAsyncResult.AsyncState as CloudTable;
//cancellationRegistration.Dispose();
return currentTable.EndExecuteQuerySegmented(result);
});
}
This person made some extensions methods for ExecuteQueryAsync that wrap the segmented methods
https://github.com/glueckkanja/tasync/blob/master/StorageExtensions.cs
Here is an alternate implementation of #Gaurav Mantri's code.
public class AzureTableQueryState
{
public CloudTable CloudTable { get; set; }
public TableContinuationToken Token { get; set; }
public ManualResetEvent Evt { get; set; } //i'm guessing that's what this is
//any other variables you were using
public int TotalEntitiesRetrieved { get; set; }
}
// snip....
while (true)
{
// Initialize variables
TableQuery query = (new TableQuery()).Take(100);
AzureTableQueryState queryState = new AzureTableQueryState();
queryState.Evt = new System.Threading.ManualResetEvent(false);
queryState.TotalEntitiesRetrieved = 0;
AsyncCallback asyncCallback = (iAsyncResult) =>
{
AzureTableQueryState state = iAsyncResult.AsyncState as AzureTableQueryState;
var response = state.CloudTable.EndExecuteQuerySegmented(iAsyncResult);
token = response.ContinuationToken;
int recordsRetrieved = response.Results.Count;
state.TotalEntitiesRetrieved += recordsRetrieved;
Console.WriteLine("Records retrieved in this attempt = " + recordsRetrieved + " | Total records retrieved = " + state.TotalEntitiesRetrieved);
state.Evt.Set();
};
// Run the method
var result = tableSymmetricKeys.BeginExecuteQuerySegmented(query, token, opt, ctx, asyncCallback, tableSymmetricKeys);
// Add cancellation token according to guidance from Table Client 2.0 Breaking Changes blog entry
cancelToken.Register((o) => result.Cancel(), null);
queryState.Evt.WaitOne();
if (token == null)
{
break;
}
else
{
// persist token
// token.WriteXml()
}
}