How to extend a WCF Data Services context class on clientside? - wcf

I'm using the following code to extend my Service-Context with a FullName on clientside:
public partial class Customer {
public string FullName
{
get
{
return string.Concat(LastName, (FirstName == "" ? "" : ", "), FirstName);
}
}
}
That works fine, until it comes to the point where I need to Add a new Object to the Context with the AddOBject Method. It throws an Exception. When I remove the FullName extension th AddObject method saves the new Object to the database.
What is the best way to extend my Context and still make it Update- and Insertable?
Edit: The DataServiceRequest Exception:
System.Data.Services.Client.DataServiceRequestException was not handled by user code.
HResult=-2146233079
Message=Fehler beim Verarbeiten dieser Anforderung.
Source=Microsoft.Data.Services.Client.WindowsStore
StackTrace:
at System.Data.Services.Client.SaveResult.HandleResponse()
at System.Data.Services.Client.BaseSaveResult.EndRequest()
at System.Data.Services.Client.DataServiceContext.EndSaveChanges(IAsyncResult asyncResult)
at Pointsale.Client.Service.PointsaleEntities.<SaveChanges>b__0(IAsyncResult asResult) in c:\Users\Jan\Desktop\pointsale_worksapce\pointsale.client\Helper\TenantHelper.cs:line 98
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
InnerException: System.Data.Services.Client.DataServiceClientException
HResult=-2146233079
Message=<?xml version="1.0" encoding="utf-8"?><m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"><m:code /><m:message xml:lang="de-DE">Error occurred while processing this request.</m:message></m:error>
StatusCode=400
InnerException:
And the SaveChanges method overide to get it async:
public async Task<DataServiceResponse> SaveChanges()
{
var queryTask = Task.Factory.FromAsync(this.BeginSaveChanges(null, null), asResult =>
{
var result = this.EndSaveChanges(asResult);
return result;
});
return await queryTask;
}
On EndSaveChanges the error occures.

Related

Find invalid values from JsonReaderException in Json.Net

I'm running the code below to purposely throw JsonReaderException. It correctly gives the exception message of "Could not convert string to boolean: aaa. Path 'Active', line 3, position 17."
Is there any way to get the value that has failed the validation directly from the JsonReaderException so I don't have to parse the exception message?
string json = #"{
'Email': 'james#example.com',
'Active': 'aaa',
'CreatedDate': '2013-01-20T00:00:00Z',
'Roles': [
'User',
'Admin'
]
}";
try
{
Account account = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(account.Email);
}
catch (JsonReaderException exc)
{
// Do Something
}
It appears that the offending value is not saved as a property in JsonReaderException. The only possible location for this value would be the Exception.Data dictionary, however Json.NET does not add anything here.
However, with some work you can leverage Json.NET's serialization error event handling functionality to directly access the bad value at the time the exception is thrown. First, define the following helper method and ErrorEventArgs subtype:
public class ErrorAndValueEventArgs : Newtonsoft.Json.Serialization.ErrorEventArgs
{
public object ReaderValue { get; } = null;
public ErrorAndValueEventArgs(object readerValue, object currentObject, ErrorContext errorContext) : base(currentObject, errorContext)
{
this.ReaderValue = readerValue;
}
}
public static partial class JsonExtensions
{
public static TRootObject Deserialize<TRootObject>(string json, EventHandler<ErrorAndValueEventArgs> error, JsonSerializerSettings settings = null)
{
using (var sr = new StringReader(json))
using (var jsonReader = new JsonTextReader(sr))
{
var serializer = JsonSerializer.CreateDefault(settings);
serializer.Error += (o, e) => error(o, new ErrorAndValueEventArgs(jsonReader.Value, e.CurrentObject, e.ErrorContext));
return serializer.Deserialize<TRootObject>(jsonReader);
}
}
}
Now you will be able to access the value of JsonReader.Value at the time the exception was thrown:
object errorValue = null;
try
{
Account account = JsonExtensions.Deserialize<Account>(json, (o, e) => errorValue = e.ReaderValue);
Console.WriteLine(account.Email);
}
catch (JsonException exc)
{
// Do Something
Console.WriteLine("Value at time of {0} = {1}, Data.Count = {2}.", exc.GetType().Name, errorValue, exc.Data.Count);
// Prints Value at time of JsonReaderException = aaa, Data.Count = 0.
}
Notes:
Since you must manually create your own JsonTextReader, you will need to have access to the JSON string (or Stream) for this approach to work. (This is true in the example shown in your question.)
A similar technique for capturing additional error information is shown in JsonSerializationException Parsing.
You might want to enhance ErrorAndValueEventArgs to also record JsonReader.TokenType. In cases where the reader is positioned at the beginning of a container (object or array) at the time an exception is thrown, JsonReader.Value will be null.
Demo fiddle here.

