I have a wcf service which has a one way operation and it sends the result in a callback. Here is my service definition
[ServiceContract(CallbackContract = typeof(IIrmCallback), SessionMode = SessionMode.Required)]
public interface IFileService
{
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
[ServiceKnownType(typeof(StatementFile))]
[ServiceKnownType(typeof(InvoiceFile))]
void UploadFile(IFile file);
}
public interface IFileCallback
{
[OperationContract]
void FileProcessed(string result);
}
public MyService : IFileService
{
IFileCallBack callbackchannel;
object result;
public void UploadFile(IFile file)
{
callbackChannel = OperationContext.Current.GetCallbackChannel<IIrmCallback>();
Task.Factory.StartNew(() => ProcessFile(file));
}
private ProcessFile(IFile file)
{
// file gets processed
callbackChannel.FileProcessed(result)
}
}
So now if there is some error during the file processing is there someway I can throw an exception back to the caller in the callback?
You can use FaultException
When an operation throws an exception, a FaultException will be returned to the client.
You need to implement a FaultContract in your service interface specifying the types of faults that are possible in that given method and then throw a new fault that inherits from FaultException within the method.
On the client, make sure to catch and handle FaultException and Exception objects individually because Exceptions will cause the WCF channel to fault and it cannot be reused, while a FaultException will allow you to continue using the existing channel.
Related
I haven't found a clear answer on this. so if there is already a question about this, my bad.
I have a WCF service that pushes data via a callback method to connected clients. this callback method is oneway. so everytime there is new data I loop over the connected users and push the data.
The problem I have right now is when a client disconnects it throws an error and the channel becomes faulted.
I always thought that oneway didn't care if the message arrives at the destination. So if there's no client, then bad luck. but no exception.
but there is an exception and that exception faults the channel.
Now I've read somewhere that if you enable reliable sessions, that the exception won't fault the channel. Is this true?
How can I prevent that the channel goes into faulted state when an exception happens on a oneway call?
The list of registered and avaiable clients you can store in some resource such as List. Create another interface which exposes Connect/Disconnect methods. Connect is invoked when application starts off and within method client is added to the list. Disconnect in turn is invoked when application shuts down in order to get rid client of list. OnStartup/OnClosing events or their equivalents, depending on what kind of application client is, refer to moment when application is launched and closed. Such a solution ensures that resource stores only users avaiable to be reached.
[ServiceContract]
interface IConnection
{
[OperationContract(IsOneWay = true)]
void Connect();
[OperationContract(IsOneWay = true)]
void Disconnect();
}
[ServiceContract]
interface IServiceCallback
{
[OperationContract(IsOneWay = true)]
void CallbackMethod();
}
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
interface IService
{
[OperationContract]
void DoSth();
}
class YourService : IConnection, IService
{
private static readonly List<IServiceCallback> Clients = new List<IServiceCallback>();
public void Connect()
{
var newClient = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.All(client => client != newClient))
Clients.Add(newClient);
}
public void Disconnect()
{
var client = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.Any(cl => cl == client))
Clients.Remove(client);
}
public void DoSth()
{
foreach(var client in Clients)
client.CallbackMethod();
}
}
At the end expose another endpoint with IConnection so that client can create proxy meant to be used only for connection/disconnection.
EDIT:
I know it has been a while since I posted an answear but I did not find in order to prepare an example. The workaround is to let service's interface derive IConnection and then expose only service as an endpoint. I attach simple example of WCF and WPF app as client. Client's application violates MVVM pattern but in this case it is irrelevant. Download it here.
To add on what Maximus said.
I've implemented this pattern in a class where clients can subscribe to get updates of internal states of a system, so a monitoring client can show graphs and other clients do other stuff like enabling/disabling buttons if some state is active.
It removes faulted channels from the list when they fail. Also all current states are sent when a client connects.
here's the code, hope it helps!
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Publish : IPublish
{
private struct SystemState
{
public string State;
public string ExtraInfo;
}
private static Dictionary<Key<string>, IPublishCallback> mCallbacks = new Dictionary<Key<string>, IPublishCallback>();
private static Dictionary<string, SystemState> mStates = new Dictionary<string, SystemState>();
public void RegisterClient(string name, string system)
{
lock (mCallbacks)
{
IPublishCallback callback = OperationContext.Current.GetCallbackChannel<IPublishCallback>();
Key<string> key = new Key<string>(name, system);
if (!mCallbacks.ContainsKey(key))
{
mCallbacks.Add(key, callback);
}
else
{
mCallbacks[key] = callback;
}
foreach (KeyValuePair<string, SystemState> s in mStates)
{
mCallbacks[key].ServiceCallback(s.Key, s.Value.State, s.Value.ExtraInfo);
}
}
}
public void UnregisterClient(string name)
{
lock (mCallbacks)
{
outer: foreach (var key in mCallbacks.Keys)
{
if (key.Key1 == name)
{
mCallbacks.Remove(key);
goto outer;
}
}
}
}
public void SetState(string system, string state, string extraInfo)
{
lock (mCallbacks)
{
List<Key<string>> toRemove = new List<Key<string>>();
SystemState s = new SystemState() { State = state, ExtraInfo = extraInfo };
SystemState systemState;
if (!mStates.TryGetValue(system, out systemState))
mStates.Add(system, s);
else
mStates[system] = s;
foreach (KeyValuePair<Key<string>, IPublishCallback> callback in mCallbacks)
{
try
{
callback.Value.ServiceCallback(system, state, extraInfo);
}
catch (CommunicationException ex)
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
catch
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
}
foreach (Key<string> key in toRemove)
mCallbacks.Remove(key);
}
}
}
My services simply call BusinessLogicLayer methods where entire business logic is put. I want to know what's the best practice for handling exceptions raised by BL?(not only fatal exceptions, also "logic" ApplicationExceptions like UserNotFoundException which my BL throws when can't find user).
Where should I transform these exceptions into FaultExceptions which client will see?
Should I throw my business Exceptions from BL and than catch them into service call and transform to FaultException and return to client? or BL should raise already "client friendly" FaultExceptions?
thanks in advance :)
I would say throw business exception from business logic layer, this would keep your business logic layer decoupled with wcf implementation. In service call you may override applydispatchbehaviour and add error handler there, something like
Overriding IServiceBehavior.ApplyDispatchBehavior
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(new FaultErrorHandler());
}
}
FaultErrorHandler
public class FaultErrorHandler : IErrorHandler
{
void IErrorHandler.ProvideFault(System.Exception error, MessageVersion version, ref Message fault)
{
if (fault == null)
{
FaultException<[ExceptionType]> fe = new
FaultException<[ExceptionType]>([Exception cass],
error.Message, FaultCode.CreateReceiverFaultCode(new FaultCode("ServerException")));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
}
}
Standard .Net exceptions are correctly serialized on server side, and deserialized on client side.
By default, not ours. Why ?
It may be a best practise to send business exception to client during debuging sessions:
- without having to put the Exception Data in a [DataMember] object
- having more info than a simple string ( ExceptionFault<ExceptionDetail>)
But take care of not sending exceptions when putting code in production. It may cause security leaks disclosing details to hackers if your service is exposed on internet !
In order to send the business exception to the client, the best (and some mandatory) practises are :
1/ Toggle the serviceDebugBehavior on
ServiceHost host = ...;
var debuggingBehavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
if (debuggingBehavior == null)
debuggingBehavior = new ServiceBehaviorAttribute();
#if DEBUG
debuggingBehavior.IncludeExceptionDetailInFaults = true;
#else
debuggingBehavior.IncludeExceptionDetailInFaults = false;
#endif
It is also pretty easy configurate it in xml
2/ On the service interface, declare some [FaultContract] :
[ServiceContract(Namespace="your namespace")]
public interface IBillingService
{
[OperationContract]
[FaultContract(typeof(BusinessException))]
void RaiseBusinessException();
}
3/ A business exception should be marked as Serializable
[Serializable]
public class BusinessException : Exception
{ ... }
4/ In order to have a business exception correctly deserialized on the client side as FaultException<BusinessException>, it is important to implement a constructor taking care of deserialization. Otherwise you'll get a generic FaultException.
protected BusinessException(SerializationInfo info, StreamingContext context)
: base(info, context)
{}
5/ If you have some extra members in you exception, serialize/deserialize them :
public DateTime CreationTime { get; set; }
protected BusinessException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
CreationTime = (DateTime)info.GetValue("CreationTime", typeof(DateTime));
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("CreationTime", CreationTime);
}
Problem:
getting a deadlock exception message in the WCF client.
Scenario:
Service calls a client callback (this call is completely independent and is initiated by some condition on the server).
While inside the client callback function, the client calls a function in the service and that's when the deadlock exception is thrown:
This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on CallbackBehaviorAttribute.
I tried to simplify the code as much as I can. I did read this article but still I cant find where the problem is: http://msdn.microsoft.com/en-us/library/cc294424.aspx
I'd appreciate any suggestions....
SERVICE:
[ServiceContract(Namespace = "http://abc.com/Core", SessionMode = SessionMode.Required, CallbackContract = typeof(ISvcCallback))]
public interface ISvc
{
// One way only - does not wait until operation completes and returns
// Can initiate session
[OperationContract(IsOneWay = true)]
void Initialize(string appId);
[OperationContract(IsInitiating = false)]
Account GetCurrentAccount();
}
public interface ISvcCallback
{
/// <summary>
/// Report status of the account
/// </summary>
/// <param name="acct"></param>
[OperationContract(IsOneWay=true)]
void AccountStatus(Account acct);
}
Service Implementation
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
public class Svc : ISvc
{
public Account GetCurrentAccount()
{
SipAccount sipAcct = null;
try
{
Account acct = m_MyBusinessObject.GetCurrentAccount();
}
catch (Exception ex)
{
}
return Acct;
}
}
}
CLIENT:
public class CallbackHandler : WcfSipItfService.IWinSipItfCallback
{
public void AccountStatus(Account Acct)
{
try
{
// display accout status in UI by delegate-wrapped event
// delegate and event declarations are somewhere else
// and work fine...
if (DisplayAccountStatusEvent != null)
DisplayAccountStatusEvent(Acct);
}
catch (Exception ex)
{
....
}
}
private void OnDisplayAccountStatusEvent(Account acct)
{
// call service function results in deadlock
Account acct = GetCurrentAccount();
}
}
The service is Duplex - uses WSDualHttpBinding.
The deadlock appears to be due to you making a new out bound call while processing a callback from the previous call.
The error message states that you may be able to solve it by "specify ConcurrencyMode of Reentrant or Multiple on CallbackBehaviorAttribute".
Edit
I missed the code that was off the screen. Couple of things to check:
Why do you need instance mode single?
Did you updated the service references after changing the concurrency mode?
What's your client? Is it a UI client? if so, you need to add callbackBehavior UseSynchronizationContext=false to client that implements callback contract.
One of the things I never understood about WCF is why no Exception message details are propagated back to the calling client when the server encounters an unhandled exception.
For example, if I have the following server code
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Server : IServer
{
public DTO GetDTO()
{
DTO dto = new DTO();
dto.dto = dto;
return dto;
}
}
public class DTO
{
public DTO dto;
}
[ServiceContract]
public interface IServer
{
[OperationContract]
DTO GetDTO();
}
I deliberately introduced an ObjectGraph to cause a serialization exception when the DTO object is returned.
If I have a client that calls this Server's GetDTO() method, I will get the following CommunicationException.
The socket connection was aborted. This could be caused by an error
processing your message or a receive timeout being exceeded by the
remote host, or an underlying network resource issue. Local socket
timeout was '00:00:58.9350000'.
Which is absolutely useless. It has no inner exception and not even the real exception message.
If you then use Microsoft Service TraceViewer, you will see the exception but you must turn on the Diagnostics tracing for this.
The exception message that should be sent back is
There was an error while trying to serialize parameter
http://tempuri.org/:GetDTOResult. The InnerException message was
'Object graph for type 'TestWCFLib.DTO' contains cycles and cannot be
serialized if reference tracking is disabled.'. Please see
InnerException for more details.
So can anybody tell me how get the right exception message show up on the client side? Obviously, setting IncludeExceptionDetailInFaults to true doesn't make a difference.
I think that it is by design that the server errors are not propogated to client. This is in general a practice to not expose server internals to clients as the main purpose of Client Server architecture is independence of server.
You can still achieve this by using Fault Exception
Decorate your service declaration with a fault contract
[ServiceContract]
public interface IServer
{
[OperationContract]
[FaultContract(typeof(MyApplicationFault))]
DTO GetDTO();
}
Then catch errors in servcie implementation and throw a fault exception.
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Server : IServer
{
public DTO GetDTO()
{
try
{
DTO dto = new DTO();
dto.dto = dto;
return dto;
}
catch (Exception ex)
{
MyApplicationFault fault = new MyApplicationFault(...);
throw new FaultException<MyApplicationFault>(fault);
}
}
}
And catch the exception in client
IServer proxy = ...; //Get proxy from somewhere
try
{
proxy.GetDTO();
}
catch (TimeoutException) { ... }
catch (FaultException<MyApplicationFault> myFault) {
MyApplicationFault detail = myFault.Detail;
//Do something with the actual fault
}
catch (FaultException otherFault) { ... }
catch (CommunicationException) { ... }
Hope this helps.
For a nice tutorial please see Code Project Tutorial on Fault Exception
Consider the following very basic WCF service implementation:
public enum TransactionStatus
{
Success = 0,
Error = 1
}
public class TransactionResponse
{
public TransactionStatus Status { get; set; }
public string Message { get; set; }
}
[ServiceContract]
[XmlSerializerFormat]
public interface ITestService
{
[OperationContract]
TransactionResponse DoSomething(string data);
}
public class TestService : ITestService
{
public TransactionResponse DoSomething(string data)
{
var result = ProcessData(data); // may throw InvalidOperationException
return new TransactionResponse()
{
Status = TransactionStatus.Success,
Message = result
};
}
private string ProcessData(string data)
{
if (data = "foobar")
throw new InvalidOperationException();
return data;
}
}
In the instance that the DoSomething method does throw an InvalidOperationException, I would like to intercept the fault and return a TransactionResponse object, rather than have WCF raise a FaultException with the client. How can I do this without surrounding each method body in a huge try catch statement? Is there some where I can hook into? Can I do this with some sort of attribute or something? An example of how I would like to handle it can be demonstrated using ASP.NET MVC:
public class ApiController : BaseController
{
protected override void OnException(ExceptionContext filterContext)
{
var ex = filterContext.Exception;
var message = HttpContext.IsDebuggingEnabled ? ex.ToString() : ex.Message;
_logger.Error("Error processing request for controller {0}, action {1}",
filterContext.RequestContext.RouteData.Values["controller"],
filterContext.RequestContext.RouteData.Values["action"]);
_logger.Error(ex.ToString());
filterContext.ExceptionHandled = true;
filterContext.Result = ToXml(new ApiResult(false)
{
Message = message
});
}
// ...
}
Using the above method in MVC, I can ensure that no matter which controller action throws an exception, I can handle it and return an appropriately formatted ActionResult containing the necessary info. Is there a way to do this kind of thing with WCF?
Check out the WCF IErrorHandler interface - it allows you to centrally define one way in your service implementation to catch all exceptions and either swallow them, or convert them to WCF-friendly SOAP exceptions. This will make sure the channel between the client and the server isn't faulted, e.g. it can still be used after this call failed.
I don't understand why you'd want to "catch" the SOAP faults and convert those to something else, though.... nor do I know of any support that WCF would give you. The basic assumption is: catch .NET exceptions and convert them into interoperable SOAP faults