WCF RIA Exception on Invoke operation interrupts Caliburn.Micro coroutine execution? - wcf-ria-services

I am executing a series of Caliburn.Micro IResults by yield returning them from an IEnumerable method called by a Caliburn.Micro action message. The first IResult calls a WCF RIA service Invoke operation. Sometimes this operation fails and throws an exception. This is handled in the IResult where the InvokeOperation object is checked for error, I mark the error as handled and set the IResult's error message field to the error so I can recover it from the client.
The problem is that for some reason this interrupts the co-routine executing. I can't think of any good reason why, but when I'm in debug mode VS skips to the server code and bring up the unhandled exception helper telling me there was an uncaught exception (duh), and the co-routine never continues executing the other members of the IEnumerable.
Here is some of the code.
Called from the Action Message:
public IEnumerable<IResult> Submit()
{
var register = new RegisterUserResult(Username, Password, Email, _userModel);
yield return register;
if (register.Success)
{
if (RegisterAsTrainer)
yield return new ApplyRoleToUserResult(Username, "Trainer", _userModel);
yield return new NavigateResult(new Uri("/MainPageViewModel", UriKind.Relative));
}
else ErrorMessage = register.ErrorMessage;
}
The code in the DomainService (which sometimes throws an exception)
[Invoke]
public void CreateUser(string username, string password, string email)
{
Membership.CreateUser(username, password, email);
}
...where Membership is the ASP.NET class, which I am using for membership management.
The IResult that calls the above service (some details elided for clarity):
public void Execute(ActionExecutionContext context)
{
ErrorMessage = null;
Success = false;
var ctx = new TrainingContext();
ctx.CreateUser(_username, _password, _email, CreateUserCallback, null);
}
private void CreateUserCallback(InvokeOperation invokeOp)
{
if (invokeOp.HasError)
invokeOp.MarkErrorAsHandled();
Completed(this, new ResultCompletionEventArgs
{
Error = invokeOp.Error,WasCancelled = invokeOp.IsCanceled
});
}
The IResult.Completed DOES fire, but the rest of the method never executes. I'm literally tearing my hair out with this, please please help me.

Ugh I figured this out, stupid me. I was setting the IResult Error field, thinking I'd need to use that information later. I didn't know that having a non-null Error field would cause co-routine execution to halt (I thought only the Canceled field would do that). I'll leave this here in case anyone else runs into this issue.

Related

GWT requestfactory: How to catch the exception i thrown in Locator at server side?