No MediaTypeFormatter is available to read an object of type 'FineUpload' from content with media type 'multipart/form-data'

I am using the FineUploader control with ASP.NET MVC 4 with Knockout.js and require.js. I have gotten the control to load properly without any errors, but when I click upload there is error uploading a file. Is there an extra step I am missing?
This is example I am trying to follow:
FineUploader Example
Here is the error captured for fiddler:
{"Message":"An error has occurred.","ExceptionMessage":"No MediaTypeFormatter is available to read an object of type 'FineUpload' from content with media type 'multipart/form-data'.","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)\r\n at System.Web.Http.Controllers.HttpActionBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(HttpParameterBinding parameterBinder)\r\n at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()\r\n at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken)"}
Here is the knockout.bindings.js file that loads the control:
knockout.bindinds.js
define('knockout.bindings', ['ko', 'moment', 'fu', 'toastr'], function (ko, moment, fu, toastr) {
ko.utils.contains = function (string, startsWith) {
string = string || "";
if (startsWith.length > string.length) return false;
return string.toLowerCase().indexOf(startsWith.toLowerCase()) >= 0;
};
ko.bindingHandlers.date = {
update: function (element, valueAccessor) {
var value = valueAccessor();
var date = moment(value());
var strDate = date.format('LL');
$(element).text(strDate);
}
};
ko.bindingHandlers.contractuploader = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
$(element).fineUploader({
request: { endpoint: '/api/UploadContract/UploadFile' }
})
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called once when the binding is first applied to an element,
// and again whenever the associated observable changes value.
// Update the DOM element based on the supplied values here.
}
};
});
Here is the controller:
UploadContractController.cs
public class UploadContractController : ApiController
{
[AcceptVerbs("GET", "POST")]
public FineUploaderResult UploadFile(FineUpload upload)
{
try
{
return new FineUploaderResult(true, new { extraInformation = 12345 });
}
catch (Exception ex)
{
throw;
}
}
}
Please let me know if any other information is needed!

MonoTouch: Another thread access variables from disposed object

