Proper usage for MvxFileDownloadCache.Clear - c#

I'm trying to cleanup unwanted http image data that I have loaded via MvxImageViewLoader.
I've found the function clear in the FileDownloadCache which seems to do what I need.
var downloadCache = Mvx.Resolve<IMvxFileDownloadCache>();
downloadCache.Clear(_imageChart1ViewLoader.ImageUrl);
Periodically it calls a function (once a second by the looks of it) which deletes the files in the private list
private readonly List<string> _toDeleteFiles = new List<string>();
Clear adds the image by url to that list.
Except once I call this function I'm still able to see the image. I.e. it stays in memory.
So really I need to know where is a good place to be calling Clear and am I using it in the correct way. Currently I call it every time I exit my DetailView which downloads an image from a URL.
MvvmCross v4.2.2 (latest)

Related

DiskCache plugin strategy for e-mail images with tracking

Currently we're sending thousands of e-mails every day to our customers. An e-mail contains an image url with a specific Message ID. When the user downloads that image, I mark that message as opened in the database. For example:
http://cdn.mydomain.com/companylogos/{guid}.png?h=100&w=100&messageid=123
At this moment, every image request requires me to get the byte[ ] of the image from cache. Resize it. And Return it. This all takes place in a httphandler.
I would like to take the advantage of ImageResizer.net and the Disk Cache plugin. However, I still need to get the MessageId-querystring parameter. So I'm thinking about this solution.
Extending HttpModule
public class CustomInterceptModule : InterceptModule
{
protected override void HandleRequest(HttpContext context, HttpModuleRequestAssistant ra, IVirtualFile vf)
{
var messageIdParam = context.Request.QueryString["messageId"];
int messageId;
if (!string.IsNullOrWhiteSpace(messageIdParam) && int.TryParse(messageIdParam, out messageId))
{
// Do something with the ID here
}
base.HandleRequest(context, ra, vf);
}
}
Does this method still creates high performance, disk cached, results? Or am I interrupting this because I'm extending the HttpModule and I'm adding my own logic in there.
If your goal is to increase performance and/or reduce the work you server needs to do, I would suggest that you should not use a full-size image for tracking purposes, but instead use an invisible 1x1.png image to track whether or not a message was opened, and simply use a standard img url that everybody shares (that can be distributed on a CDN and/or cached) for the image that people actually see.
You can simply add an event handler to ImageResizer.Config.Current.Pipeline.AuthorizeImage - this will be called for all image requests, and is less brittle than subclassing the HttpModule.

Ria Framework DomainDataSource MoveToNextPage, MoveToPage, MoveToFirstPage doesn't move pages

I'm attempting to write a save results extension to the DomainDatasourceView.
I can successfully write the contents of the current page of results but when I attempt to call MoveToNextPage(), the PageIndex stays current. MSDN docs regarding this don't provide any details other than MoveToNextPage returns a bool is it successfully moves to the next page.
The following sample code results in an infinite loop, and the Current page is never changed.
private void WriteResults(DomainDataSourceView resultsview)
{
StringBuilder csvdata = new StringBuilder();
... Do Work on current page ...
if(resultsview.CanChangePage && resultsview.MoveToNextPage())
{
csvdata.Append(WriteResults(resultsview));
}
}
Do I need to listen for the PageChanged Event to continue Saving Results?
Do I need to call Load on the DomainDataSource for each page?
The MSDN Docs on DomainDataSourceView doesn't go into too much details on this subject.
[Edit]
After playing around some more, I was able to determine that the Move...Page commands do call the the DomainDataSource Load operation, however its another Async call, so any consecutive work that needs to be done on the loaded pages, should be handled accordingly.

How to reload data in UITableView in MonoTouch?

