I have an api method that gets a lot of data in the application start up.
90% of the data is relevant for all application users and the other 10% need to be changed by the user id and his environment and app version.
To avoid calling the first method every time user connect and make the start up slower,
i added : AspNetCacheProfile attribute.
But in this situation i can`t use the user id, environment and version at this method
because it is the data of the first user called the method.
So,
I added new method (the second one) and set the AspNetCacheProfile attribute above it
and called this method from the first one.
This way i can cache the general data and update the other 10% each call.
I just wanted to know .. will it work ?
I was not sure because the second method is not called directly, it is called from the first one.
[WebGet(UriTemplate = "/GetData")]
public APIResponse GetData()
{
try
{
MyCachedResponse = GetMyCachedResponse();
foreach (var info in data.info)
{
if (Something)
{
// Update some specific values inside
}
}
AppState.CurrentResponse.Data = data;
}
return AppState.CurrentResponse;
}
[AspNetCacheProfile("OneMinuteCaching")]
private MyCachedResponse GetMyCachedResponse()
{
return new MyCachedResponse(categories);
}
Related
I'm trying to do a get all from an Azure service and it returns an AsyncPageable. According to the doc it says
A collection of values that may take multiple service requests to iterate over.
Does that mean that it is equal to doing the request for a single item multiple times with a loop?
If a service call returns multiple values in pages it would return Pageable<T>/AsyncPageable<T> as a result. Check out Consuming Service Methods Returning AsyncPageable.
To get more clarity, have a look at below:
This shows control over receiving pages of values from the service use AsyncPageable<T>.AsPages method:
// call a service method, which returns AsyncPageable<T>
AsyncPageable<SecretProperties> response = client.GetPropertiesOfSecretsAsync();
await foreach (Page<SecretProperties> page in response.AsPages())
{
// enumerate through page items
foreach (SecretProperties secretProperties in page.Values)
{
Console.WriteLine(secretProperties.Name);
}
// get continuation token that can be used in AsPages call to resume enumeration
Console.WriteLine(page.ContinuationToken);
}
If your project doesn't have C# 8.0 enabled you can still iterate over AsyncPageable using a while loop:
// call a service method, which returns AsyncPageable<T>
AsyncPageable<SecretProperties> response = client.GetPropertiesOfSecretsAsync();
IAsyncEnumerator<SecretProperties> enumerator = response.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
SecretProperties secretProperties = enumerator.Current;
Console.WriteLine(secretProperties.Name);
}
}
finally
{
await enumerator.DisposeAsync();
}
Check out Azure.Core Response samples to understand more about this.
To change page size, you can use pageSizeHint parameter to AsPages method.
I have xamarin forms application. I use odata simple client to manipulate app database.
I am trying to add data to many-to-many tables.
This is my first entity
public class Genre : BaseGenre
{
List<User> Users { get; set; }
}
And my other one
public class User : BaseUser
{
List<Genre> Genres { get; set; }
}
And this the function I am trying to link them
public async void AddGenresAsnyc(User u, List<Genre> Genres)
{
u.Genres = Genres;
try {
//await client.For<User> (CollectionName).Key(u).LinkEntryAsync(us => us.Genres, Genres);
await client.For<User> (CollectionName).Key(u.id).Set(u).UpdateEntriesAsync();
} catch (Exception e) {
Exception ex = e;
}
}
The first one, linkentryasync throws the exception
Number of parameter does not match expected count.
And the second one throws
Linked collection for type [Genre] not found
Any help would be great. I am stuck at work. Thanks in advance.
One immediate thing that you need to change is make properties Genre.Users and User.Genres public. Simple.OData.Client uses reflection to assign property values and is not capable of assigning values for private properties/fields. I tested your code with the schema you sent me and as long as the properties were public, the request went through.
Regarding the next example (using LinkEntryAsync), if you want to update links in a single call, you should use UpdateEntryAsync, because LinkEntryAsync does it for a single link. So either use:
var user = await client.For<User>("ApiUser").Key(1).FindEntryAsync();
user.Genres = genres;
await client.For<User>("ApiUser").Key(user).Set(user).UpdateEntryAsync();
or
foreach (var genre in genres)
{
await client.For<User>("ApiUser").Key(user).LinkEntryAsync(genre);
}
The first operation could have been written in a more efficient way:
await client.For<User>("ApiUser").Key(1).Set(new {Genres = genres}).UpdateEntryAsync();
That will generate HTTP PATCH instead of PUT with only Genres updated, but it looks like your OData service requires all mandatory properties to be sent on the entity being updated, so this won't work.
Last but not least: get the latest version (4.9.1) of Simple.OData.Client. It has a fix that is important for your scenario.
UPDATE. I tested your OData service, and it doesn't seem to have a proper support for addressing links. For example, if I test sample OData service, I can execute requests like http://services.odata.org/V4/OData/%28S%28ygi3rwu514y0a4ooybn3d1gc%29%29/OData.svc/Products%284002%29/Categories/$ref (note $ref segment that addresses Caterogories link so this URI can be used to post link updates). But if I execute request http://{your_service_uri}/ApiUsers%281%29/Genres/$ref then I get an error "No HTTP resource was found that matches the request URI 'http://partymag.azurewebsites.net/ApiUsers(1)/Genres/$ref'." As long as this link doesn't work on a server side you won't be able to use LinkEntryAsync or UnlinkEntryAsync but you can still use UpdateEntryAsync as I showed above.
UPDATE2. The version that uses UpdateEntryAsync executes fine but service doesn't update links, here is the result from Fiddler:
Generated URI: PATCH http://{your_service_uri}/ApiUsers(1)
PATCH payload:
{ "#odata.type":"#PMWeb.Models.Models.User",
"id":1,"Name":"Ege",
"LastName":"Aydin",
"Email":"{removed}",
"Password":"{removed}",
"Genres#odata.bind":[
"http://{your_service_uri}/Genre(31)","http://{your_service_uri}/Genre(32)"
]
}
Response:
{
"#odata.context":"http://{your_service_uri}/$metadata#ApiUsers/$entity",
"id":1,
"Name":"Ege",
"LastName":"Aydin",
"Email":"{removed}",
"Password":"{removed}"
}
If I now check the content of User's genres, they are the same. Since generated payload is correct and the service accepted it, it must be something on the server that is not executed properly.
I want to make use of the saveChangesAsync in a synchronous function. The situation I want to use this in is for example.
public string getName(int id)
{
var db = new dbContext();
String name= db.names.find(id);
db.log.add(new Log("accessed name");
db.savechangesAsync();
return name;
}
So basically I dont care when the log is actually saved to the database, I just dont want it to slow down my getName function. I want the getname to return and then the log can be saved to the database any time after / during that.
How would I go about achieving this? Nothing is dependant on the time that the log is submitted, So it can take 2 min for all I care.
I have come up with another solution:
private async void UpdateLastComms(string _id)
{
int id = Int32.Parse(_id);
using (var db = new dbContext())
{
db.Devices.Where(x => x.UserId == id).FirstOrDefault().LastComms = DateTime.Now;
await db.SaveChangesAsync();
}
}
and I then can call this function like so UpdateLastComms("5");
How will the this compare to the first and will it execute as I think?
The problem with "fire and forget" methods like this is error handling. If there is an error saving the log to the database, is that something you want to know about?
If you want to silently ignore errors, then you can just ignore the returned task, as in your first example. Your second example uses async void, which is dangerous: if there is a database write error with your second example, the default behavior is to crash the application.
If you want to handle errors by taking some action, then put a try/catch around the body of the method in your second example.
Consider the following scenario:
You are in a view with a lot of data (i.e. a view with a ListBox full of items). The data has been downloaded from an external data source, and contains a lot of properties that's not exposed in the current view. You'd like to make a detailed view to show this information.
When you have wired up your ListBoxItems to navigate to your new view with an attached "id=" in the navigation URL, you can easily query your data source (i.e. a REST-service) for the data again with the given ID-parameter. But the data is already available on the parent view - so how could you send this data in some way to the next view - skipping another call to your data source and speeding up your application?
I'm sure there are several ways to do this, but this is the easiest way to accomplish it without using any external libraries.
Create a Utility class - NavigationUtility (or whatever) - and implement the following structure:
public static class NavigationUtility
{
// The object to send
private static object passedObject = null;
// A method to fetch the object
public static object GetObject() {
// implementation below
}
// Utility method to check if an object was passed
public static bool HasObject()
{
return passedObject != null;
}
// A method to navigate to a page
public static void Navigate(string url, object obj = null)
{
// implementation below
}
}
This is the interface you'll be implementing. It has a private variable that keeps your object safe while transitioning between views, and methods for both navigating and fetching the information sent.
Now there are a few things you need to consider in order to implement this in a correct way.
You must only be able to fetch the passed object once - otherwise careless use can make the wrong object show up in the wrong part of the program.
In order to use this instead of the NavigationService.Navigate(Uri) method for navigating throughout your application, it must also be able to handle situations where no object needs to be sent.
So let's look at the first method in our interface - GetObject - and how it's implemented:
public static object GetPassedObject()
{
var obj = passedObject;
passedObject = null;
return obj;
}
As you see, we take care of requirement #1 easily by nulling out the internal passedObject for each time the property is fetched. This would work in the following way (in the receiving view):
NavigationUtility.HasObject(); // returns true if an object was sent
var data = NavigationUtility.GetObject() as SomeModel; // fetches the object and casts it
NavigationUtility.HasObject(); // returns false always;
Now to the funny bit - implementing the Navigate-method:
public static void Navigate(string url, object obj = null)
{
// Saves the object
passedObject = obj;
if( url != null && url.length > 0 )
{
// Creates the Uri-object
Uri uri = new Uri(url, UriKind.Relative);
// Navigates the user (notice the funky syntax - so that this can be used from any project
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(uri);
}
}
And that's it!
with the help creating your data with application object you can easily navigate your object data to another view when you making call of REST-service be sure that your data will set to your application object too
you can also use json object and set it to Any global object
I have a silverlight 5 app that depends on several asynchronous calls to web services to populate the attributes of newly created graphics. I am trying to find a way to handle those asynchronous calls synchronously. I have tried the suggestions listed in this article and this one. i have tried the many suggestions regarding the Dispatcher object. None have worked well, so I am clearly missing something...
Here is what I have:
public partial class MainPage : UserControl {
AutoResetEvent waitHandle = new AutoResetEvent(false);
private void AssignNewAttributeValuesToSplitPolygons(List<Graphic> splitGraphics)
{
for (int i = 0; i < splitGraphics.Count; i++)
{
Graphic g = splitGraphics[i];
Thread lookupThread1 = new Thread(new ParameterizedThreadStart(SetStateCountyUtm));
lookupThread1.Start(g);
waitHandle.WaitOne();
Thread lookupThread2 = new Thread(new ParameterizedThreadStart(SetCongressionalDistrict));
lookupThread1.Start(g);
waitHandle.WaitOne();
}
private void SetStateCountyUtm(object graphic)
{
this.Dispatcher.BeginInvoke(delegate() {
WrapperSetStateCountyUtm((Graphic)graphic);
});
}
private void WrapperSetStateCountyUtm(Graphic graphic)
{
GISQueryEngine gisQEngine = new GISQueryEngine();
gisQEngine.StateCountyUtmLookupCompletedEvent += new GISQueryEngine.StateCountyUtmLookupEventHandler(gisQEngine_StateCountyUtmLookupCompletedEvent);
gisQEngine.PerformStateCountyUtmQuery(graphic.Geometry, graphic.Attributes["clu_number"].ToString());
}
void gisQEngine_StateCountyUtmLookupCompletedEvent(object sender, StateCountyUtmLookupCompleted stateCountyUtmLookupEventArgs)
{
string fred = stateCountyUtmLookupEventArgs.
waitHandle.Set();
}
}
public class GISQueryEngine
{
public void PerformStateCountyUtmQuery(Geometry inSpatialQueryGeometry, string cluNumber)
{
QueryTask queryTask = new QueryTask(stateandCountyServiceURL);
queryTask.ExecuteCompleted += new EventHandler<QueryEventArgs>(queryTask_StateCountyLookupExecuteCompleted);
queryTask.Failed += new EventHandler<TaskFailedEventArgs>(queryTask_StateCountyLookupFailed);
Query spatialQueryParam = new ESRI.ArcGIS.Client.Tasks.Query();
spatialQueryParam.OutFields.AddRange(new string[] { "*" });
spatialQueryParam.ReturnGeometry = false;
spatialQueryParam.Geometry = inSpatialQueryGeometry;
spatialQueryParam.SpatialRelationship = SpatialRelationship.esriSpatialRelIntersects;
spatialQueryParam.OutSpatialReference = inSpatialQueryGeometry.SpatialReference;
queryTask.ExecuteAsync(spatialQueryParam, cluNumber);
}
//and a whole bunch of other stuff i can add if needed
}
If I leave the 'waitHandle.WaitOne()' method uncommented, no code beyond that method is ever called, at least that I can see with the step through debugger. The application just hangs.
If I comment out the 'waitHandle.WaitOne()', everything runs just fine - except asynchronously. In other words, when the app reads the Attribute values of the new graphics, those values may or may not be set depending on how quickly the asynch methods return.
Thanks for any help.
It's going to be rather difficult to work through a problem like this as there are a few issues you'll need to address. SL is by nature asynch so forcing it to try and work synchronously is usually a very bad idea. You shouldn't do it unless it's absolutely necessary.
Is there a reason that you cannot wait for an async. callback? From what I see you appear to be making two calls for every state that is being rendered. I'm guessing the concern is that one call must complete before the second is made? In scenarios like this, I would kick off the first async call, and in it's response kick off the second call passing along the result you'll want to use from the first call. The second call response updates the provided references.
However, in cases where you've got a significant number of states to update, this results in a rather chatty, and difficult to debug set of calls. I'd really be looking at creating a service call that can accept a set of state references and pass back a data structure set for the values to be updated all in one hit. (or at least grouping them up to one call per state if the batch will be too time consuming and you want to render/interact with visual elements as they load up.)