I have an issue with my application crashing after opening a view many times.
When my view appears, it spawns off multiple queued requests on other threads. A delegate is invoked from the other thread to update the controller.
I do attempt to cancel the threads, but occasionally, the delegate will be called, referencing a "this" which doesn't exist anymore (garbage collected).
How do I check to see if the native object (which is created for every managed object), is released?
Below is the method that gets invoked multiple times every time the controller is loaded.
protected void RequestImageFromSource (string source, NIPhotoScrollViewPhotoSize photoSize, int photoIndex)
{
var isThumbnail = photoSize == NIPhotoScrollViewPhotoSize.NIPhotoScrollViewPhotoSizeThumbnail;
var identifier = IdentifierWithPhotoSize (photoSize, photoIndex);
var identifierKey = IdentifierKeyFromIdentifier (identifier);
var photoIndexKey = CacheKeyForPhotoIndex (photoIndex);
// avoid duplicate requests
if (ActiveRequests.Contains (identifierKey))
return;
NSUrl url = new NSUrl (source);
NSMutableUrlRequest request = new NSMutableUrlRequest (url);
request.TimeoutInterval = 30;
var readOp = AFImageRequestOperation.ImageRequestOperationWithRequest (request, null,
(NSUrlRequest req, NSHttpUrlResponse resp, UIImage img) =>
{
// Store the image in the correct image cache.
if (isThumbnail) {
ThumbnailImageCache.StoreObject(img, photoIndexKey);
} else {
HighQualityImageCache.StoreObject(img, photoIndexKey);
}
// If you decide to move this code around then ensure that this method is called from
// the main thread. Calling it from any other thread will have undefined results.
PhotoAlbumView.DidLoadPhoto(img, photoIndex, photoSize);
if(isThumbnail) {
if(PhotoScrubberView != null)
PhotoScrubberView.DidLoadThumbnail(img, photoIndex);
}
// ERROR THROWN HERE
this.ActiveRequests.Remove(identifierKey);
}, (NSUrlRequest req, NSHttpUrlResponse resp, NSError er) => {
});
readOp.ImageScale = 1;
// Start the operation.
ActiveRequests.Add(identifierKey);
Queue.AddOperation(readOp);
}
Here is the error that gets thrown.
MonoTouch.Foundation.MonoTouchException: Objective-C exception thrown.
Name: NSInvalidArgumentException Reason: -[__NSCFSet removeObject:]: attempt to remove nil at
at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:void_objc_msgSend_IntPtr (intptr,intptr,intptr)
at MonoTouch.Foundation.NSMutableSet.Remove (MonoTouch.Foundation.NSObject nso) [0x0001c] in /Developer/MonoTouch/Source/monotouch/src/Foundation/NSMutableSet.g.cs:152
at MonoTouch.Nimbus.Demo.NetworkPhotoAlbumViewController+ <RequestImageFromSource>c__AnonStorey0.<>m__1 (MonoTouch.Foundation.NSUrlRequest req, MonoTouch.Foundation.NSHttpUrlResponse resp, MonoTouch.UIKit.UIImage img) [0x000a4] in /Users/Paul/Git/MedXChange.iOS/SubModules/MonoTouch.Nimbus/MonoTouch.Nimbus.Demo/Photos/NetworkPhotoAlbumViewController.cs:127
at MonoTouch.Trampolines+SDImageRequestOperationWithRequestSuccess2.TImageRequestOperationWithRequestSuccess2 (IntPtr block, IntPtr request, IntPtr response, IntPtr image) [0x00053] in /Users/Paul/Git/MedXChange.iOS/SubModules/MonoTouch.Nimbus/MonoTouch.Nimbus/obj/Debug/ios/ObjCRuntime/Trampolines.g.cs:182
at
at (wrapper native-to-managed) MonoTouch.Trampolines/SDImageRequestOperationWithRequestSuccess2:TImageRequestOperationWithRequestSuccess2 (intptr,intptr,intptr,intptr)
at
at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
at MonoTouch.Nimbus.Demo.Application.Main (System.String[] args) [0x00000] in /Users/Paul/Git/MedXChange.iOS/SubModules/MonoTouch.Nimbus/MonoTouch.Nimbus.Demo/Main.cs:17
You can check if a managed object's native peer has been released by checking the Handle property:
bool IsReleased (NSObject obj)
{
return obj.Handle == IntPtr.Zero;
}

How to return IEnumerable <int> in WebAPI (applying filters manually)

I want to return all IDs of my process (Processo class) (apply filters and order before), something like this:
Url: api/processos/getIds?&$filter=(Id eq 1)
public IEnumerable<int> getIds(ODataQueryOptions opts)
{
var results = Repositorio.Query<Processo>();
results = opts.ApplyTo(results) as IQueryable<Processo>;
return results.Select(p => p.Id).ToArray();
}
Error
Full image in: http://i.stack.imgur.com/gzJ7n.jpg
Exception
System.ArgumentException was unhandled by user code
HResult=-2147024809
Message=Cannot apply ODataQueryOptions of 'System.Int32' to IQueryable of 'CreditoImobiliarioBB.Model.Processo'.
Parameter name: query
Source=System.Web.Http.OData
ParamName=query
StackTrace:
at System.Web.Http.OData.Query.ODataQueryOptions`1.ValidateQuery(IQueryable query)
at System.Web.Http.OData.Query.ODataQueryOptions`1.ApplyTo(IQueryable query)
at CreditoImobiliarioBB.Web.Api.processosController.getIds(ODataQueryOptions opts) in w:\Clients\creditoimobiliariobb.com.br\src\CreditoImobiliarioBB\CreditoImobiliarioBB.Web\Api\processosController.cs:line 39
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
InnerException:
You need to give ODataQueryOptions a type argument, otherwise it will use the action's return type to build the options. Here is the fixed code:
public IEnumerable<int> getIds(ODataQueryOptions<Processo> opts)
{
var results = Repositorio.Query<Processo>();
results = opts.ApplyTo(results) as IQueryable<Processo>;
return results.Select(p => p.Id).ToArray();
}

