Catch Fault Exception in Silverlight with Channel Factory - silverlight

I am trying to call a WCF service from a Silverlight client using channel factory as per this link. Working with channel factory is something new for me so please bear with me!
Everything mentioned in the article works just fine. But now I am trying to implement Fault exceptions so that I can catch the actual exceptions on the Silverlight side. But for some reason I always end up catching CommunicationException which doesn't serve my purpose.
Here is my service contract:
[OperationContract]
[FaultContract(typeof(Fault))]
IList<Category> GetCategories();
Catch block of the service:
catch (Exception ex)
{
Fault fault = new Fault(ex.Message);
throw new FaultException<Fault>(fault, "Error occured in the GetCategories service");
}
Service contract for client with async pattern:
[OperationContract(AsyncPattern = true)]
[FaultContract(typeof(Fault))]
IAsyncResult BeginGetCategories(AsyncCallback callback, object state);
IList<Category> EndGetCategories(IAsyncResult result);
Here is the service call from client:
ICommonServices channel = ChannelProviderFactory.CreateFactory<ICommonServices>(COMMONSERVICE_URL, false);
var result = channel.BeginGetCategories(
(asyncResult) =>
{
try
{
var returnval = channel.EndGetCategories(asyncResult);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
CategoryCollection = new ObservableCollection<Category>(returnval);
});
}
catch (FaultException<Fault> serviceFault)
{
MessageBox.Show(serviceFault.Message);
}
catch (CommunicationException cex)
{
MessageBox.Show("Unknown Communications exception occured.");
}
}, null
);
I am sharing the DataContract .dll between both the service and client applications and hence they are referring to same data contract classes (Category & Fault)
Please tell me what I am doing wrongly?
UPDATE: I do clearly see the fault exception sent from the service in Fiddler. Which makes me believe I am missing something in the client side.

For catching normal exceptions in sivleright you must create "Silverlight-enabled WCF Service" (Add -> New Item -> Silverlight-enabled WCF Service).
If you already created standard WCF service you can add attribute [SilverlightFaultBehavior] to your service manually.
Default implementation of this attribute is:
public class SilverlightFaultBehavior : Attribute, IServiceBehavior
{
private class SilverlightFaultEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new SilverlightFaultMessageInspector());
}
public void Validate(ServiceEndpoint endpoint)
{
}
private class SilverlightFaultMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
if ((reply != null) && reply.IsFault)
{
HttpResponseMessageProperty property = new HttpResponseMessageProperty();
property.StatusCode = HttpStatusCode.OK;
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
endpoint.Behaviors.Add(new SilverlightFaultEndpointBehavior());
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}

We use our own custom ServiceException class on the server e.g.
[Serializable]
public class ServiceException : Exception
{
public ServiceException()
{
}
public ServiceException(string message, Exception innerException)
: base(message, innerException)
{
}
public ServiceException(Exception innerException)
: base("Service Exception Occurred", innerException)
{
}
public ServiceException(string message)
: base(message)
{
}
}
And then in our server side service methods we use error handling like this:
try
{
......
}
catch (Exception ex)
{
Logger.GetLog(Logger.ServiceLog).Error("MyErrorMessage", ex);
throw new ServiceException("MyErrorMessage", ex);
}
We then use a generic method for all web service calls:
/// <summary>
/// Runs the given functon in a try catch block to wrap service exception.
/// Returns the result of the function.
/// </summary>
/// <param name="action">function to run</param>
/// <typeparam name="T">Return type of the function.</typeparam>
/// <returns>The result of the function</returns>
protected T Run<T>(Func<T> action)
{
try
{
return action();
}
catch (ServiceException ex)
{
ServiceLogger.Error(ex);
throw new FaultException(ex.Message, new FaultCode("ServiceError"));
}
catch (Exception ex)
{
ServiceLogger.Error(ex);
throw new FaultException(GenericErrorMessage, new FaultCode("ServiceError"));
}
}

Related

Update ObservableCollection from wcf service callback function