In my application I display data from a online web service into several UITableViews. I have added several ways for the user to update the data, but the TableView.ReloadData() method does not seem to work.
When the user calls for an update, I get a new set of data from the server, pass it to the UITableViewSource instance that is attached to the UITableViewController and then call the ReloadData() method, which unfortunately does not in fact reload the data. Only after I return to the main screen and then go back to the table view (because it is already created, I just display the instance that already exists) does the new data show up in the tableview.
What am I doing wrong? I tried creating a new instance of the UITableViewSource when updating the data, but that does not help either.
Here is the code for loading data into the tableview (I reuse it for any event that requires data to be loaded into it):
dataControl.GetList(Tables.UPDATES)); //gets data from the server and passes it to the SQL database
Source source = GetSource(theType.Name, theType, groups); //creates a new source loaded with the data
Updates.TableView.Source = source;
Updates.TableView.AllowsSelection = false;
Updates.TableView.ReloadData();
This code is of course executed in a separate thread that invokes on the main thread.
Basically the same code is called when the user asks for an update(an animation is played while the background thread works).
Pavel is correct - try the following to see if it works:
InvokeOnMainThread(delegate{
Updates.TableView.Source = source;
Updates.TableView.AllowsSelection = false;
Updates.TableView.ReloadData();
});
In future, whenever you're dealing with something that will change the UI currently shown, you will need to ensure that it takes place on the main thread (also known as the GUI thread). This InvokeOnMainThread is a method from NSObject so can be called like above in all UIViews / UIViewControllers etc - you can also call it from an entirely C# class using:
MonoTouch.UIKit.UIApplication.SharedApplication.InvokeOnMainThread(delegate{ /*code here*/ });
You say it is done in a different thread, could it be that the worker thread cannot call the UI thread? I.e. you should call the ReloadData via delegate to be sure it gets called in the UI thread and not "only" in the worker thread, as it might interlock and never get actually called (happened to me in a different scenario).
I also ran into this problem and found this question. Using some of the hint here, I finally got it work by reseting the DataSource and call ReloadView().
tableView.DataSource = new MyDataSource(...);
tableView.RelaodData();
From my testing, it doesn't make different if I wrap the ReloadView() within the InvokeOnMainThread or not. Well, that's maybe because I'm not using worker thread.
It is strange that in another case I could refresh the table view by simply calling ReloadData() in ViewDidAppear(). The only difference is that the above case is the refresh within the same view.
You can reload datain viewwillappear() or set load data code in viewwillappear().
For googlers:
I had the same issues. This is what fixed it for me
InvokeOnMainThread(delegate {
myTableView.Source = new TableViewSource();
myTableView.ReloadData();
this.View.SetNeedsDisplay();
});

Calling a webservice async

Long post.. sorry
I've been reading up on this and tried back and forth with different solutions for a couple of days now but I can't find the most obvious choice for my predicament.
About my situation; I am presenting to the user a page that will contain a couple of different repeaters showing some info based on the result from a couple of webservice calls. I'd like to have the data brought in with an updatepanel (that would be querying the result table once per every two or three seconds until it found results) so I'd actually like to render the page and then when the data is "ready" it gets shown.
The page asks a controller for the info to render and the controller checks in a result table to see if there's anything to be found. If the specific data is not found it calls a method GetData() in WebServiceName.cs. GetData does not return anything but is supposed to start an async operation that gets the data from the webservice. The controller returns null and UpdatePanel waits for the next query.
When that operation is complete it'll store the data in it's relevant place in the db where the controller will find it the next time the page asks for it.
The solution I have in place now is to fire up another thread. I will host the page on a shared webserver and I don't know if this will cause any problems..
So the current code which resides on page.aspx:
Thread t = new Thread(new ThreadStart(CreateService));
t.Start();
}
void CreateService()
{
ServiceName serviceName = new ServiceName(user, "12345", "MOVING", "Apartment", "5100", "0", "72", "Bill", "rate_total", "1", "103", "serviceHost", "password");
}
At first I thought the solution was to use Begin[Method] and End[Method] but these don't seem to have been generated. I thought this seemed like a good solution so I was a little frustrated when they didn't show up.. is there a chance I might have missed a checkbox or something when adding the web references?
I do not want to use the [Method]Async since this stops the page from rendering until [Method]AsyncCompleted gets called from what I've understood.
The call I'm going to do is not CPU-intensive, I'm just waiting on a webService sitting on a slow server, so what I understood from this article: http://msdn.microsoft.com/en-us/magazine/cc164128.aspx making the threadpool bigger is not a choice as this will actually impair the performance instead (since I can't throw in a mountain of hardware).
What do you think is the best solution for my current situation? I don't really like the current one (only by gut feeling but anyway)
Thanks for reading this awfully long post..
Interesting. Until your question, I wasn't aware that VS changed from using Begin/End to Async/Completed when adding web references. I assumed that they would also include Begin/End, but apparently they did not.
You state "GetData does not return anything but is supposed to start an async operation that gets the data from the webservice," so I'm assuming that GetData actually blocks until the "async operation" completes. Otherwise, you could just call it synchronously.
Anyway, there are easy ways to get this working (asynchronous delegates, etc), but they consume a thread for each async operation, which doesn't scale.
You are correct that Async/Completed will block an asynchronous page. (side note: I believe that they will not block a synchronous page - but I've never tried that - so if you're using a non-async page, then you could try that). The method by which they "block" the asynchronous page is wrapped up in SynchronizationContext; in particular, each asynchronous page has a pending operation count which is incremented by Async and decremented after Completed.
You should be able to fake out this count (note: I haven't tried this either ;) ). Just substitute the default SynchronizationContext, which ignores the count:
var oldSyncContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var serviceName = new ServiceName(..);
// Note: MyMethodCompleted will be invoked in a ThreadPool thread
// but WITHOUT an associated ASP.NET page, so some global state
// might be missing. Be careful with what code goes in there...
serviceName.MethodCompleted += MyMethodCompleted;
serviceName.MethodAsync(..);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSyncContext);
}
I wrote a class that handles the temporary replacement of SynchronizationContext.Current as part of the Nito.Async library. Using that class simplifies the code to:
using (new ScopedSynchronizationContext(new SynchronizationContext()))
{
var serviceName = new ServiceName(..);
// Note: MyMethodCompleted will be invoked in a ThreadPool thread
// but WITHOUT an associated ASP.NET page, so some global state
// might be missing. Be careful with what code goes in there...
serviceName.MethodCompleted += MyMethodCompleted;
serviceName.MethodAsync(..);
}
This solution does not consume a thread that just waits for the operation to complete. It just registers a callback and keeps the connection open until the response arrives.
You can do this:
var action = new Action(CreateService);
action.BeginInvoke(action.EndInvoke, action);
or use ThreadPool.QueueUserWorkItem.
If using a Thread, make sure to set IsBackground=true.
There's a great post about fire and forget threads at http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/10/make-methods-fire-and-forget-with-postsharp.aspx
try using below settings
[WebMethod]
[SoapDocumentMethod(OneWay = true)]
void MyAsyncMethod(parameters)
{
}
in your web service
but be careful if you use impersonation, we had problems on our side.
I'd encourage a different approach - one that doesn't use update panels. Update panels require an entire page to be loaded, and transferred over the wire - you only want the contents for a single control.
Consider doing a slightly more customized & optimized approach, using the MVC platform. Your data flow could look like:
Have the original request to your web page spawn a thread that goes out and warms your data.
Have a "skeleton" page returned to your client
In said page, have a javascript thread that calls your server asking for the data.
Using MVC, have a controller action that returns a partial view, which is limited to just the control you're interested in.
This will reduce your server load (can have a backoff algorithm), reduce the amount of info sent over the wire, and still give a great experience to the client.