How to use BeginExecuteReader with an async WCF method

I am trying to use BeginExecuteReader method of ADO.Net in an asyn WCF method, but not able to get it.
I have the following contract and service code. I cannot understand how do I fill in the details for callback method in the begin method of service. Any help would be greatly appreciated since I cannot find any examples on the web or any documentation on MSDN for this. Even some link to sample code would help since I am TOTALLY confused with how to do this.
Contract code:
[ServiceContract(Namespace = ServiceConstants.ServiceContractNamespace,
Name = ServiceConstants.ServiceName)]
public interface IAsyncOrderService
{
[OperationContract(AsyncPattern=true)]
IAsyncResult BeginGetProducts(string vendorId, AsyncCallback callback,
object state);
List<Product> EndGetProducts(IAsyncResult result);
}
The service code is:
public IAsyncResult BeginGetProducts(string vendorId, AsyncCallback cb, object s)
{
DocumentsSummaryByProgram summary = null;
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Conn1"].ConnectionString);
SqlCommand sqlCmd = null;
sqlCmd = new SqlCommand("dbo.GetProducts", conn);
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Parameters.AddWithValue("#vendorId", sqlCmd);
conn.Open();
return sqlCmd.BeginExecuteReader(cb, vendorId);
}
public List<Product> EndGetProducts(IAsyncResult r)
{
List<Product> products = new List<Product>();
SqlCommand cmd = r.AsyncState as SqlCommand;
if (cmd != null)
{
SqlDataReader dr = cmd.EndExecuteReader(r);
while (dr.Read())
{
//do your processing here and populate products collection object
}
}
return products;
}
UPDATE 1 : This seems like an impossible task. Microsoft should have provided examples to show how ADO.Net async methods are called from WCF in async manner, since this would be useful for many apps out there that want to be scalable.
UPDATE 2: I have provided a detailed answer to my question, after I was able to successfully implement async pattern in WCF. Please look at the answer in a separate post below.
You never called opened your SqlConnection
conn.Open();
Also you created two SqlConnection objects:
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Conn1"].ConnectionString);
and:
sqlCmd = new SqlCommand("dbo.GetProducts", new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["VHA_EDM"].ConnectionString));
Edit
To add an async callback you would do:
var callback = new AsyncCallback(HandleCallback);
sqlCmd.BeginExecuteReader(callback, command);
If you don't have any asynchronous code that you planned on running in between BeginExecuteReader and EndExecuteReader you are better off just using ExecuteReader.
Edit 2
The AsyncCallback delegate has the following signature:
public delegate void AsyncCallback(IAsyncResult ar);
From within that delegate method you can Invoke your EndGetProducts method.
Edit 3
Here is an example of retrieving data using BeginExecuteReader:
public SqlCommand Command { get; set; }
public IAsyncResult BeginGetStuff()
{
var connect = "[enter your connection string here]";
// Note: Your connection string will need to contain:
// Asynchronous Processing=true;
var cn = new SqlConnection(connect);
cn.Open();
var cmd = new SqlCommand("[enter your stored proc name here]", cn);
cmd.CommandType = CommandType.StoredProcedure;
this.Command = cmd;
return cmd.BeginExecuteReader();
}
public List<string> EndGetStuff(IAsyncResult r)
{
var dr = this.Command.EndExecuteReader(r);
var list = new List<string>();
while (dr.Read())
list.Add(dr[0].ToString());
return list;
}
I am providing a separate post to answer my question, since it's quite a long answer. I hope it helps others quickly implement async pattern in their WCF.
The points that I was missing when implementing async pattern in WCF, are as below. Without these, I was either getting a hung WCF problem saying 'Connecting...' or operation was aborted/canceled error message at WCF level. In my solution below, I have not discussed exception handling in async pattern on WCF side in order to keep it simple.
Do not invoke the EndGetProducts method of WCF by your code like calling it by using delagateInstance.Invoke or any other way. In async pattern, all you need to do is call the client-side callback, when your long async operation is complete, which will result in your client-side callback being called which in turn will call the WCF EndGetProduct method ( example: cb(asyncResult1) where cb is the callback delegate instance passed by the client-side code calling this WCF). I was trying to call the EndGetProducts WCF method by using Invoke, which is wrong. Even when client-side is passing nothing for client callback, this should still be done to invoke the End method in WCF.
Do not return the asyncresult you get from ADO.Net async begindatareader method, from BeginGetProducts method, since it needs to be the same AsyncResult that is in the context of client's call to WCF. This means you must include the client-side callback and the client-side state object in the AsyncResult that your BeginGetProducts will return, even when client-side is passing nothing for these. I was returning the AsyncResult of ADO.Net async method begindatareader from BeginGetProducts, which is wrong.
When calling the client-side callback delegate instance from WCF, make sure you pass the AsyncResult that contains client-side context that I have discussed in last bullet. Also, do this when your async operation is complete, which I do in the callback of beginexecutereader after I have created a List object.
One last point to keep in mind is that you must set sufficiently large timeouts at WCF and ADO.Net levels, since your async operation might take quite a long time else you will get timeouts in WCF. For this, set the ADO.Net command timeout to 0 ( infinite timeout) or to an appropriate value, and for WCF you can include configuration like below.
<binding name="legacyBinding" openTimeout="00:10:00" sendTimeout="00:10:00"
receiveTimeout="00:10:00" closeTimeout="00:10:00" maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647" >
Now the code, which might appear lengthy, but my intention is to make it easy for others to implement async pattern in their WCF. It was quite difficult for me.
WCF Contract
[OperationContract(AsyncPattern = true)]
[FaultContract(typeof(string))]
IAsyncResult BeginGetProducts(string vendorId, AsyncCallback cb, object s);
//The End method must return the actual datatype you intend to return from
//your async WCF operation. Also, do not decorate the End method with
//OperationContract or any other attribute
List<Product> EndGetProducts(IAsyncResult r);
WCF Implementation
public IAsyncResult BeginGetProducts( string vendorId, AsyncCallback cb, object s)
{
SqlCommand sqlCmd = null;
sqlCmd = new SqlCommand("dbo.ABC_sp_GetProducts", "Data Source=xyz;Initial Catalog=NorthwindNew;Integrated Security:true;asynchronous processing=true;"));
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Parameters.AddWithValue("#vendorId", vendorId);
sqlCmd.CommandTimeout = 0;//async operations can be long operations so set a long timeout
//THIS ASYNRESULT MUST REFLECT THE CLIENT-SIDE STATE OBJECT, AND IT IS WHAT SHOULD FLOW THROUGH TO END METHOD of WCF.
//THE CLIENT CALLBACK (PARAMETER 'cb') SHOULD BE INVOKED USING THIS ASYNCRESULT, ELSE YOUR WCH WILL HANG OR YOUR WCF WILL GET ABORTED AUTOMATICALLY.
AsyncResult<FinalDataForDocumentsSummary> asyncResult1 = new AsyncResult<FinalDataForDocumentsSummary>(false, s);//this is the AsyncResult that should be used for any WCF-related method (not ADO.Net related)
AsyncCallback callback = new AsyncCallback(HandleCallback);//this is callback for ADO.Net async begindatareader method
sqlCmd.Connection.Open();
//AsynResult below is for passing information to ADO.Net asyn callback
AsyncResult<Product> cmdResult = new AsyncResult<Product>(false, new object[] {sqlCmd, cb,s});
sqlCmd.BeginExecuteReader(HandleCallback, cmdResult);
return asyncResult1;//ALWAYS RETURN THE ASYNCRESULT INSTANTIATED FROM CLIENT PARAMETER OF STATE OBJECT. FOR DATAREADER CREATE ANOTHER ASYNCRESULT THAT HAS COMMAND OBJECT INSIDE IT.
}
/// <summary>
/// This is the callback on WCF side for begin data reader method.
/// This is where you retrieve data, and put it into appropriate data objects to be returned to client.
/// Once data has been put into these objects, mark this ASYNC operation as complete and invoke the
/// client callback by using 'cb(asyncResult1)'. Use the same asyncresult that contains the client passed state object.
/// </summary>
/// <param name="result"></param>
public void HandleCallback(IAsyncResult result)
{
List<Product> summaries = new List<Product>();
Product product = null;
//THIS ASYNCRESULT IS ONLY FOR DATAREADER ASYNC METHOD AND NOT TO BE USED WITH WCF, ELSE BE READY FOR WCF FAILING
AsyncResult<Product> asyncResult = result.AsyncState as AsyncResult<Product>;
object[] objects = asyncResult.AsyncState as object[];
SqlCommand cmd = objects[0] as SqlCommand;
AsyncCallback cb = objects[1] as AsyncCallback;
object s = objects[2];
//CREATE THE SAME ASYNCRESULT THAT WE HAD IN BEGIN METHOD THAT USES THE CLIENT PASSED STATE OBJECT
AsyncResult<Product> asyncResult1 = new AsyncResult<Product>(false, s);
SqlDataReader dr = null;
if (cmd != null)
{
try
{
dr = cmd.EndExecuteReader(result);
while (dr.Read())
{
product = new Product(dr.GetInt32(0), dr.GetString(1));
summaries.Add(summary);
}
dr.Close();
cmd.Connection.Close();
//USE THE CORRECT ASYNCRESULT. WE NEED THE ASYNCRESULT THAT WE CREATED IN BEGIN METHOD OF WCF.
asyncResult1.Data = new FinalDataForDocumentsSummary(count, summaries.OrderByDescending(x => x.CountOfOverDue).ToList());
}
finally
{
if (dr != null)
{
dr.Close();
}
if (cmd.Connection != null)
{
cmd.Connection.Close();
cmd.Connection.Dispose();
}
//USE THE CORRECT ASYNCRESULT. WE NEED THE ASYNCRESULT THAT WE CREATED IN BEGIN METHOD OF WCF
asyncResult1.Complete();
//THIS IS REQUIRED ELSE WCF WILL HANG. EVEN WHEN NO CALLBACK IS PASSED BY CLIENT,
//YOU MUST EXECUTE THIS CODE. EXECUTE IT AFTER YOUR OPERATION HAS COMPLETED,
//SINCE THIS IS WHAT CAUSES THE END METHOD IN WCF TO EXECUTE.
//DON'T TRY TO CALL THE WCF END METHOD BY YOUR CODE (like using delegateInstance.Invoke) SINCE THIS WILL HANDLE IT.
cb(asyncResult1);
}
}
}
/// <summary>
/// This method gets automatically called by WCF if you include 'cb(asyncResult1)' in the reader's callback meethod, so don't try to call it by your code.
/// But always use 'cb(asyncResult1)' just after data has been successfully retrieved from database and operation is marked as complete.
/// </summary>
/// <param name="r"></param>
/// <returns></returns>
public List<Product> EndGetProducts(IAsyncResult r)
{
AsyncResult<Product> result = r as AsyncResult<Product>;
// Wait until the AsyncResult object indicates the
// operation is complete, in case the client called the End method just after the Begin method.
if (!result.CompletedSynchronously)
{
System.Threading.WaitHandle waitHandle = result.AsyncWaitHandle;
waitHandle.WaitOne();
}
// Return the database query results in the Data field
return result.Data;
}
Generic class for AsyncResult that is needed in async pattern
using System;
using System.Threading;
class AsyncResult<T> : IAsyncResult
{
private T data;
private object state;
private bool isCompleted = false;
private AutoResetEvent waitHandle;
private bool isSynchronous = false;
public T Data
{
set { data = value; }
get { return data; }
}
public AsyncResult(bool synchronous, object stateData)
{
isSynchronous = synchronous;
state = stateData;
}
public void Complete()
{
isCompleted = true;
((AutoResetEvent)AsyncWaitHandle).Set();
}
public object AsyncState
{
get { return state; }
}
public WaitHandle AsyncWaitHandle
{
get
{
if (waitHandle == null)
waitHandle = new AutoResetEvent(false);
return waitHandle;
}
}
public bool CompletedSynchronously
{
get
{
if (!isCompleted)
return false;
else
return isSynchronous;
}
}
public bool IsCompleted
{
get { return isCompleted; }
}
}
How to call this from client-side:
protected void Page_Load(object sender, EventArgs e)
{
using (ABCService.ServiceClient sc = new ABCService.ServiceClient())
{
// List<ABCService.Product> products = sc.GetDocSummary("Vend1", null, false);//this is synchronous call from client
sc.BeginGetProducts("Vend1",GetProductsCallback, sc);//this is asynchronous call from WCF
}
}
protected void GetProductsCallback(IAsyncResult asyncResult)
{
List<ABCService.Product> products = ((ABCService.ServiceClient)asyncResult.AsyncState).EndGetProducts(asyncResult);//this will call the WCF EndGetProducts method
}