At client side:
factory.find(proxyId).fire(new Receiver<P>()
{
#Override
public void onSuccess( P response )
{
proxy = response;
...
}
#Override
public void onFailure( com.google.web.bindery.requestfactory.shared.ServerFailure error )
{
Window.alert( error.getMessage() );
}
}
at server side i use an Locator like below:
public class myLocator extends Locator<T, String>
{
#Injector LocatorHook hook;
#Override
public T find( Class<? extends T> clazz, String id )
{
T result = ...;
hook.run( result );
return result;
}
....
}
The hook.run() method may throwRunTimeException("validation exception") there, i expect to catch the
exception at client side in onFailure(), however, i did catch the exception, but the message is "Internal Server Error",
not the exception i thrown in hook.run():"validation exception".
Any ideas to let client catch the exception i throw at server side?
Updation:
As Thomas said it's weird that validating objects that come fresh from data store, but i encounter a
situation that i don't know how to use service method:
At client i get EntityProxyId object, through the factory.find( proxyId ).fire(...) i can get the entity
from datastore, but the entity may not suitable for the user to access, in this situation i need to check it at server side, but i can't find a suitable place to do the
validation, Any ideas about this?
RequestFactory doesn't expect exceptions to be thrown by locators. Exceptions should only be thrown by service methods, and will be directed to the appropriate Receiver on the client-side (the one attached to the service method that threw).
Outside service methods, the only exceptions that gets routed to the client are ReportableExceptions, that can only be thrown from a ServiceLocatorDecorator's report() methods. That means you could hook your own ServiceLocatorDecorator that catches exceptions from your locators and report()s them.
That said, validating objects that come fresh from your data store seems weird. You might want to provide a ServiceLocatorDecorator that overrides validate() (that'll validate the objects after the changes coming from the client have been applied). The errors will go back to the client in the Receiver's onConstraintViolations, and the RequestContext will be unfrozen so you can further edit your proxies and fire() again.

Silverlight 4 Entity framework issue

I have created a sample application in Silverlight with RIA services. I am using entity framework for CRUD operation but it does not work for INSERT Operation with following Exception, "Submit operation failed validation. Please inspect Entity.ValidationErrors for each entity in EntitiesInError for more information." I have not apply any validation but don't know how the error occurs.
I have tested that when I create an object of DB entity and assign values to it and then save by calling object.SaveChages(), it works fine. But its default method does not work. Any help is appreciated.
Thanks
The SubmitOperation callback has an EntitiesInError property which you can use to iterate thru the entities. That's the way of getting the "real" error.
Here's the method I have to show the user what went wrong...
public static bool WasSubmittedOK(SubmitOperation so, string errorMessageHeader, out string errorMessage)
{
errorMessage = string.Empty;
if (!so.HasError)
return true;
so.MarkErrorAsHandled();
errorMessage = "An unknown error has occurred";
if (so.EntitiesInError.Count() > 0)
{
StringBuilder builder = new StringBuilder();
builder.AppendFormat("{0}\r\n", errorMessageHeader);
foreach (Entity item in so.EntitiesInError)
{
#if DEBUG
builder.AppendFormat("\r\nFor {0}", item.GetType());
#endif
foreach (ValidationResult error in item.ValidationErrors)
{
builder.AppendFormat("\r\n- {0}", error.ErrorMessage);
Debug.WriteLine(string.Format("Error in {0}:'{1}'", string.Join(",", error.MemberNames.ToArray()), error.ErrorMessage));
}
}
errorMessage = builder.ToString();
}
else if (so.Error != null) { errorMessage = so.Error.Message; }
return false;
}
Are you able to drill into the validation errors? I actually have an article about this coming in December MSDN Magazine Data Points but I bet you don't want to wait, right? :)
Even if you haven't applied any specific validations, there are things like foreign key contsraints that EF will still check. If you can see what the error is that will be ultimately useful in solving your problem. Debug into the exception. See if there is a DbEntityValidationException available...maybe it's in an innerexceptoin. DbEntityValidationException will have one or more EntityValidationErrors. Each of those contains a list of all of the errors found for one instance. That means expanding the EntityValidationErrors items one at a time and looking at the ValidationError items contained within.

Clientside error (Cannot call method 'nullMethod' of null arguments: nullMethod)

My App engine/ GWT project is spitting out a nasty little pile of stack trace whenever it attempts to return from my login method. I am using GAE version 1.5.0 and GWT version 2.3.0 .
It's a facebook app, so what I've got is this:
The player navigates to the app page.
They click a button, and are redirected to the OAuth authentication page
They are then redirected back to the app, with the authentication token in the query string
I break the query string apart to get the UID, and then use that as the primary key for my Player entity (RPC to app engine backend)
I retrieve the Player entity instance from the datastore, and turn it into a serializable type to return to the client
Epic fail.
When I spit out the exception in a JSAlert, I get a big nasty pile of stack trace (I already was thoughtful enough to compile using "pretty" instead of "obfuscated").
My login function looks like this:
#Override
public ClientPlayer login(String uid) {
PersistenceManager pm=PMF.get().getPersistenceManager();
log.warning(Player.class.getName());
log.warning(uid);
Key k=KeyFactory.createKey(Player.class.getSimpleName(), uid);
Player p;
List<List<Integer>> stats;
try{
p=pm.getObjectById(Player.class, k);
} catch (JDOObjectNotFoundException e){
p=new Player(uid);
p.setKey(k);
pm.makePersistent(p);
} finally {
pm.close();
}
stats=p.getStats();
return new ClientPlayer(p.getUID(),p.getPerm(), p.getDecks(),stats.get(0), stats.get(1), stats.get(2));
}
Unfortunately, due to NDA, I can't link to the app, but here's the output:
Failure to log in because of:
com.google.gwt.core.client.JavaScriptException: (TypeError): Cannot call method 'nullMethod' of null
arguments: nullMethod,
type: non_object_property_call
stack: TypeError: Cannot call method 'nullMethod' of null
at Object.ClientPlayer_1 (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:993:89)
at Object.ClientPlayer_0 (http://*com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:984:18)
at Array.instantiate_1 [as 0] (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:1031:10)
at $instantiate_0 (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10660:34)
at $instantiate (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:1948:10)
at $readObject (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10148:95)
at Object.read_8 [as read] (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10608:10)
at $onResponseReceived (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:10352:247)
at $fireOnResponseReceived (http://*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:5002:5)
at Object.onReadyStateChange (http:/*.com/com.MES.Tap2/A37A2E2E9A65DB1BAAE2BFA42572F7F8.cache.html:5222:5)
The issue was in the use of the IsSerializable interface, or rather my poor understanding of it.
When you create an IsSerialiazable object, it requires a no-argument constructor. I was passing null values from that constructor to the main constructor, so when methods were called on them, null pointer exceptions occurred. This was dumb of me, but hey, it was a learning experience.
In my particular case, it went a little like this...
public class ClientObject implements IsSerializable {
private Object field1;
private Object field2;
private String field3;
public ClientObject(){
this(null, null);
}
public ClientObject(Object arg1, Object arg2){
field1=arg1;
field2=arg2;
field3=arg1.toString()+arg2.toString();
//Error on above line, though not obviously mentioned in the message
}
}
What should have been done was...
public ClientObject(){
this(new Object(), new Object());
}
Hope this helps someone.

Autofac, OrchardProject and AsyncControllers

I'm working on trying to get an AsyncController to work in OrchardProject. The current version I'm using is 2.2.4.9.0.
I've had 2 people eyeball my code: http://www.pastie.org/2117952 (AsyncController) which works fine in a regular MVC3 vanilla application.
Basically, I can route to IndexCompleted, but I can't route to Index. I am going to assume i'm missing something in the Autofac configuration of the overall project.
I think the configuration is in the global.asax: http://pastie.org/2118008
What I'm looking for is some guidance on if this is the correct way to implement autofac for AsyncControllers, or if there is something/someplace else I need to implement/initialize/etc.
~Dan
Orchard appears to register its own IActionInvoker, called Orchard.Mvc.Filters.FilterResolvingActionInvoker.
This class derives from ControllerActionInvoker. At a guess, in order to support async actions, it should instead derive from AsyncControllerActionInvoker.
Hope this helps!
Nick
The Autofac setup looks ok, and as long as you can navigate to something I cannot say that your assumption makes sense. Also, the pattern you are using for initialization in global.asax is used by others too.
The AsyncController requires that async methods come in pairs, in your case IndexAsync & IndexCompleted. These together represent the Index action. When you say you can navigate to IndexCompleted, do you mean that you open a url "..../IndexCompleted"?
Also, and this I cannot confirm from any documentation, but I would guess that AsyncController requires that all actions are async. Thus, your NewMessage action causes trouble and should be converted to an async NewMessageAsync & NewMessageCompleted pair.
I did too needed to have AsyncController which I easily changed FilterResolvingActionInvoker to be based on AsyncControllerActionInvoker instead of ControllerActionInvoker.
But there was other problems because of automatic transaction disposal after completion of request. In AsyncController starting thread and the thread that completes the request can be different which throws following exception in Dispose method of TransactionManager class:
A TransactionScope must be disposed on the same thread that it was created.
This exception is suppressed without any logging and really was hard to find out. In this case session remains not-disposed and subsequent sessions will timeout.
So I made dispose method public on ITransactionManager and now in my AsyncController, whenever I need a query to database I wrap it in:
using (_services.TransactionManager) {
.....
}
new TransactionManager :
public interface ITransactionManager : IDependency, IDisposable {
void Demand();
void Cancel();
}
public class TransactionManager : ITransactionManager {
private TransactionScope _scope;
private bool _cancelled;
public TransactionManager() {
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void Demand() {
if (_scope == null) {
Logger.Debug("Creating transaction on Demand");
_scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = IsolationLevel.ReadCommitted
});
_cancelled = false;
}
}
void ITransactionManager.Cancel() {
Logger.Debug("Transaction cancelled flag set");
_cancelled = true;
}
void IDisposable.Dispose() {
if (_scope != null) {
if (!_cancelled) {
Logger.Debug("Marking transaction as complete");
_scope.Complete();
}
Logger.Debug("Final work for transaction being performed");
try {
_scope.Dispose();
}
catch {
// swallowing the exception
}
Logger.Debug("Transaction disposed");
}
_scope = null;
}
}
Please notice that I have made other small changes to TransactionManager.
I tried the AsyncControllerActionInvoker route as well to no avail. I would get intermittent errors from Orchard itself with the following errors:
Orchard.Exceptions.DefaultExceptionPolicy - An unexpected exception was caught
System.TimeoutException: The operation has timed out.
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.Async.ReflectedAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3f.<BeginInvokeAsynchronousActionMethod>b__3e(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
NHibernate.Util.ADOExceptionReporter - While preparing SELECT this_.Id as Id236_2_, this_.Number as Number236_2_,...<blah blah blah>
NHibernate.Util.ADOExceptionReporter - The connection object can not be enlisted in transaction scope.
So I don't think just wrapping your own database calls with a transaction object will help. The innards of Orchard would have to modified as well.
Go vote for this issue if you want AsyncControllers supported in Orchard:
https://orchard.codeplex.com/workitem/18012

StatusCodeException Vs. RuntimeException in GWT

In my GWT app. I overrode RemoteServiceServlet to check if the session is valid right before the service method is being called. I am trying to throw a RuntimeException("expired session") from the server and I would like the client to catch this exception from the asynccallback onFailure...
In the client I would like to:
Asynccallback:
#Override
public void onFailure(Throwable caught) {
final String message = caught.getMessage();
if (!isNullOrEmptyString(message) && message.contains("expired session")) {
com.google.gwt.user.client.Window.Location.reload();
}
}
However, in the client, the caught object is still a StatusCodeException and the message is still the default "...Exception in the server...". how can I override the exception at least the default message to compare if it was a session expired message I sent from the server?
thanks
Hi Gursel,
Here's my code:
-> Custom RemoteServiceServlet. I'm trying to "intercept" every method before it's invoked. I check the session and throw a RuntimeException if it's already expired. So basically, it is not the declared method that throws the exception but the custom RemoteServiceServlet. It still goes to the "onFailure" in the client async but the Throwable object is still of type "StatusCodeException" without the EXPIRED_SESSION_MSG message. Don;t know how to make this work. Thanks!
public class XRemoteServiceServlet extends RemoteServiceServlet {
private final static String EXPIRED_SESSION_MSG = "ERROR: Application has expired session.";
#Override
protected void onAfterRequestDeserialized(RPCRequest rpcRequest) {
HttpServletRequest httpServletRequest = this.getThreadLocalRequest();
HttpSession session = httpServletRequest.getSession(false);
if (session != null) {
final String sessionIdFromRequestHeader = getSessionIdFromHeader();
if (!isNullOrEmptyString(sessionIdFromRequestHeader)) {
final String sessionId = session.getId();
if (!sessionId.equals(sessionIdFromRequestHeader)) {
throw new RuntimeException(EXPIRED_SESSION_MSG);
}
}
All RuntimeExceptions thrown by Server side of gwt application has been wrapped as StatusCodeException if you did not declare them at your remote method declaration.
EDIT :
After, Thomas Broyer comment, I have learned that all exceptions (checked or unchecked) that are declared at remote method declaration are propagated to gwt client. Therefore all you have to do is just declare your remote method such as :
public void myRemoteMethod() throws RuntimeException;
The post looks too old, still here is the solution I have come up with. Override processCall(String payload) of RemoveServiceServlet, if the session is invalid execute the blow code else call super.processCall(payload).
//Pass the exception to Client
RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass(), this);
onAfterRequestDeserialized(rpcRequest);
return RPC.encodeResponseForFailure(rpcRequest.getMethod(), new CustomException("Invalid Session!"),rpcRequest.getSerializationPolicy(), rpcRequest.getFlags());
All the GWT services are servlets so GWT serializes the custom exception and sends to client as string, we follow the same :)