I'm currently working on chat client-server application based on wcf service, which wich operate in duplex mode. I have two WPF applications: Server, where service itself is hosted and Client.
Here is service contract:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ISendToClient))]
interface IChatService
{
[OperationContract]
ChatUser ClientConnect(ChatUser userName);
[OperationContract]
void RemoveUser(ChatUser user);
[OperationContract(IsOneWay = true)]
void sendNewMessage(ChatMessage message);
}
interface ISendToClient
{
[OperationContract]
void newUserConnected(ChatUser user);
[OperationContract(IsOneWay = true)]
void deliverNewMessage(ChatMessage message);
}
and its implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
public class ChatService : IChatService, INotifyPropertyChanged
{
public ChatService()
{
conectedUsers = new ObservableCollection<ChatUser>();
}
ISendToClient callback;
private ObservableCollection<ChatUser> _conectedUsers;
public ObservableCollection<ChatUser> conectedUsers
{
get { return _conectedUsers; }
set
{
if (_conectedUsers == value) return;
_conectedUsers = value;
OnPropertyChanged();
}
}
public ChatUser ClientConnect(ChatUser user)
{
//do smth
}
public List<ChatMessage> GetNewMessages(ChatUser user)
{
//do smth
}
public void sendNewMessage(ChatMessage newMessage)
{
callback = OperationContext.Current.GetCallbackChannel<ISendToClient>();
callback.deliverNewMessage(newMessage);
}
#region PropertyChangedMembers
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
View model on clinet side include two additional methods
public void newUserConnected(ChatUser user)
{
Users.Add(user);
}
public void deliverNewMessage(ChatMessage message)
{
messHistory.Add(message);//adding message to ObservaleCollection
//this Collection is binded to listbox, but not displays
}
Server use this methods to inform client about new messages or new users logged in.
Service is correctly configured, client can connect to server and send messages, server sends them to all users via calling deliverNewMessage() method on client side, this messages successfully added to ObservaleCollection, which is binded to listBox:
<ListBox Grid.Row="0" Margin="3" Name="lst"
ItemsSource="{Binding messHistory}" />
BUT! No messages displays in listBox. I tried to do like this:
Application.Current.Dispatcher.Invoke(new Action(() =>
{
messHistory.Add(message);
}));
with no effect.
I guess, one little detail is missed, but have no clue.
I'd appreciate any ideas how to fix this issue.

Why API method never fired?

I have this API methods:
namespace Playground.Web.Controllers.API
{
public class FilterConfigurationsController : ApiController
{
private readonly PlaygroundContext _context;
public FilterConfigurationsController()
{
_context = new PlaygroundContext();
}
[HttpPost]
public async Task<IHttpActionResult> Post(Filter data)
{
try
{
var ere = FilterViewModel.GenerateDataForFilters(data, _context);
return Ok(data);
}
catch (Exception ex)
{
throw;
}
}
[HttpPost]
public async Task<IHttpActionResult> Post(IEnumarable<Filter> filterDescriptionList)
{
try
{
var data = FilterViewModel.GenerateDataForFilters(filterDescriptionList, _context);
return Ok();
}
catch (Exception ex)
{
throw;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
}
}
To call this methods I use this HTTP post calls:
$http.post(config.baseUrl + "api/FilterConfigurations/", scope.filterParams).then(function (result) {});
I am working on web API project and using AngularJS in my project.
This HTTP post call:
$http.get(config.baseUrl + "api/FilterConfigurations/,{data= scope.filterParams[0]}).then(function (result) {});
have to call this method API:
public async Task Post(Filter data)en(function (result) {});
But the method API above never fired.
Instead this method alwaes fired:
public async Task<IHttpActionResult> Post(Filter filterDescriptionList)
Any idea why the API method never fired?
What I am doing wrong?

PRISM Property injection fails

In my Silverlight RIA app I can successfully inject an ExceptionHelper class via constructor injection:
[InjectionConstructor]
public CDashCatDataService(IExceptionHelper exceptionHelper)
{
_exceptionHelper = exceptionHelper;
SetUp();
}
void SetUp()
{
if (_context == null)
{
try
{
throw new Exception("this is a test exception");
_context = new CDASHDomainContext();
}
catch (Exception ex)
{
_exceptionHelper.HandleException(ex, string.Format("{0} -> {1}", GetType().FullName, "Setup"));
}
}
Context = _context;
}
The SetUp method successfully logs and handles the test exception.
However, I can't get this working if I use property injection instead:
[Dependency]
public IExceptionHelper ExceptionHelper { get; set; }
and then using
catch (Exception ex)
{
ExceptionHelper.HandleException(ex, string.Format("{0} -> {1}", GetType().FullName, "Setup"));
}
in the SetUp method. If I use this, the test exception is not logged and handled.
I'm clearly missing something here - any ideas?
I think this is because the ExceptionHelper property is being called from the constructor via the SetUp method and you can't call injected properties until after the constructor has executed.

WCF Data Services UpdateObject not working

I have a Silverlight client with a grid getting data from WCF Data Service. Works fine.
However if I want to update some changed grid row, the service data context UpdateObject is not working:
DataServiceContext.UpdateObject(MyGrid.SelectedItem);
foreach (Object item in DataServiceContext.Entities)
{
//
}
DataServiceContext.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, DataServiceContext);
I just have created a loop to inspect the values for the entities items and the value is not updated at all. BeginSaveChanges works fine, but it just uses not updated values.
Any ideas how to fix that?
thanks
Right a fully flushed out SaveChanges that will show the error message if EndSaveChanges() fails, like the code sample below. Obviously you can't use the console to write out your message in silverlight, but you get the idea.
For instance, when I wrote the following sample, I found that I was getting a forbidden error, because my entity set had EntitySetRights.AllRead, not EntitySetRights.All
class Program
{
private static AdventureWorksEntities svc;
static void Main(string[] args)
{
svc =
new AdventureWorksEntities(
new Uri("http://localhost:5068/AWDataService.svc",
UriKind.Absolute));
var productQuery = from p in svc.Products
where p.ProductID == 740
select p;
var product = productQuery.First();
ShowProduct(product);
product.Color = product.Color == "Silver" ? "Gray" : "Silver";
svc.UpdateObject(product);
svc.BeginSaveChanges(SaveChangesOptions.Batch, OnSave, svc);
ShowProduct(product);
Console.ReadKey();
}
private static void ShowProduct(Product product)
{
Console.WriteLine("Id: {0} Name: {1} Color: {2}",
product.ProductID, product.Name, product.Color);
}
private static void OnSave(IAsyncResult ar)
{
svc = ar.AsyncState as AdventureWorksEntities;
try
{
WriteResponse(svc.EndSaveChanges(ar));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void WriteResponse(DataServiceResponse response)
{
if(response.IsBatchResponse)
{
Console.WriteLine("Batch Response Code: {0}", response.BatchStatusCode);
}
foreach (ChangeOperationResponse change in response)
{
Console.WriteLine("Change code: {0}", change.StatusCode);
if(change.Error != null)
{
Console.WriteLine("\tError: {0}", change.Error.Message);
}
}
}
}

"Authentication Failure" when calling a method on Remote Object in wpf

I am developing an application which uses WindowsFormsApplicationBase to enforce Single Instance. I get the following error when calling a method on a Remote object. It works fine if I don't use Single Instance approach.
System.Runtime.Remoting.RemotingException: Authentication failure ---> System.IO.IOException: Unable to read data from the transport connection: The connection was closed.
at System.Net.Security.NegoState.ProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.NegotiateStream.AuthenticateAsClient(NetworkCredential credential, String targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel)
at System.Runtime.Remoting.Channels.Tcp.TcpClientTransportSink.CreateAuthenticatedStream(Stream netStream, String machinePortAndSid)
Here is my Code:
public class EntryPoint
{
[STAThread]
public static void Main(string[] args)
{
SingleInstanceManager sim = new SingleInstanceManager();
sim.Run(args);
}
}
public class SingleInstanceManager : WindowsFormsApplicationBase
{
private App app;
public SingleInstanceManager()
{
IsSingleInstance = true;
}
protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
{
app = new App();
app.InitializeComponent();
app.Run();
return false;
}
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
base.OnStartupNextInstance(eventArgs);
app.Activate();
}
}
This is how I am invoking the Remoting object:
public Hashtable GetData(string[] arg1, string[] arg2)
{
IDataProvider dataProvider = (IDataProvider )Activator.GetObject(typeof(IDataProvider ), "tcp://.....");
Hashtable data = dataProvider.GetData(arg1, arg2);
return data;
}
Thanks in advance.
I found the solution myself.
I used the following to implement single instance(http://www.ai.uga.edu/mc/SingleInstance.html).
[STAThread]
static void Main() // args are OK here, of course
{
bool ok;
m = new System.Threading.Mutex(true, "YourNameHere", out ok);
if (! ok)
{
MessageBox.Show("Another instance is already running.");
return;
}
Application.Run(new Form1()); // or whatever was there
GC.KeepAlive(m); // important!
}

Resources