What is a good practice to implement MMVM with WCF Services? The View model will be communicating with the service. So lets say in a view I have 3-4 data display modules. All this information for the modules is coming from different WCF Service calls.
If I do this synchronously, I have a feeling that the data in the view model will take time to load.
I want to do the calls for all these service methods asynchronously with out waiting for the first call to come back. What is good way of doing this?
I think the best way is to call the Service Asynchronously and then assign value on Complete method, like:
class TestViewModel : ViewModelBase
{
private ObservableCollection<string> data;
public ObservableCollection<string> Data
{
get { return data; }
set
{
if (value == data) return;
data = value;
RaisePropertyChanged("Data");
}
}
public TestViewModel()
{
GetDataClient proxy = new GetDataClient();
System.EventHandler<GetDataCompletedEventArgs> Client_GetDataCompleted = null;
Client_GetDataCompleted = (s, e) =>
{
if (e.Error == null && e.Result != null)
{
Data = new ObservableCollection<SelectionItem<string>>(e.Result));
}
proxy.GetDataCompleted -= Client_GetDataCompleted;
};
proxy.GetDataCompleted += Client_GetDataCompleted;
proxy.GetDataAsync();
}
}
Related
Currently I am looking for best practice in handling conditions inside the controller actions in asp.net mvc. For example -
public ActionResult Edit(int Id = 0)
{
var Item = _todoListItemsRepository.Find(Id);
**if (Item == null)
return View("NotFound");
if (!Item.IsAuthorized())
return View("NotValidOwner");**
return View("Edit", Item);
}
The above two conditions marked in bold is used in other actions inside the controller. So, in order not to repeat these conditions in all the actions. I have used the below approach.
[HttpGet]
[Authorize]
[ModelStatusActionFilter]
public ActionResult Edit(int Id = 0)
{
var Item = _todoListItemsRepository.Find(Id);
return View("Edit", Item);
}
public class ModelStatusActionFilterAttribute : ActionFilterAttribute
{
private readonly ITodoListItemsRepository _todoListItemsRepository;
public ModelStatusActionFilterAttribute()
: this(new TodoListItemsRepository())
{
}
public ModelStatusActionFilterAttribute(ITodoListItemsRepository todoListItemsRepository)
{
_todoListItemsRepository = todoListItemsRepository;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
try
{
var Id = Convert.ToInt32(filterContext.RouteData.Values["Id"]);
var Item = _todoListItemsRepository.Find(Id);
if (Item == null)
{
filterContext.Result = new ViewResult() { ViewName = "NotFound" };
}
else if (!Item.IsAuthorized())
{
filterContext.Result = new ViewResult() { ViewName = "NotValidOwner" };
}
}
catch
{
}
}
}
I am unsure if this is the best practice in handling such scenarios. So, could someone please advise ?
Regards,
Ram
usually you don't use action filter for so-called business logic of your web application - this is what the controllers are for. Action filter are rather for the whole stuff which is external to the actual logic - common case is logging, performance measurement, checking if user is authenticated / authorized (I don't think this is your case, although you call IsAuthorized method on the "Item").
Reducing code is generally good thing but in this case, I don't think putting the logic to action is a good way, because you;ve actually made it a bit unreadable, and unreadable code is in my opinon much worse than repeated code.
Also, specifically in your case, for all valid items you actually call the _todoListItemsRepository.Find() twice (for each valid item), which might be costly if this is some webservice call or db lookup.
If the code is just repeated throughout the actions, make a method out of it like:
private View ValidateItem(Item) {
if (Item == null)
return View("NotFound");
if (!Item.IsAuthorized())
return View("NotValidOwner");
return null; }
I inherited a Silverlight 5 application. On the server side, it has a DomainContext (service) with a method marked as
[Invoke]
public void DoIt
{
do stuff for 10 seconds here
}
On the client side, it has a ViewModel method containing this:
var q = Context.DoIt(0);
var x=1; var y=2;
q.Completed += (a,b) => DoMore(x,y);
My 2 questions are
1) has DoIt already been activated by the time I attach q.Completed, and
2) does the return type (void) enter into the timing at all?
Now, I know there's another way to call DoIt, namely:
var q = Context.DoIt(0,myCallback);
This leads me to think the two ways of making the call are mutually exclusive.
Although DoIt() is executed on a remote computer, it is best to attach Completed event handler immediately. Otherwise, when the process completes, you might miss out on the callback.
You are correct. The two ways of calling DoIt are mutually exclusive.
If you have complicated logic, you may want to consider using the Bcl Async library. See this blog post.
Using async, your code will look like this:
// Note: you will need the OperationExtensions helper
public async void CallDoItAndDosomething()
{
this.BusyIndicator.IsBusy = true;
await context.DoIt(0).AsTask();
this.BusyIndicator.IsBusy = false;
}
public static class OperationExtensions
{
public static Task<T> AsTask<T>(this T operation)
where T : OperationBase
{
TaskCompletionSource<T> tcs =
new TaskCompletionSource<T>(operation.UserState);
operation.Completed += (sender, e) =>
{
if (operation.HasError && !operation.IsErrorHandled)
{
tcs.TrySetException(operation.Error);
operation.MarkErrorAsHandled();
}
else if (operation.IsCanceled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(operation);
}
};
return tcs.Task;
}
}
I read that the best practice for using WCF proxy would be:
YourClientProxy clientProxy = new YourClientProxy();
try
{
.. use your service
clientProxy.Close();
}
catch(FaultException)
{
clientProxy.Abort();
}
catch(CommunicationException)
{
clientProxy.Abort();
}
catch (TimeoutException)
{
clientProxy.Abort();
}
My problem is, after I allocate my proxy, I assign event handlers to it and also initialize other method using the proxy:
public void InitProxy()
{
sdksvc = new SdkServiceClient();
sdksvc.InitClusteringObjectCompleted += new EventHandler<InitClusteringObjectCompletedEventArgs>(sdksvc_InitClusteringObjectCompleted);
sdksvc.InitClusteringObjectAsync(Utils.DSN, Utils.USER,Utils.PASSWORD);
sdksvc.DoClusteringCompleted += new EventHandler<DoClusteringCompletedEventArgs>(sdksvc_DoClusteringCompleted);
sdksvc.CreateTablesCompleted += new EventHandler<CreateTablesCompletedEventArgs>(sdksvc_CreateTablesCompleted);
}
I now need to call the InitProxy() method each Time I use the proxy if I want to use it as best practice suggests.
Any ideas on how to avoid this?
There are several options. One option is to write a helper class as follows:
public class SvcClient : IDisposable {
public SvcClient(ICommunicationObject service) {
if( service == null ) {
throw ArgumentNullException("service");
}
_service = service;
// Add your event handlers here, e.g. using your example:
sdksvc = new SdkServiceClient();
sdksvc.InitClusteringObjectCompleted += new EventHandler<InitClusteringObjectCompletedEventArgs>(sdksvc_InitClusteringObjectCompleted);
sdksvc.InitClusteringObjectAsync(Utils.DSN, Utils.USER,Utils.PASSWORD);
sdksvc.DoClusteringCompleted += new EventHandler<DoClusteringCompletedEventArgs>(sdksvc_DoClusteringCompleted);
sdksvc.CreateTablesCompleted += new EventHandler<CreateTablesCompletedEventArgs>(sdksvc_CreateTablesCompleted);
}
public void Dispose() {
try {
if( _service.State == CommunicationState.Faulted ) {
_service.Abort();
}
}
finally {
_service.Close();
}
}
private readonly ICommunicationObject _service;
}
To use this class write the following:
var clientProxy = new YourClientProxy();
using(new SvcClient(clientProxy)) {
// use clientProxy as usual. No need to call Abort() and/or Close() here.
}
When the constructor for SvcClient is called it then sets up the SdkServiceClient instance as desired. Furthermore the SvcClient class cleans up the service client proxy as well aborting and/or closing the connection as needed regardless of how the control flow leaves the using-block.
I don't see how the ClientProxy and the InitProxy() are linked but if they are linked this strong I'd move the initialization of the ClientProxy to the InitProxy (or make a method that initializes both) so you can control both their lifespans from there.
A wcf service accessing an SQL database:
private void GetImagesDataFromDB(int imageIndex, int **extraParam**)
{
ServiceReference1.DbServiceClient webService =
new ServiceReference1.DbServiceClient();
webService.GetSeriesImagesCompleted += new EventHandler<ServiceReference1.GetSeriesImagesCompletedEventArgs>(webService_GetSeriesImagesCompleted);
webService.GetSeriesImagesAsync(imageIndex);
}
the GetImageSeriesCompleted EventHandler is here:
void webService_GetSeriesImagesCompleted(object sender,
TheApp.ServiceReference1.GetSeriesImagesCompletedEventArgs e)
{
if (e.Result != null)
{
if (**extraParam** == 1)
{
lstImages = e.Result.ToList();
}
else
{
// do something else
}
}
}
The service itself is like this:
public List<Image> GetSeriesImages(int SeriesId)
{
DataClassDataContext db = new DataClassDataContext();
var images = from s in db.Images
where s.SeriesID == SeriesId
select s;
return images.ToList();
}
What is the best way to pass the extraParam to the service completed EventHandler? I need this to direct my service return to a proper UI control.
Thanks.
You've probably figured this out by now, but the webService.GetSeriesImagesAsync() call has a second overload, namely, webService.GetSeriesImagesAsync(int seriesId, object userState). That second parameter will get passed into the callback as e.UserState. A good pattern is actually to pass a lambda callback as the userstate, and execute that in the webService_GetSeriesImagesCompleted() method.
If you call a web service from Silverlight like this:
MyServiceClient serviceClient = new MyServiceClient();
void MyMethod()
{
serviceClient.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(serviceClient_GetDataCompleted);
serviceClient.GetDataAsync();
// HOW DO I WAIT/JOIN HERE ON THE ASYNC CALL, RATHER THAN BEING FORCE TO LEAVE THIS METHOD?
}
I would rather wait/join with the asych service thread inside "MyMethod" rather than leaving "MyMethod" after calling "GetDataAsync", what is the best way to do this?
Thanks,
Jeff
No you cannot do this way. You will end up in a deadlock. GetDataCompleted is called by the mainthreed. The same threed thait is waiting in WaitOne.
I have to ask; why? The point is to provide your user with a fluid experience and waiting on a web service call will not necessarily do that. I suppose you want the full block of content to load before the Silverlight control loads. In that case, I would turn to caching the content rather than forcing the client to wait indefinitely.
To do this you would use a ManualResetEvent in your class (class level variable) and then wait on it.
void MyMethod()
{
wait = new ManualResetEvent(false);
// call your service
wait.WaitOne();
// finish working
}
and in your event handler code
void serviceClient_GetDataCompleted(...)
{
// Set values you need from service
wait.Set();
}
You could also use a lambda and closure to get similar behavior:
serviceClient.GetDataCompleted += (s,e) =>
{
// Your code here
};
serviceClient.GetDataAsync();
If you had a base class provide the mechanics of building a WCF channel, it could then be used to build the BeginX / EndX methods for a async call.
public class ServiceFooCoordinator : CoordinatorBase<IServiceFoo>
{
public IAsyncResult BeginMethodFoo ()
{
IAsyncResult ar = null;
IServiceFoo channel = null;
channel = _factory.GetChannel();
Begin( channel, () => ar = channel.BeginMethodFoo( null, channel ) );
return ar;
}
public Bar[] EndMethodFoo ( IAsyncResult ar )
{
IServiceFoo channel = null;
channel = _factory.GetChannel();
return channel.EndMethodFoo( ar );
}
}
Which can then be used in a method:
ServiceFooCoordinator _coordinator;
var asyncResult = _coordinator.BeginMethodFoo();
try
{
var result = _coordinator.EndMethodFoo( asyncResult );
}
catch ( Exception )
{ }
Which gets you your asynchronous call in a sychronous manner.