ASMX webservices with Silverlight Async confusion

I have a silverlight 4 web app that needs to communicate with a server by accessing the ASMX web service on the server.
I have a list(yes, the array), of objects that I need to send(one by one) as a parameter to the service. However looping through the list and running the method(objecttosend); will not work because I need to send then one after another and Silverlight seems to only support Async(presumably to not lockup interface - makes sense).
So I tried this:
public void SendNextPart()
{
if (partsToSend.Count > 0)
{
Part thisPart = partsToSend.Dequeue();
fuWS.createPartCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(fuWS_createPartCompleted);
fuWS.createPartAsync(thisPart);
}
}
Queue<Part> partsToSend = new Queue<Part>();
void fuWS_createPartCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
SendNextPart();
}
Which, as far as I can understand it, will check to see if the List has parts to send, then run the webservice(called fuWS) method and delete that part from the partsToSend List. Once it gets the completed event it should then run the SendNextPart method again and send the next part.
However what is happening(picked this up by watching HTTPwatch) is that it sends the first part, then after that is sends 2 parts at once and then after that more and more, all at once. Almost as if it is receiving the completed event before it has actually sent to the server and run the method successfully.
Please help, this is bugging the hell out of me, and it completely breaks what I need to do :'(
I don't see the SendNextBuffer method that you're calling in the web service callback event handler. But in any case, at best your code has a race condition. If the web service completes and returns before the partsToSend.RemoveAt line is executed (theoretically possible) then you could be making the next request before you've removed the one you just sent.
So first, you should check to make sure you've included all the code in your example unless you meant for SendNextBuffer to say SendNextPart.
Secondly, you should move the partsToSend.RemoveAt line before the web service call.
Finally, you should probably change the partsToSend list into a Queue<Part> (first in, first out) or Stack<Part> (last in, first out) instead since that is what you're using it as.
Ok, so after using Debug.WriteLine, I realized that I was being an idiot.
Check out this line:
fuWS.createPartCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(fuWS_createPartCompleted);
What this was doing was adding a new event handler every time it had to send a new part. So the second part sending now had two callback then the third would have more and so on increasing exponentially.

Categories