PDA

View Full Version : Ext.Direct .NET Router



evant
11 May 2009, 8:42 AM
Here's my implementation of a .NET router. After seeing some of the others I've modified my original implementation a bit.

Attached is both the source for the router and a small sample project to demonstrate the functionality.

http://extjs.com/playpen/router/Router-1.0.zip

UPDATED ON 24th FEB

1. Setting up the Api Handler
The API describes which methods are made available to the client. In the .NET version, this is implemented as an ASHX handler. The handler instances a provider object and passes in
a) The name of the provider
b) The url of the handler for this provider
c) An assembly containing the methods for this provider

Upon calling the configure method, reflection is used to find all classes with a DirectActionAttribute. From here, any methods marked with either a DirectMethodAttribute or a DirectFormMethodAttribute will be included in the generated API. A simple caching class has been provided. It is recommended you use this to save generating the API on each call to the handler.

2. The rest

Simply setup your handler ASHX, in this case I've called it DirectHandler.ashx. From here, all we need to do is pass the appropriate provider and the HttpRequest to our processor and it will return a result.

NB: Any form methods will only be passed a single parameter, which is the HttpRequest object. Any other parameters are ignored.


Comments/questions/improvements welcome.

crp_spaeth
11 May 2009, 9:17 AM
Cool! I just gave it a short look... A few more nice ideas! Since I have followed the last .net thread, I am working on a similar implementation too! So I am counting 3 yet...
I have just started an Open Source Project and commited the code from "shibubh" with a small change in the router it self today... What do you think about merging our ideas together?

Two things i got on my list for the shibubh's current Implementation:

- Pack things to a Dll so you dont need to add those aspx files to an existing Project
http://msdn.microsoft.com/en-us/library/system.web.ihttphandler.processrequest.aspx
(makes it possible to add the proxy and router handler via web.config)

- Use JSON.NET for de- and -serialization see: http://www.codeplex.com/Json
Thinking about restrict to the use of Static classes or even support them. (This is importent cause of performance issues in big applications)



So As i have Seen so far your version solves the second point nicely... But what do you think about the first one?


regards

elishnevsky
11 May 2009, 11:11 AM
This is great! Thanks so much, Evan.

evant
11 May 2009, 4:08 PM
RE:

1) You need to do some configuration to get this working, for example, passing in the assembly. I guess you could do that with a config file, however my .NET is rather rusty.

Would you have me include the core stuff in the Ext.Direct dll and then have the user extend the ashx?

2) As you mentioned, it's using JSON.NET internally.

shibubh
11 May 2009, 7:40 PM
hi evant,
thanks for sharing. Cool stuff. i just loved it. i love the you way code it.

boolean
13 May 2009, 8:58 PM
good work, good idea
this is something like ajaxpro,but it's better then ajaxpro.
we can paste the client extjs and the server side c# method with it.

alekdigital
14 May 2009, 5:37 AM
That was great!

But the issue is if you have tons of pages in one assembly that need lots of Ajax function for different purposes, it renders all methods to every page while it might not be needed, so I made some modification (in sample application not Library), hope that helps:

Instead of including ApiHandler.ashx as a script block, I used it as a helper class like this:



public static void Register(Page page) {

DirectProviderCache cache = DirectProviderCache.GetInstance();
DirectProvider provider;

string hash = GetHashKey(page);
string key = "Ext.app.REMOTING_API." + hash;

//After being configured, the provider should be cached.
if (!cache.ContainsKey(key)) {
provider = new DirectProvider("Ext.app.REMOTING_API", "/DirectHandler.ashx?hash=" + hash);
provider.Configure(new object[] { page });
cache.Add(key, provider);
}
else {
provider = cache[key];
}

string script = provider.ToString();
page.ClientScript.RegisterClientScriptBlock(page.GetType(), "ApiHandler", script, true);
}


private static string GetHashKey(Object o) {
return o.GetType().GetHashCode().ToString();
}



The every page that needs this functionality has to call:



ApiHandler.Register(this);


In the Load event.

As this register every page in different Cache key reference I needed to do an alter in Directhandler as well:



public void ProcessRequest(HttpContext context)
{
string key = "Ext.app.REMOTING_API." + context.Request.QueryString["hash"];

DirectProvider provider = DirectProviderCache.GetInstance()[key];
context.Response.ContentType = DirectHandlerContentType;
context.Response.Write(DirectProcessor.Execute(provider, context.Request));
}


By moving these two lines:



Ext.ns('Ext.app');
Ext.Direct.addProvider(Ext.app.REMOTING_API);


Inside of Ext.onReady method in default.aspx page, every thing is ready to go to use functions that are defined in pages. So every page has access to it's own functions only, that help organizing methods.

Note: When registering an action, it gets name of page's type, that returns 'default_aspx' instead of Default, so I had to call:
default_aspx.Echo(.....);

Alek.



PS: adding provider can be moved to Register function in helper class like this:


string script = provider.ToString();
script += @"
Ext.ns('Ext.app');
Ext.Direct.addProvider(Ext.app.REMOTING_API);";

page.ClientScript.RegisterClientScriptBlock(page.GetType(), "ApiHandler", script, true);

So in the page there is no need to register the provider, and it will be ready on load.

Alek.

evant
14 May 2009, 7:34 AM
I've updated the router, new features include:

1) Native date support. Since both Ext.encode() and the native browser JSON encoders send off dates as string, the router will automatically find date arguments in the server method and attempt to convert the parameter to a date.

2) Better exception handling.

3) Added namespace option for provider.

4) Renamed a few files, tidied up the code a bit.

Can find it here: http://extjs.com/playpen/router/Router-0.2.zip

Gunmen
14 May 2009, 9:36 AM
Thank you Evant!

Maybe better to add it below your first message?

elishnevsky
14 May 2009, 12:49 PM
Hi, Evan. Just a quick thing, there's a bug in your implementation of Direct router. A null reference exception is thrown if you call a direct method with no parameters.

The exception is thrown from DirectProvider.cs line 187.


if (request.Data == null && method.Parameters > 0)
{
throw new DirectException("Parameters length does not match");
}
else if(request.Data.Length > 1 && method.IsForm) // <--- EXCEPTION IS THROWN HERE: request.Data is null
{
throw new DirectException("Form methods can only have a single parameter.");
}
else if (request.Data.Length != method.Parameters)
{
throw new DirectException("Parameters length does not match");
}


To fix it I've replaced this piece of code with this:


if (request.Data == null)
{
if (method.Parameters > 0)
{
throw new DirectException("Parameters length does not match");
}
}
else
{
if (request.Data.Length > 1 && method.IsForm)
{
throw new DirectException("Form methods can only have a single parameter.");
}
else if (request.Data.Length != method.Parameters)
{
throw new DirectException("Parameters length does not match");
}
}

nayato
18 May 2009, 6:54 PM
It's always great to see some .net action happening around ExtJS!

Though I think using async handlers is a bit dated approach. Did you think of writing WCF service for that purpose? It could help a lot in writing server-side code in more focused, service oriented manner.
It also has JSON support is built in and provides extended possibilities for easy exception handling.

evant
18 May 2009, 7:13 PM
Not really, I'm a bit out of the loop as far as .NET is concerned. Could you point me to some relevant examples? FYI the built in JSON support in .NET I found was reasonably poor, which is why I ended up using JSON.NET.

Lloyd K
19 May 2009, 1:14 AM
WCF is far too heavy and overcomplicated which will put many off imho. Also I agree with Evan in that .NET's JSON support is poor and the Newtonsoft library is probably the best freely available.

crp_spaeth
19 May 2009, 2:15 AM
I agree with evant for using JSON.NET.

This is much faster and better than the builtin json Support!

If you had a look at my Implementation of a Direct Router you may have seen that its really easy to let JSON.Net try to deserialize the json data to fit into the defined method parameters. This may shows a small part of the power of JSON.NET

Revotion
4 Jun 2009, 11:07 AM
To Start with, I am more of a VB user, so please forgive the fact that this is not in C#. As a feature request, why not create a DirectHandler as an IHTTPHandler that can do all of the work for the user, while grouping related function into a single file (and not having to expose every method in an Application to all pages)

Examples as follows.....


The DirectHandler to be added to the DLL (notice that I wrapped the DirectHandler and ApiHandler into one file, and check for the existance of any data. If none found, we assume you are looking for the API Definition).



Imports Ext.Direct

Public Class DirectHandler
Implements IHttpHandler

Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest

Dim provider As DirectProvider = Nothing
Dim cacheKey As String = GetType(Handler1).Name
Dim cache = DirectProviderCache.GetInstance()

If context.Request.TotalBytes = 0 AndAlso String.IsNullOrEmpty(context.Request("extAction")) Then

context.Response.ContentType = "text/javascript"

If cache.ContainsKey(cacheKey) Then
provider = cache(cacheKey)
Else
provider = New DirectProvider("Ext.app." & cacheKey & "_API", context.Request.Path)
provider.Configure(New Object() {Me})
cache.Add(cacheKey, provider)
End If

context.Response.Write(provider.ToString())

Else

context.Response.ContentType = "text/javascript"

provider = cache(cacheKey)

context.Response.Write(DirectProcessor.Execute(provider, context.Request))

End If

End Sub

ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property

End Class


The Handler file that I would add to my application (remember to add the file as a Genric Handler with the ashx extension) ....




Imports Ext.Direct

<DirectAction()> _
Public Class Handler1
Inherits DirectHandler

<DirectMethod()> _
Public Function Echo(ByVal name As String) As String
Return "Echo: " & name
End Function

End Class






And Finally, the HTML code need to use this....




<script type="text/javascript" src="Handler1.ashx"></script>

<script type="text/javascript">

Ext.onReady(function() {

Ext.Direct.addProvider(Ext.app.Handler1_API);

Handler1.Echo("abc", function(result, response) { alert(result) })

})


</script>

evant
4 Jun 2009, 12:33 PM
Wicked suggestions, I've made these changes, as well as

1) Exception handling
2) Better date handling

I'll update the original post.

GodKnow
14 Jun 2009, 11:27 PM
Hello,

The sample in "ext-direct-pack.zip" response:



{"type":"rpc","tid":2,"action":"Sample","method":"SaveForm","result":"{\"firstName\":\"4\",\"lastName\":\"4\",\"age\":4}"}


The value of "result" is string.

So I modify Directresponse.cs:


return JsonConvert.SerializeObject (response);


replace :


if (response.Result.GetType().ToString() == "Newtonsoft.Json.Linq.JObject")
{
JObject o = new JObject(
new JProperty("type",response.Type),
new JProperty("tid",response.TransactionId),
new JProperty("action",response.Action),
new JProperty("method",response.Method),
new JProperty("result",(JObject)response.Result)
);
return o.ToString();
}
else
{
return JsonConvert.SerializeObject(response);
}



The Sample.cs:


[DirectMethodForm]
public string SaveForm(HttpRequest request)
{
int age = 0;
int.TryParse(request["age"], out age);
JObject o = new JObject(
new JProperty("firstName", request["firstName"]),
new JProperty("lastName", request["lastName"]),
new JProperty("age", age));
return o.ToString(Newtonsoft.Json.Formatting.None);
}



replace:


[DirectMethodForm]
public Object SaveForm(HttpRequest request)
{
int age = 0;
int.TryParse(request["age"], out age);
JObject o = new JObject(
new JProperty("firstName", request["firstName"]),
new JProperty("lastName", request["lastName"]),
new JProperty("age", age));
return o;
}


response:


{
"type": "rpc",
"tid": 2,
"action": "Sample",
"method": "SaveForm",
"result": {
"firstName": "4",
"lastName": "4",
"age": 4
}
}

Please tell me if you have any good idea.

evant
15 Jun 2009, 2:17 AM
I've added something similar, now you can return JArray or JObjects from your methods and have them serialized. Updated the first post.

GodKnow
17 Jun 2009, 6:10 AM
Sorry, I have a question:
In the Router0.4,I need add a class,please tell me how to do that?

thx.


I am doing it like this now:
In the defalut.aspx,add:


<script type="text/javascript" src="test.ashx"></script>


test.ashx:


namespace ExtDirectSample
{
[DirectAction]
public class test : DirectHandler
{

public override string ProviderName
{
get
{
return "Ext.app.REMOTING_API_1";
}
}

public override string Namespace
{
get
{
return "MyApp_1";
}
}

[DirectMethod]
public string SayHello()
{
return "Hello!";
}

}
}

return:


Ext.app.REMOTING_API_1 = {"type":"remoting","url":"/control/ExtDirectSample2/ExtDirectSample/test.ashx"
,"namespace":"MyApp_1","actions":{"test":[{"name":"SayHello","len":0}]}};
thx.

evant
17 Jun 2009, 2:25 PM
What are you trying to do? Sorry I don't understand.

GodKnow
18 Jun 2009, 12:38 AM
Sorry, I didn't make myself clear. What I means is...
I try to add a test class to the "ExtDirectSample" so that it can be seem by the client .
Here is how I do it:
I created a Test.ashx as below:


namespace ExtDirectSample
{
[DirectAction]
public class test : DirectHandler
{

public override string ProviderName
{
get
{
return "Ext.app.REMOTING_API_1";
}
}

public override string Namespace
{
get
{
return "MyApp_1";
}
}

[DirectMethod]
public string SayHello()
{
return "Hello!";
}

}
}


I used different ProviderName and Namespace in class compare to the same items of Sample class in Sample.ashx
Then, I add following code to default.aspx.


<script type="text/javascript" src="test.ashx"></script>


After that client will return results.



Ext.app.REMOTING_API_1 = {"type":"remoting","url":"/control/ExtDirectSample2/ExtDirectSample/test.ashx"
,"namespace":"MyApp_1","actions":{"test":[{"name":"SayHello","len":0}]}};

How to make the client return test class using the same ProviderName and Namespace?

thx.

lucg
19 Jun 2009, 12:59 PM
Thank you Evant and all for the great work.
In my application, I replaced shibubh's Direct .net implementation by Router 0.4.

Since I made the change, my custom type can't be serialized.
Do you know why?

Error message: Unsupported type: TestExtJS.ServiceResponse. Use the JsonSerializer class to get the object's JSON representation.
Thrown from ResultConverter.cs

Custom class returned by DirectMethod:

public class ServiceResponse
{
public bool Success { get; set; }
public string Ex { get; set; }
public int Total { get; set; }
public object Data { get; set; }
}

I'm using .net 3.5

Thanks,
Luc

evant
19 Jun 2009, 6:46 PM
@GodKnow: I see what you're saying now. Let me have a think :-?

evant
19 Jun 2009, 8:27 PM
Ok, I've updated the main post. Basically I've added a few virtual methods that let you decide how to configure a handler, as opposed to just making them a one to one mapping between classes. See Group.ashx for an example of doing this.

GodKnow
20 Jun 2009, 7:00 AM
Ok, I've updated the main post. Basically I've added a few virtual methods that let you decide how to configure a handler, as opposed to just making them a one to one mapping between classes. See Group.ashx for an example of doing this.


This is great! Thanks so much, Evan.

lucg
22 Jun 2009, 7:05 AM
Do I need to create a custom json serializer for custom classes returned from a direct action?

evant
22 Jun 2009, 7:30 AM
Not really sure, I wouldn't think so, though I'm not really familiar with the internals of the JSON framework.

lucg
25 Jun 2009, 12:55 PM
I found out that the serializer was failing because the class ResultConverter was unable to convert my custom types.

I disabled it and now it works fine. What was the purpose of this converter? It was not present in version 0.1.


I also added a JsonConverter array to DirectHandler and modified DirectProcessor to call JsonConvert with the array. It enables the web application to add json converters by direct handler.

Do you intend to put the code online? I would contribute my changes.

Thanks,
Luc

evant
25 Jun 2009, 2:52 PM
The issue was that it wouldn't automatically serialize JObject or JArray types when returned from a method. If you have an alternate solution that makes these play well together that would be great.

lucg
26 Jun 2009, 6:17 AM
Hello evant,

Here is how I could make it works.

1) I renamed ResultConverter to JContainerJsonConverter, made the class public, and changed the CanConvert method to:


return objectType.IsSubclassOf(typeof(JContainer));By doing this, the converter won't get in the way of other types it can't convert.

2) We have to remove the attribute [JsonConverter(typeof(ResultConverter))] from the Result property in DirectResponse class.

3) Add JsonConverters property to DirectHandler:


/// <summary>
/// Retrieves the list of JsonConverter to apply to the json serializer
/// </summary>
public virtual Newtonsoft.Json.JsonConverter[] JsonConverters
{
get
{
return new Newtonsoft.Json.JsonConverter[] {
new JContainerJsonConverter()
};
}
}4) Add JsonConverters parameter to DirectProcessor.Execute in DirectHandler file, line 42:

data = DirectProcessor.Execute(provider, context.Request, this.JsonConverters);5) In DirectProcessor class, apply modifications to Execute method:

public static string Execute(DirectProvider provider, HttpRequest httpRequest, JsonConverter[] jsonConverters)
{
List<DirectResponse> responses = new List<DirectResponse>();
if (!String.IsNullOrEmpty(httpRequest[DirectRequest.RequestFormAction]))
{
DirectRequest request = new DirectRequest();
request.Action = httpRequest[DirectRequest.RequestFormAction] ?? string.Empty;
request.Method = httpRequest[DirectRequest.RequestFormMethod] ?? string.Empty;
request.Type = httpRequest[DirectRequest.RequestFormType] ?? string.Empty;
request.IsUpload = Convert.ToBoolean(httpRequest[DirectRequest.RequestFormUpload]);
request.TransactionId = Convert.ToInt32(httpRequest[DirectRequest.RequestFormTransactionId]);
request.Data = new object[] { httpRequest };
responses.Add(DirectProcessor.ProcessRequest(provider, request));
}
else
{
UTF8Encoding encoding = new UTF8Encoding();
string json = encoding.GetString(httpRequest.BinaryRead(httpRequest.TotalBytes));
List<DirectRequest> requests = JsonConvert.DeserializeObject<List<DirectRequest>>(json);
if (requests.Count > 0)
{
foreach (DirectRequest request in requests)
{
responses.Add(DirectProcessor.ProcessRequest(provider, request));
}
}
else
{
responses.Add(DirectProcessor.ProcessRequest(provider, JsonConvert.DeserializeObject<DirectRequest>(json)));
}
}
if (responses.Count > 1)
{
return JsonConvert.SerializeObject(responses, jsonConverters);
}
else
{
DirectResponse response = responses[0];
if (response.IsUpload)
{
return String.Format("<html><body><textarea>{0}</textarea></body></html>", JsonConvert.SerializeObject(response).Replace("&quot;", "\\&quot;"));
}
else
{
return JsonConvert.SerializeObject(response, jsonConverters);
}

}
}Now, in the DirectHandler class in my application, I can add the override property JsonConverters. Mine looks like this:

public override JsonConverter[] JsonConverters
{
get
{
return new JsonConverter[] {
new Ext.Direct.JContainerJsonConverter(),
new SearchResultJsonConverter()
};
}
}Where JContainerJsonConverter is the converter defined in the Ext.Direct project; and SearchResultJsonConverter is a custom json converter defined in my application. If you don't override the JsonConverters property, it will serialize the same way as before, using the JContainer converter.

I hope that helps. What do you think?

Gunmen
5 Jul 2009, 10:17 AM
I tried the load the ExtDirectSample. All examples work, except the upload form.

Question: how can I load a form?

Without Direct I used:



listeners:{
'render': {
fn:loadForm,
scope:this
}}



function loadForm(form){
switch (form){
case myform:
form.getForm().load({
url:'myform.ashx?a=spi',
method:'post',
waitMsg:'Loading...'
})
break;
}


Can someone provide me an example? Thanks!

evant
5 Jul 2009, 10:42 AM
Not sure what you mean, I just tried it using the example code and I can get the file uploaded no problem.

Gunmen
6 Jul 2009, 12:49 AM
Not sure what you mean, I just tried it using the example code and I can get the file uploaded no problem.

I will check the upload form example in more detail later today.

Any ideas / answers regarding my question?
I can write a funtion that retuns the form data from a database, but how do I load the form / how do I connect the extjs form to the 'direct' function?

evant
6 Jul 2009, 1:10 AM
http://extjs.com/deploy/ext-3.0-rc3/examples/direct/direct-form.php

Gunmen
12 Jul 2009, 2:12 AM
http://extjs.com/deploy/ext-3.0-rc3/examples/direct/direct-form.php

I have checked all the direct examples and are still not able to change your example in such a way that it load a form. :s/:):">

In your next release, is it an idea that you extend your examples with an load function for forms?

In the future I hope to work with grids, paging, and extra parameters... but first simple forms. ;)

Thank you very much!

evant
12 Jul 2009, 7:57 AM
Hi All,

The router in the main post has been updated, which includes:

1) New sample of using Ext direct to load and submit forms.
2) Fixed an issue setting the content type of responses when uploading files. Also added extra code to this sample to show how to return info from a file upload.
3) Updated the code in the example to use Ext 3.0 final (which fixes a few bugs).

Enjoy.

Gunmen
12 Jul 2009, 8:18 AM
Hi All,

The router in the main post has been updated, which includes:

1) New sample of using Ext direct to load and submit forms.
2) Fixed an issue setting the content type of responses when uploading files. Also added extra code to this sample to show how to return info from a file upload.
3) Updated the code in the example to use Ext 3.0 final (which fixes a few bugs).

Enjoy.

Evan, thank you! =D>

I'm on my way to check it out!

ooNxt
12 Jul 2009, 6:33 PM
THX a lot,evan.
Is there a example about editgrid?

evant
13 Jul 2009, 6:10 PM
Not a .NET one, but there's a PHP version in the 3.0 examples, it should be trivial to move it over.

Dave.Sanders
22 Jul 2009, 8:03 AM
Hi Evan,

It looks like its your router that is packaged in the Ext.Direct download, correct?

If so, I have a few additions that I've made today based on the latest in that download. Specifically:

1. I've made it handle and send out exceptions.
2. I've made it handle and sent out events.
3. I've made it so that if a method returns a List<>, each object in the list turns into a response.

The third one may only be useful to me. The case was that I wanted to be able to return the data from the method AND call an event easily. However, in practice while this makes your events nice and maintainable, it might not be useful since it seems that events ALWAYS get called last - so you can't rely on an event being called which might affect how you handle your response.

Would these additions be useful to anyone, and if so, how do you want me to post them up here? (there are a number of changes, so it might not be useful to just post up the code bits.)

Dave.Sanders
22 Jul 2009, 9:28 AM
Also, I've just added a Name property to the attributes. So, you can alias methods / actions in your code. (I can see this as being very useful if someone were switching between platforms and you didn't want to / couldn't rename all of your methods to match.)

So, you would declare an action like:

[DirectAction("user")]
public class UserController
{
[DirectMethod("getUser")]
public string GetUserById(int Id)
{...}
}

and your client side method would be user.getUser instead of UserController.GetUserById

Gunmen
22 Jul 2009, 2:50 PM
@durlabh: I think this thread must continue as intended, namely a central thread for comments, questions, and improvements regarding Evan's Ext.Direct .NET Router. I appreciate your ExtJs related initiatives but please do not use this thread as a 'promotional platform' for your version.

@Dave.Sanders: Name property is a good suggestion.

evant
22 Jul 2009, 5:07 PM
Dave, I like your ideas, however make sure you've got the latest version (first post of this thread).

Dave.Sanders
24 Jul 2009, 7:16 AM
Ok, I've attached my version of the code. I looked around at the latest version, but I think the differences were things that I had modified in my code (in a slightly different way.) If I missed something, please let me know and I'll update my code.

Some highlights: (any line numbers are from my code)

1. I've done a little bit of cleaning up in the logic of DirectProcessor around line 58 just to make it more concise.

2. I've changed out how the JSON is created by using the DefaultValueHandling property on JsonSerializerSettings. (See this thread here (http://json.codeplex.com/Thread/View.aspx?ThreadId=24459) on JSON.Net board - it was a feature I implemented, then James modified a year or so ago.) This, combined with [DefaultValue] attributes added to JsonResponse makes the JSON more concise. If an event is sent back, then you don't get the other unnecessary null fields. (DirectProcessor line 60 and attributes in DirectResponse)

3. I've added DirectEvent, which can be passed back by an action/method to invoke a clientside event in Ext.Direct.

4. I've modified DirectResponse to recognize an exception or an event and to send different response types down to the client. (DirectResponse line 14)

5. I've modified DirectProcessor.ProcessRequest to be able to handle the return of a List<object> from the method, and to create a List<DirectResponse> that it passes back to be serialized. This allows events AND data to be sent down to the client at the same time. (Though caution has to be taken due to how events are ran by Ext.Direct.) (DirectProcessor line 77)

6. I've modified ProcessRequest to catch and handle exceptions. Instead of just handing DirectExceptions like your code does, I handle ALL exceptions, but I've wrapped the try catch in a Preprocessor directive so that the IDE will still catch on errors while in debug mode. You can put it in Release mode to have the errors sent only to the client. (This needs finished to not send down callstack when in Release mode.) (DirectProcessor Line 72)

I think that's it, but you can see why it was easier to just attach my code instead of trying to describe patches. Take a look and let me know what's good / bad. I'd be happy to help integrate these into your code, or vice versa.

Cheers
D

UPDATED: Code files removed. LATEST CODE IS IN THIS POST (http://extjs.com/forum/showthread.php?p=366222#post366222)

Dave.Sanders
25 Jul 2009, 8:05 AM
UPDATE: Attached a new version of the code, there was a slight bug in finding actions / methods by the name you give to the attribute. Which was a feature I forgot to mention:

7. You can abstract the name of the action / method used in the client side away from the server side by specifying a name property when using the DirectAction or DirectMethod attributes. example:

[DirectAction("myAction")]
public class YourAction
{
[DirectMethod("myMethod")]
public string YourMethod
}

would map the clientside myAction.myMethod() to the server side YourAction.YourMethod(). Useful if you really do decide to change your server back end, or having naming conflicts between client side objects and server side objects. I personally use it so that I can name all of my Direct Action classes as "ObjectController" without having to call "ObjectController" on the clientside.

anyway, the previous post had a bug, so I'm updating the code in that post to fix the bug.

Dave.Sanders
25 Jul 2009, 11:06 AM
I have another version of the library now that will pass back the "enableBuffer" property on methods that I propose in this other thread. (http://extjs.com/forum/showthread.php?p=363837)

I don't want to post it up yet because I don't want to muddy the waters between official ext and the ux I'm using for adding this feature to Ext.Direct. But if someone wants it, let me know. The change is pretty straight forward, just add a new property to DirectMethod called enable buffering, and make it render to the json, then use it when you use the DirectMethod attribute when needed.

I would also suggest adding "enableBuffer" at the Provider level so that it conforms with the Ext.Direct interface.

mbajema
29 Jul 2009, 7:42 AM
I am using the Router 0.6 and can get it to work by calling a Direct method from my Javascript code, but when I try to use it to populate a combobox specifying a directFn and baseParams, request.Data in DirectProcessor.ProcessRequest ends up looking like this:

{object[1]}
[0]: {object}

I get the following error:
Error occurred while calling Direct method: Object of type 'System.Object' cannot be converted to type 'System.String'

It doesn't seem to matter what I put in the baseParams--I'd like to send more than one parameter. Can anyone help me?

Dave.Sanders
29 Jul 2009, 8:29 AM
I actually haven't tried the Direct Data store stuff yet, or even really looked at it but:

1. Can we see some code showing how you are calling it and
2. Can we see what is being sent to the server, via Firebug

Then maybe I'll have an idea. I should be getting to trying the data store stuff here in the next day or so.

Dave

mbajema
29 Jul 2009, 9:43 AM
It seems that with the directFn approach, it sends the parameters as a JSON object, so the problem can be reproduced by simply passing a JSON object to a Direct method. For example:


var config = {
param1: 'test1',
param2: 2
};

MyApp.MyDirectMethod(config, function(e, result) {
alert(Ext.util.JSON.encode(result.result));
});
The problem is with the deserialization here:

JsonConvert.DeserializeObject<DirectRequest>(json)It deserializes the JSON object into an {object}, but I'm not sure how to cast that into something usable.

I just tried using durlabh's alternate Ext.Direct .Net implementation (http://extjs.com/forum/showthread.php?t=75311) which uses Microsoft's JavaScript serialization. It deserializes the JSON object into an object that can be cast into a Dictionary<string, object> type.

Dave.Sanders
29 Jul 2009, 10:14 AM
Ah, for anything that I need to send a JSON object to the server, I encode it first:


Ext.encode(myJSONObject);

Then it gets sent up as a string to the server.

In my current case, I am deserializing the string into a dictionary:


JsonConvert.DeserializeObject<Dictionary<string, string>>(FormData.ToString())

With "FormData" being the result of the Ext.encode.

That said - this won't handle cases where the object contains objects, etc.

Dave.Sanders
29 Jul 2009, 10:37 AM
Hey, guess what. I just tried my own DirectStore in my project attached to a combobox and I see your issue now. Or rather, I have the same issue as you - so ignore my last comment about encoding.

Am going to play with it now, will let you know when I have a solution.

Dave.Sanders
29 Jul 2009, 11:10 AM
So basically, the way objects are being deserialized just doesn't work the way its set up. I've posed this question over on James' site (http://json.codeplex.com/Thread/View.aspx?ThreadId=63916) (JSON.Net) to see if there is an out of the box solution, otherwise, the DirectRequest is going to need to have a custom deserializer.

I'm working on it now, will let the group know if I get it working. Unless someone already has an answer to the question I posed over at JSON.Net.

mbajema
29 Jul 2009, 11:28 AM
What would be really cool is if it would not deserialize the parameters when deserializing the DirectRequest object--just leave the parameters as a JSON string--and then attempt to deserialize each parameter in the DirectProvider.Execute() method based on the actual types (defaulting to a JObject if necessary). The SanitizeDates() method in the router 0.6 kind of does that for DateTime parameters.

That way, our Direct methods could look like this:


[DirectMethod]
public string MyDirectMethod(MyCustomConifgClass config)
The way I described it might not make sense, but hopefully you get the picture.

Dave.Sanders
29 Jul 2009, 11:50 AM
That didn't make sense to me. :)

Attached is a new version of my branched copy that does the proper JSON Deserializing on embedded objects. (At least as far as I can tell in my tests.)

So, if you pass in a DirectStore request it will look similar to this:


{"action":"Patient","method":"Search","data":[{"query":"test","start":0,"limit":5}],"type":"rpc","tid":8}

This will be passed to your function as a Dictionary<string, object>. So, in the above case I have this catching method:


public string Search(Dictionary<string, object> Params){}

If I want access to the properties in the dictionary, I just call them like so:


string query = Params["query"];

I would STRONGLY suggest that something like this get added to the base Ext.Direct code. This is pretty important stuff. You might want to encapsulate the Dictionary into a custom Direct object, though I really don't think that's necessary unless you need it to support C# code that can't use generics.

Note: this version of my code includes the EnableBuffer property on DirectMethod and DirectProvider, like I mention in the thread above. I've also added in the MaxRetries property to DirectProvider. (and really need to get in there and specify ALL of the properties that are listed in the RemoteProvider docs)

Let me know if you find bugs or have questions.

Dave

(note: I'm not trying to subvert this thread to promote my own version - rather, I'd like to see these folded into the "official" version. Please steal whatever you need out of my code to bolster the official version, or just code-review my additions and make it version 0.7. Or ignore them entirely.)

UPDATED: code updated to include mbajema's better Object Convertor described below.

UPDATED: Code files removed. LATEST CODE IS IN THIS POST (http://extjs.com/forum/showthread.php?p=366222#post366222)

Dave.Sanders
29 Jul 2009, 11:52 AM
Oh, and it will still be able to handle multiple parameters in the array. So this:


"data":["mystring", 4, {id:1,test2:"test"}]

will be available in your method as:


public string MyMethod(string MyString, int ANumber, Dictionary<string, object> Params){}

If you want to see what I did, take a look at the ComplexDataConverter class. This is assigned to the Data property with the JsonConverter Attribute in DirectRequest.

Dave.Sanders
29 Jul 2009, 12:05 PM
I would also suggest that you create these extension methods somewhere. These will aid you in easily getting the params out of the dictionary. With the below code, you can easily get a string, int, or bool out of the dictionary by calling the correct method.



bool myBool = Params.GetBool("ParamId");
string myString = Params.GetString("ParamId");
int myInt = Params.GetInt("ParamId");


There is an optional "default" parameter too in case the item can't be converted:


int myInt = Params.GetInt("ParamId", -1);

would set myInt = -1 if ParamId was missing, or wasn't an int.

The extensions:



public static int GetInt(this Dictionary<string, object> Input, string Id)
{
try
{
return Convert.ToInt32(Input[Id]);
}
catch
{
return int.MinValue;
}
}

public static int GetInt(this Dictionary<string, object> Input, string Id, int Default)
{
try
{
return Convert.ToInt32(Input[Id]);
}
catch
{
return Default;
}
}

public static string GetString(this Dictionary<string, object> Input, string Id)
{
try
{
return Input[Id].ToString();
}
catch
{
return null;
}
}

public static string GetString(this Dictionary<string, object> Input, string Id, string Default)
{
try
{
return Input[Id].ToString();
}
catch
{
return Default;
}
}

public static bool GetBool(this Dictionary<string, object> Input, string Id)
{
try
{
return Convert.ToBoolean(Input[Id]);
}
catch
{
return false;
}
}

public static bool GetBool(this Dictionary<string, object> Input, string Id, bool Default)
{
try
{
return Convert.ToBoolean(Input[Id]);
}
catch
{
return Default;
}
}

mbajema
29 Jul 2009, 1:16 PM
The ComplexDataConverter class was not handling arrays within the object, so I updated it to be a more generic ComplexObjectConverter class (it doesn't need to start as an array). Here it is:



using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace Ext.Direct
{
internal class ComplexObjectConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (objectType == typeof(object[]) || objectType == typeof(object)) { return true; }
return false;
}

public override object ReadJson(JsonReader reader, Type objectType)
{
if (reader.TokenType == JsonToken.StartArray)
return ReadArray(reader);
else if (reader.TokenType == JsonToken.StartObject)
return ReadObject(reader);
else
return null;
}

private object[] ReadArray(JsonReader reader)
{
ArrayList output = new ArrayList();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
return output.ToArray();
}
if (reader.TokenType == JsonToken.StartArray)
{
output.Add(ReadArray(reader));
}
if (reader.TokenType == JsonToken.StartObject)
{
output.Add(ReadObject(reader));
}
else
{
output.Add(reader.Value);
}
}

throw new JsonReaderException();
}

private object ReadObject(JsonReader reader)
{
Dictionary<string, object> subobject = new Dictionary<string, object>();
string currentKey = null;

while (reader.Read())
{
if (reader.TokenType == JsonToken.EndObject)
{
return subobject;
}
else if (currentKey == null)
{
currentKey = reader.Value.ToString();
}
else
{
if (reader.TokenType == JsonToken.StartArray)
{
subobject.Add(currentKey, ReadArray(reader));
}
else if (reader.TokenType == JsonToken.StartObject)
{
subobject.Add(currentKey, ReadObject(reader));
}
else
{
subobject.Add(currentKey, reader.Value);
}
currentKey = null;
}
}
return null;
}

public override void WriteJson(JsonWriter writer, object value)
{
throw new NotImplementedException();
}
}
}
It could use some better exception handling, but it's a start.

Dave.Sanders
29 Jul 2009, 1:37 PM
Excellent! I've incorporated this into my code instead of my version of the converter, and re-posted the code above.

Thanks!
Dave

Dave.Sanders
30 Jul 2009, 4:43 AM
Yet another update. The JSON serialization was not serializing datetime fields into the format needed by Ext. I've updated the serializer to use the JavaScriptDateTimeConverter per this article from James. (http://james.newtonking.com/archive/2009/02/20/good-date-times-with-json-net.aspx)

This latest version of the code is attached here. To not have any confusion, I'm going to go back to the old posts and point them here for the code. Hopefully we can just get this rolled into a .7 of the "official" code so there is less confusion. :)

evant
30 Jul 2009, 5:34 AM
FYI I am still monitoring this thread, however there's some other stuff that's a bit higher priority at the moment. Perhaps we could setup a google code project.

Dave.Sanders
30 Jul 2009, 7:27 AM
Is this router considered the "official" router for .NET and ExtJs by Ext? Or is this completely a community thing?

I'm happy to contribute to a google code project if one is set up. Just let me know. I _think_ its pretty well set, but I'm continuing to convert my project over to Ext.Direct, so I might trip across other issues.

Dave

mbajema
30 Jul 2009, 8:18 AM
Ok, back to what I was getting to my post about passing the JSON through and then deserialize each parameter into the actual types of the Direct method...

This needs some serious revision, but I'm just hacking to make it work right now. The object[] Data property is still being deserialized with the ComplexObjectConverter, but I'm overwriting it later. I added a string DataJson property to the DirectRequest class and populate it using a helper function and modified code in the DirectProcessor.Execute method as shown here:

Helper function:


private static string[] GetDataJson(JObject o)
{
JArray data = (JArray)o["data"];

string[] dataJson = new string[data.Count];
for (int i = 0; i < data.Count; i++)
{
string j = JsonConvert.SerializeObject(data[i]);
dataJson[i] = j;
}

return dataJson;
}


Updated portion of DirectProcessor.Execute:


else
{
UTF8Encoding encoding = new UTF8Encoding();
string json = encoding.GetString(httpRequest.BinaryRead(httpRequest.TotalBytes));
JsonSerializerSettings jsettings = new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Auto };
List<DirectRequest> requests = JsonConvert.DeserializeObject<List<DirectRequest>>(json, jsettings);
if (requests.Count > 0)
{
JArray jRequests = JArray.Parse(json);

int idx = 0;
foreach (DirectRequest request in requests)
{
request.DataJson = GetDataJson((JObject)jRequests[idx]);
responses.Add(DirectProcessor.ProcessRequest(provider, request));
}
}
else
{
DirectRequest request = JsonConvert.DeserializeObject<DirectRequest>(json, jsettings);

JObject o = JObject.Parse(json);
request.DataJson = GetDataJson(o);

responses.Add(DirectProcessor.ProcessRequest(provider, request));
}
}


And then in the DirectProvider class, I added a function that deserializes each value in the DataJson array to the actual types in the Direct method and call it from a modified DirectProvider.Execute() method as shown here:

New function:


private void SetDataTypes(MethodInfo method, DirectRequest request)
{
int idx = 0;
foreach (ParameterInfo p in method.GetParameters())
{
object o = JsonConvert.DeserializeObject(request.DataJson[idx], p.ParameterType);
request.Data[idx] = o;

idx++;
}
}


Updated portion of DirectProvider.Execute:


try
{
object[] parameters = { request.Data };

if (!method.IsForm)
{
this.SetDataTypes(method.Method, request);
parameters = request.Data;
}
return method.Method.Invoke(type.Assembly.CreateInstance(type.FullName), parameters);
}
catch (Exception ex)
{
throw new DirectException("Error occurred while calling Direct method: " + ex.Message);
}


And this way, my Direct method can have complex custom classes as parameters. This is a lot easier to use than parsing nested Dictionary<string,object> objects. For example:



[DirectMethod]
public string MyDirectMethod(MyComplexCustomClass myObject)


Any thoughts? Does this seem like a good idea?

mbajema
30 Jul 2009, 9:42 AM
I'm not sure why I hacked that together like I did, but I now see that it would be much easier/cleaner to simply make the Data property of the DirectRequest a JArray type rather than an object[] type. This could then be re-serialized and deserialized into the proper types within the DirectoProvider class.

mbajema
30 Jul 2009, 9:56 AM
Then again, the DirectRequest fails to deserialize when Data is a JArray (I'm not sure why). I'm actually in way over my head here.

Dave.Sanders
30 Jul 2009, 12:03 PM
I see where you are going, I think, and think that its the next natural progression. Let me restate what you said in a simpler way and see if I'm on base:


You want to be able to specify custom types for objects that are passed to your direct method.

You want Ext.Direct to look at the method before deserializing the passed in JSON.

You want Ext.Direct to attempt to deserialize the json property into the object type listed in the method.


This makes sense to me, as long as there was a fall back of Dictionary<> or some similar style name / value pair object - if its an object, or a string. It would actually be pretty slick, in places that it makes sense, to basically just pass through a business object from the client side, through the JSONConverter, then have it right there in your method to save, etc.

I'm doing something sort of similar, but in my case I'm taking name / value pairs of form data from the dictionary and running a little process to populate my business objects. I sort of like having the extra layer of abstraction there in case I need it, but there might be places that I'd like to pass an object straight through. Would be a time saver at least.

So, were you able to get this all working, or no? If not, I can take a look at the code again and try to figure out how I'd implement it.

Dave

mbajema
31 Jul 2009, 6:15 AM
You explained it much better than me. I do have a working implementation (as I described in the previous post), but like I said, it is a hack. As for falling back to a Dictionary<>, it handles that just fine, because if Dictionary<> is the type of the parameter in your Direct method, it will attempt to deserialize to that (maybe I'm not following you).

I will post my code when I have a chance to code it properly.

By the way, this doesn't work for form-type Direct methods. Ext won't send a complex JSON object along with the form values. Instead, I just serialize my object and send it as a string along with the form values.

mbajema
31 Jul 2009, 6:29 AM
By the way, the deserializer in the latest version of Json.Net 3.5 changed enough to break this Ext.Direct .Net implementation.

It fails on this line:

List<DirectRequest> requests = JsonConvert.DeserializeObject<List<DirectRequest>>(json, jsettings);

My guess is that this just needs to move into a try/catch block and that this line would work if the json really was a batch of requests, but I haven't tested that.

Dave.Sanders
31 Jul 2009, 6:43 AM
Same here on the forms. I'm using some of my own code that will grab all field objects from a higher object down, and create a parameters object to pass along. I do this because I do some custom stuff in the client code with validation and with checking to see if a reason for the update is needed.

I'm still waiting on confirmation, but I posed this question to James at Newtonsoft and he (I think) is making some Json.net changes so that objects that look like this in JSON will get converted into JObjects and JArrays automatically. So, once that code is ready and eventually into Ext.Direct then some of my issues MAY be moot.

Will keep you up to date if I hear anything.

Dave.Sanders
31 Jul 2009, 6:45 AM
By the way, the deserializer in the latest version of Json.Net 3.5 changed enough to break this Ext.Direct .Net implementation.

Yeah, I saw that, but I didn't pursue it further since that version of Json.Net is beta still. If James adds in the bit about JObject and Jarray like I talked about, then it might be worth grabbing the latest and fixing the issue. Of course, technically we should wait until its out of "beta". :)

mbajema
5 Aug 2009, 7:27 AM
FYI: In order to access the Session in Direct methods, your generic web handler needs to implement IRequiresSessionState or IReadOnlySessionState (for read only access).

For example, your handler should look like:


public class MyHandler: DirectHandler, IRequiresSessionState

Dave.Sanders
5 Aug 2009, 9:48 AM
I don't use sessions, but I do use Forms authentication. So, what I do is:

1. In my Direct handler, I get the user object from the Forms Auth and add that to the Request context:


HttpContext.Current.Items["user"] = User.GetByUsername(HttpContext.Current.User.Identity.Name);

(User.GetByUsername is a function in my data model. The HttpContext.Current.User is set by my form auth code)

Then, all of my controllers (the methods I call from Ext.Direct) inherit from a common base object where I go to the context to get my user:


protected User user
{
get
{
return (User)HttpContext.Current.Items["user"];
}
}

so, that's another way to get data from the "front-end" of your web request to the "back-end" where your controllers live.

mbajema
5 Aug 2009, 10:01 AM
I have never used HttpContext.Current.Items before--that's a good thing to know about.

I don't know how expensive the call to User.GetByUsername() is, but with your approach, I'm assuming it gets called for every request--correct?

With a session, the User object would persist in memory between requests.

Dave.Sanders
5 Aug 2009, 10:40 AM
Well, User does go to the database, but there is some caching involved with the ORM I'm using - though I don't know if it works across requests.

However, I don't, on principle, use Sessions. If you need to spread your web app across multiple servers, you will have to remove sessions or will have to have them stored somewhere central to the web servers - like a database server. At which point, sessions aren't buying you anything because you have to go to the central server anyway.

So, for me its best to just ignore sessions and just hit the DB every time - trusting my ORM, ADO and the database server to be able to handle the process as efficiently as it can.

williware
20 Aug 2009, 6:39 AM
Is this router considered the "official" router for .NET and ExtJs by Ext? Or is this completely a community thing?

I'm happy to contribute to a google code project if one is set up. Just let me know. I _think_ its pretty well set, but I'm continuing to convert my project over to Ext.Direct, so I might trip across other issues.

Dave

I'm new to the forum and did not see an answer to this. Looks like great improvements to the official code. After repeated efforts, the DavesExtDirectBranch.zip in an earlier post still doesn't unzip for me.

Is there an alternative link where I can download the improvements. Any chance these will be rolled in to the "official" version.

Thanks

Dave

Dave.Sanders
20 Aug 2009, 6:57 AM
Strange, it downloads and unzips fine for me...

I've unzipped and rezipped it and placed it here - see if this copy works.

http://vulgrin.com/DavesExtDirectBranch.zip

williware
20 Aug 2009, 7:45 AM
:D Thanks, that did the trick.

Whatty
21 Aug 2009, 7:25 AM
Does anyone know if there is a corresponding Java implementation for the integration of ExtJS

Dave.Sanders
21 Aug 2009, 8:22 AM
You can see all of the stacks out there on this sticky.

http://extjs.com/forum/showthread.php?t=67992

Whatty
21 Aug 2009, 8:38 AM
TY

fastwings
23 Aug 2009, 5:51 AM
i build two roters
one (http://pastebin.com/d6428da67)and two (http://pastebin.com/m3c87180d)
and also the code (http://pastebin.com/d9dd06a8)

here (http://pastebin.com/d10af931d) its how i try use at
and my problem that he don't loaded the second handler he say something like that


uncaught exception: Ext.data.DataProxy: DataProxy attempted to execute an API-action but found an undefined url / function. Please review your Proxy url/api-configuration.
[Break on this error] (no source for )
[Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIChannel.contentType]" nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)" location: "JS frame :: file:///C:/Program%20Files%20(x86)/Mozilla%20Firefox/components/FeedProcessor.js :: FP_onStartRequest :: line 1440" data: no]
[Break on this error] channel.contentType = "application/vnd.mozilla.maybe.feed";\n
as well herre the output of the first handler

Ext.app.REMOTING_API = {"type":"remoting","url":"http://localhost:6970/Services/Admin/Ext/Router.ashx","actions":{"Gridcontrol":[{"name":"GetCategorys","len":2,"formHandler":false}]}};

can some tell how i can fix at what i missed
where any leads?

williware
24 Aug 2009, 5:51 AM
When adding another handler in the same namespace, I have been foiled by the built in caching. Turn off the caching, or reset IIS. Maybe that can help.

thomson
25 Aug 2009, 2:16 AM
Can somebody let me know which is the latest Router Version?

Thanks in Advance
Thomson

Dave.Sanders
25 Aug 2009, 2:58 AM
Latest "official" code is in post #1 (the 0.6 version)

My latest code with the additions I've mentioned in this thread, which I don't believe the Ext team has had time to evaluate and include, are in this message:

http://extjs.com/forum/showthread.php?p=366222#post366222

Or on my site at:

http://vulgrin.com/DavesExtDirectBranch.zip

because someone had a problem unzipping the version I uploaded.

I would have started a new thread for my "branch" but the changes aren't all that different and I'd rather they just get included in the original code. There was talk about setting up a google code for it, and if someone at Ext wants to do that then I'll happily seed it with my latest copy to eliminate confusion.

D

thomson
25 Aug 2009, 4:21 AM
Hello All,
I have tried using the router version 6 i have tried calling a function using Ext.Direct

In firebug i can see there is a post happening to the handler and immediately i am getting the

uncaught exception: Ext.data.DataProxy: DataProxy attempted to execute an API-action but found an undefined url / function. Please review your Proxy url/api-configuration.

I am loading the store using the following code


var mystore = new Ext.data.DirectStore({
paramsAsHash: false,
root: 'Rows',
api: {
load: MyApp.Sample.GetOrder('labelle')
},
//directFn: ,
idProperty: 'id',
autoLoad: true,
fields: [
{ name: 'id' },
{ name: 'name' }


],

listeners: {
load: function(s, records) {
Ext.MessageBox.alert("Information", "Loaded " + records.length + " records");
},
beforeload: function(t, params) {
Ext.MessageBox.alert(t);
},
exception: function(current, action, rs, params) {
Ext.MessageBox.alert(action);

}
}
});

var grid = new Ext.grid.GridPanel({
store:mystore,
cm: new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer(),
{ header: "+/-", width: 120, sortable: true, dataIndex: 'id' },
{ header: "Sel", width: 70, sortable: true, dataIndex: 'name' },

]),

viewConfig: {
forceFit: true
}

});

The Response will be having a Result attribute which having my json, How do i tell the reader to take only that

Thanks in Advance

Thomson

Dave.Sanders
25 Aug 2009, 6:37 AM
I don't understand the "api" property you have on your directstore. Here's an example of one of mine:



noteStore: new Ext.data.DirectStore({
directFn : Note.GetNotes,
idProperty : 'NoteId',
root : 'notes',
fields : ["NoteId", "ShortText", "NoteText", "Updated", "Username"]
}),


This calls a method that gathers up the note objects and then packs them into a custom JSON object that I use in my C# code. The method returns that, which gets turned into the JSON below and sent back to the browser.



{"type":"rpc","tid":5,"action":"Note","method":"GetNotes",
"result":
{"total":2,"notes":[{"ShortText":"test note asdf asfasdf","NoteId":7295,"SampleId":21704,"NoteText":"test note asdf asfasdf","Updated":new Date(1250429820000),"Username":"dsanders"},

{"ShortText":"booga","NoteId":7296,"SampleId":21704,"NoteText":"booga","Updated":new Date(1250429820000),"Username":"dsanders"}]}}]

(I'm not using 0.6 btw, I'm using my branch of it, so I don't use ParamsAsHash, etc.)

thomson
25 Aug 2009, 8:27 PM
Hello Sanders,
Can you send me a sample of your code using the Ext.Direct version of yours

Thanks
Thomson

thomson
25 Aug 2009, 8:47 PM
Hello Sanders,
Can u give an example using Dave ExtBranch, I just replaced your dll in the router 6 , now its complaining of missing DirectHandler

Thanks

Thomson

Dave.Sanders
26 Aug 2009, 7:10 AM
FYI, I think there is a bug in the sample. The sample is setting the response content type to "text/javascript". This is fine for the majority of the operations, but it makes Ext.Direct throw up parsing errors when you try to do a file upload. In the sample, here are the two lines:


private const string DirectHandlerContentType = "text/javascript";
...
context.Response.ContentType = DirectHandlerContentType;

I took out the assignment in my code and it seems to be working all around - so I think this was an unnecessary step anyway. Will report back if its not.

What's funny is that the third form of the sample (the one where you upload an image file) was failing silently. The file would get uploaded to the code, but if you notice, the callback code never ran. Just escaped notice until now I guess.

D

Dave.Sanders
26 Aug 2009, 10:20 AM
To Mark: (mbajema)

Concerning your ComplexObjectConverter code from waaaay back on post #57, there is a bug. In ReadArray, those should really be If...else if...else statements, not separate Ifs.

I found a bug where if you are sending in an array of vars from the client, it was adding on a null object to the list of parameters in Request Data. This was making the processor blow up because it would be looking for the wrong number of arguments to pass to the DirectMethod. If you change the function to read like this, the problem goes away:


private object[] ReadArray(JsonReader reader)
{
ArrayList output = new ArrayList();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
return output.ToArray();
}
else if (reader.TokenType == JsonToken.StartArray)
{
output.Add(ReadArray(reader));
}
else if (reader.TokenType == JsonToken.StartObject)
{
output.Add(ReadObject(reader));
}
else
{
output.Add(reader.Value);
}
}

throw new JsonReaderException();
}

Cheers
D

Dave.Sanders
27 Aug 2009, 11:13 AM
Hello Sanders,
Can u give an example using Dave ExtBranch, I just replaced your dll in the router 6 , now its complaining of missing DirectHandler


It's Dave. :) The examples given in the Direct Pack from Ext should work with my branch of Ext.Direct. My code doesn't change the interfaces, it just adds some features as listed in posts #44, #45 and #46 of this thread.

Gunmen
30 Aug 2009, 2:28 AM
@Evant: Still monitoring this thread? I hope so.

Can you tell me where do I find the most recent version of your example code? In the Ext.Direct Pack (http://extjs.com/products/extjs/download.php) or in the first post of this thread?

Thank you very much.

evant
30 Aug 2009, 5:32 PM
The latest version of mine is in the first post.

Dave.Sanders
2 Sep 2009, 12:08 PM
Evan: any more thoughts of Google Coding this router so that we can merge your version and mine and make one stack that we can both update and keep up to date?

Cheers
D

IHaveRead
19 Sep 2009, 1:17 PM
If anyone is interested i'm sharing my JsonResposeFilter, an IHttpModule which replaces invalid date strings on the server side, thus removing some load from the client.

Thanks to OP for that great Router.

Matt.Posgate
11 Nov 2009, 3:09 AM
Hi, this looks great to me. I'm new to Ext Js though and am struggling to get a Listview to work with this mechanism. Could anybody post an example?

amorworx
11 Nov 2009, 6:55 AM
@evant

Found a minor bug in ResultConverter.cs when using Json.NET 3.5r5



using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Ext.Direct
{
internal class ResultConverter : JsonConverter
{

public override bool CanConvert(Type objectType)
{
//always return true
return true;
}

public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer)
{
//no custom reading here
throw new NotImplementedException();
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JContainer o = value as JContainer;
if (o != null)
{
writer.WriteRawValue(o.ToString(Formatting.None));
}
else
{
writer.WriteValue(value);
}
}

}
}


The ReadJson and WriteJson methods must include "JsonSerializer serializer" as a third parameter

cgountanis
13 Nov 2009, 12:19 PM
Is this the current .NET recommended method? I was using some code that included DirectHandler.ashx and you could write everything in classes. I tried updating the Ext.Direct.dll from this for debug and now I get 'Ext.Direct.DirectProvider' does not contain a constructor that takes '2' arguments. Can this be explained to me and can you point me to the stable version of the .NET Ext.Direct supported method?

cgountanis
16 Nov 2009, 2:00 PM
My issue was mixing the DLL from your post up with the application on the 3.0.3 downloads page. The Ext.Direct.Pack.... what is more current should we use that pack as a template or your router example which does not include the same layout. They are quite different and I want to use the more stable method as a base.

Please let us all know as this is not the first time this question has come up.

Dave.Sanders
16 Nov 2009, 4:18 PM
Which exact version are you trying to use? The one that is attached on the first post from evan, the one from the download page, or the one that I posted up later on in the thread with a bunch of additions that I'd like to see in the "official" version?

D

Dave.Sanders
16 Nov 2009, 4:23 PM
Oh, and as far as I know - the version that's in this thread is the latest - I don't know if anyone updated the zips for the example routers, they aren't really maintained the same way the rest of Ext is I don't think. There was talk at some point of putting the Ext.Direct router on Google Code so the community could manage it, but I don't know if that was ever resolved.

cgountanis
16 Nov 2009, 9:12 PM
Ah... so the .6 in the first post is the recommended method and template then? I started with the one on the downloads page and it sets up 2 ASHX pages like api and direct handler so you can use class files directly without all the setup but the debugging is a pain in the butt. I don;t mind having my DirectMethod in a ASHX file like the .6 example here... just didn;t know what I should use for production.

Dave.Sanders
17 Nov 2009, 3:50 AM
Yeah, I don't know that I've ever used the one from the zip - I've always used 0.6.

If you search back through the posts here in this thread, I uploaded a newer version (consider it a fork for now) that adds some features and reworks a couple things. (which are again mentioned further up in the thread) It's based off of 0.6 and includes some features that I hope they will include in their version.

Good luck
D

dancardillo
17 Nov 2009, 12:38 PM
Question for Mr. Sanders... does your version handle the upgraded Newtonsoft library to r5? I have run into a problem ever since upgrading where the JsonConvert.SerializeObject method now throws an error:

Expected a JsonObjectContract or JsonDictionaryContract for type 'System.Collections.Generic.List`1[Ext.Direct.DirectRequest]', got 'Newtonsoft.Json.Serialization.JsonArrayContract'.

Dave.Sanders
17 Nov 2009, 3:44 PM
Heh, well then I think that answers that question... :)

I haven't messed with my Json.net in a while on my current project, using this code so I haven't run into it yet. Probably a minor tweak to the interface when its calling to serialize or deserialize something.

If someone wants to fix it and repost, that'd be awesome. Otherwise I'll get to it as soon as I can.

vrassouli
18 Nov 2009, 6:44 AM
Hi, can anyone tell me how to use this library (router) with a paging grid?
thanks

enumoran
20 Nov 2009, 4:16 AM
Hi, can anyone tell me how to use this library (router) with a paging grid?
thanks

Yep, I would like to know it too.

evant
23 Nov 2009, 3:53 PM
To all:

The correct version is in the first post, 0.6.

I'll try to get a paging example up soon.

Whatty
18 Dec 2009, 5:00 AM
Good morning,

I am wondering if you happen to have been following the DirectJngine implementation specifically the integration with Spring.

I would love to have the same type of integration with the Ext.Direct on the .NET side with Spring support but am reluctant to crack it open and start making modifications especially if the base code changes underneath me.

In particular I would love to see extensions similar to what the DirectJngine supports so that I can extend the behavior at certain key points, thereby enabling Spring based object lookup and automagical session scope management (i.e. session stuffing).

Additionally, I have a project with many dependent sub-projects where most of code actually lives and I have had to bring my action classes up to the base project just so the ApiHandler can resolve to the [DirectAction] classes, it would be nice to be able to provide so sort of sub-assembly support.

Whatty

PS: This message was directed at the original authors of the Ext.Direct implementation

Gunmen
26 Dec 2009, 10:45 AM
To all:

The correct version is in the first post, 0.6.

I'll try to get a paging example up soon.

Cool! I love to see an update of the original Ext.Direct .NET Router. =D>

There are now so many implementations that it is difficult to know what a good starting point is for Ext.Direct and .NET. Also therefore, I would like to keep using this 'Ext-connected' version of the Ext.Direct .NET Router. I will keep monitoring this thread. B)

dcrysler
12 Jan 2010, 11:09 AM
Hello,

I am new to Ext JS and I have tried running several of the Ext.Direct for .Net samples and I must be doing something wrong. The samples run however every click results in a 30-60 second delay before anything happens. Even the initial page load is extremely slow. I am running the samples on a Windows XP workstation. I do have the 1.1, 2, 3, adn 3.5 frameworks loaded on my machine. I've also tried running the sample on a Windows 2003 SR2 server and they run a little faster but not much. Is there anything that I need to do besides configuring an IIS application to point to one of the samples in order the sites to run?

lauralxj
12 Jan 2010, 8:02 PM
loader: new Ext.tree.TreeLoader({
directFn: Lauralxj.Method.GetTrees,
paramsAsHash: true,
//paramsOrder:['node','param1','param2'],
listeners: {
'beforeload': function (loader, node) {
this.baseParams.param1 = 'value1';
this.baseParams.param2 = 'value2';
}
}

}),


Firebug Info:



{"action":"Method","method":"GetTrees","data":["0",{"param1":"value1","param2":"value2"}],"type":"rpc"
,"tid":2}


Server Code


[DirectMethod]
public JArray GetTrees(string ID,Object obj)
{

List<DirectTree> nodes = new List<DirectTree>();
for (int i = 1; i < 10; i++)
{
DirectTree node = new DirectTree();
node.id = i.ToString();
node.href = "#";
node.leaf = true;
node.text = "逆ヘ" + p.Param1;
nodes.Add(node);
}

return JArray.FromObject(nodes);
}


HOW TO GET Parameter obj 'S VALUES? Thanks!

AndreaCammarata
15 Jan 2010, 1:48 AM
Hi guys!
I'm developing a big ux component and i need some help.
Could someone give me a hand to this post?
http://www.extjs.com/forum/showthread.php?t=89742

I read all the forum thying to solve this but nothing really helps me.
Thank you guys!

Gunmen
16 Jan 2010, 6:37 AM
Hi Evant,

Any idea when you 0.7 is comming out? :-?

Thanks!

fnazarios
21 Jan 2010, 7:42 AM
I am with difficulties to implement yours branch in project Ext.Direct Router. Can help me?

Gunmen
22 Jan 2010, 10:11 AM
Until absense of a good and more ExtJs supported grid paging example, here (http://www.extjs.com/forum/showthread.php?t=7966) you can find a little example. It's not perfect but better than nothing and maybe you can learn from it.

fnazarios
22 Jan 2010, 10:31 AM
-Gunmen (http://www.extjs.com/forum/member.php?u=6802)

Tanks reply!

I've got a serious problem to fill a combobox by DirectStore.

C# Code:


[DirectMethod]
public JArray CentroExpedicao()
{
//JObject test = new JObject(
// new JProperty("value", "Teste do cacete!")
//);
JArray test = new JArray(new JObject(
new JProperty("value", "caceteeeee")
));
return test;
}
Javascript Code:


var storeCentro = new Ext.data.DirectStore({
storeId: 'open-tickets',
directFn: MyApp.Handler1.CentroExpedicao,
writer: new Ext.data.JsonWriter({encode:true}),
reader: new Ext.data.ArrayReader({
fields: ['value']
}),
idProperty: 'value',
fields: ['value']
});

new Ext.form.ComboBox({
fieldLabel: 'Centro de Expediçao',
name: 'ddlCentroExpedicao',
id: 'ddlCentroExpedicao',
displayField: 'value',
valueField:'value',
typeAhead: true,
triggerAction: 'all',
mode: 'remote',
emptyText: 'Escolha o Centro de Expedição...',
store: storeCentro

})
Response:

{"type":"rpc","tid":2,"action":"Handler1","method":"CentroExpedicao","result":[{"value":"caceteeeee"}],"message":null}
In the ComboBox appear:
http://img689.imageshack.us/img689/4548/comboresult.jpg

Can someone help me! (:|:-/:-/

Gunmen
7 Feb 2010, 5:18 AM
Is ExtJs or Evan Trimboli still supporting this thread / .NET Router implementation?

Thank you very much!

evant
7 Feb 2010, 4:31 PM
Yes, it's still being supported.

It has nothing to do with direct, looks like your reader definition is incorrect.

Gunmen
8 Feb 2010, 1:49 AM
Okay, good to know that you are still online and that you not close an eye on our struggles. B)

Gunmen
8 Feb 2010, 12:50 PM
To everybody,

Did someone test the Evan's Group.ashx example in combination with an extjs application? There is no official example and I can't get it to work.

Thanks!

Gunmen
9 Feb 2010, 11:09 AM
Hi Evan!

Never give up! ;)

I installed Visual Studio and found out that Visual Web Developer Express was not the problem. After trying all kind of options I found the solution. I use multiple .js files to structure my work and did not add Ext.Direct.addProvider(Ext.app.USER_API); in the first loaded file but in my last loaded (main) .js file. So, now I can use your group router code. B)


Next problem. When I submit a form, how can I use the funtions / options: isValid, waitTitle, waitMsg, success, and failure?

This is my old way of working... and is not working anymore / yet:


handler:function(){
if(Ext.getCmp('f_login').getForm().isValid()){
Ext.getCmp('f_login').getForm().submit({

waitTitle:'Connecting',
waitMsg:'Connect...',
success:function(form, action){

r = Ext.util.JSON.decode(action.response.responseText);
// ...
},
failure:function(form, action){
// ...


EDIT: have it working! Gonna love ExtJS!

Thank you!

Gunmen
10 Feb 2010, 8:41 AM
Also to Evan: I tried to use the most recent version of Newtonsoft.Json.dll (http://www.codeplex.com/json/Release/ProjectReleases.aspx). However, this version is not working. Therefor we have to use your distributed version of Newtonsoft.Json.
Just to inform you in case you didnt know.

Gunmen
14 Feb 2010, 9:46 AM
Hi Evan!

Still going strong? I need some assistance of you...

I have made a lot of progress with extjs and your router code. Now, I want to display data in a panel with a pagingtoolbar. The data must be visualised with a template, as shown like in this extjs dataview (http://www.extjs.com/deploy/dev/examples/view/data-view.html) example.

I have several questions: Do I need a json store or a reader? How do I connect the store/reader with your .NET Router code? And, how to apply the pagingtoolbar with a total record counter? How to send parameters to fine tune the query?

Unfortunately, your .NET Router example does not include a dataview, paging, or json reader example -and- I'm not that die hard developer as most of you. With some good examples, reading the docs and forum posts I can make big steps (as I did in the past).

Evan, do you want to help me -and others- further with new examples (in the area of dataview, paging, and reader)? An update of your .NET Router will be highly appreciated. It will also save a lot of questions and 'drop outs'.

Awaiting your reply,

Thanks you very much!

stalek
15 Feb 2010, 6:45 AM
Hi Gunmen,
I think I have a solution for your problem with the newest Newtosoft.JSON library. See my post here:
http://www.extjs.com/forum/showthread.php?t=91953

Regards,
Alex

Gunmen
15 Feb 2010, 8:59 AM
Hi Gunmen,
I think I have a solution for your problem with the newest Newtosoft.JSON library. See my post here:
http://www.extjs.com/forum/showthread.php?t=91953

Regards,
Alex

Hi Stalek, I doubt that because I added the newest Newtosoft.JSON library in Evan's project and the compilation of that project resulted in an error. Did does not happen with the original distributed library.

stalek
16 Feb 2010, 12:28 AM
Hi Gunmen,
I took the newest version of Direct (currently it's in version 0.6) and I see your problem.

To solve it do 2 steps:

- modify ResultConverter.cs file (class) by appending serializer parameter on third position.
It should look like in the code snippet below:



internal class ResultConverter : JsonConverter
{

public override bool CanConvert(Type objectType)
{
//always return true
return true;
}

public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer)
{
//no custom reading here
throw new NotImplementedException();
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JContainer o = value as JContainer;
if (o != null)
{
writer.WriteRawValue(o.ToString(Formatting.None));
}
else
{
writer.WriteValue(value);
}
}
}


- apply the change from my post about the problem with DirectProcessor.Execute method.
I mean this code snippet:



// ...
else
{
UTF8Encoding encoding = new UTF8Encoding();
string json = encoding.GetString(httpRequest.BinaryRead(httpRequest.TotalBytes));

List<DirectRequest> requests = new List<DirectRequest>();

try
{
requests = JsonConvert.DeserializeObject<List<DirectRequest>>(json);
}
catch
{
}

if (requests.Count > 0)
{
foreach (DirectRequest request in requests)
{
responses.Add(DirectProcessor.ProcessRequest(provider, request));
}
}
else
{
responses.Add(DirectProcessor.ProcessRequest(provider, JsonConvert.DeserializeObject<DirectRequest>(json)));
}
}


Should work, because it works on my machine ;)
Does it mean that I earned "Works On My Machine" certificate ;) ?

Regards,
Alek

Gunmen
16 Feb 2010, 12:42 AM
Hi Alek, thanks for your support. I will give this a try. Certificate? I have given you a reputation point. ;)


I also hope that Evan will reply -someday- to keep his work up to date for new and ambitious users.

stalek
16 Feb 2010, 3:37 AM
Hi Gunmen,
please comment/remove such attribute in file DirectResponse.cs to make things working also:

[JsonConverter(typeof(ResultConverter))]

Regards,
Alek

evant
24 Feb 2010, 12:13 AM
UPDATE!

New router is out, the link in the first post has been updated.

Includes:

1) Added aliasing
2) Added timeout/retries/namespace options
3) Added autoNamespace option, for creating the namespace of your router eg. MyApp.MyRouter. This defaults to true
4) Added several converters to transform data more easily
5) Added the ability to specify custom converters
6) Added the ParseAsJson attribute, which lets you get the method data as either a JObject or JArray.
7) Redid all of the examples
8) Added the new JSON.NET library
9) A few other minor things

Whatty
24 Feb 2010, 4:36 AM
Evan,

I updated the previous verion of the 0.6 router to include Spring integration and annotated session support.

Unfortunately this required that I modify the base classes of the ExtDirect to accomplish this.

Now that you have updated the base library I need to execute this integration again, obviously not a good situation to be in.

I have attached my source so that you can take a look at what I have done, in particular in the class DirectProvider I have attached hooks so that I can delegate creation of the DirectActions to the Spring framework, falling through to the original code if the object is not managed by Spring, I have also added session support through annotation similar in nature to the DirectJNgine support on the Java (yes I work on both platforms at the same time).

It would be nice to provide an API / configuration option for ExtDirect such that object lifecycle / creation could be delegated to an external framework (such as Spring) otherwise the default would apply. This way as the ExtDirect framework evolves, any external integrations would not have to re-integrate. (I face a similar problem on the Java side with DirectJNgine and have already posted a similar response to Pagullo update http://www.extjs.com/forum/showthread.php?p=437084#post437084 6:53 pm - coincidentally posted yesterday as well).

Please take a look and let me know what you think and how we could potentially proceed in allowing this type of external integration into the base ExtDirect framework.

Whatty

Gunmen
24 Feb 2010, 10:18 AM
UPDATE!

New router is out, the link in the first post has been updated.



=D>B)=D>B)=D>B)=D>B)

Thanks for the update and support! I'm going to start immediately.

mbajema
24 Feb 2010, 11:21 AM
Great work! I was able to seamlessly replace the version I had hacked away at to make it work for my needs with this new version and it worked perfectly. I'm happy to be using the pseudo-official version of the .NET router now.

Gunmen
24 Feb 2010, 12:14 PM
@Evan, I receive an js error in IE (Id, string or number is expected) when opening the Grid and Form examples. However, the examples do work in Mozilla Firefox. I'm running the examples with the Visual Studio .NET web server.

Anyone else having this problem?

UPDATE: For IE, you need to remove the comma after 'direction: 'ASC',' in all js files in the Grid directory and the comma after 'company: 'Ext',' in the form.js file in the Form directory. (Big up to myself :D)

Thanks!

evant
24 Feb 2010, 4:22 PM
Ah, duh. Didn't test it on IE. I'll fix this up.

evant
24 Feb 2010, 4:34 PM
Thanks guys, got it updated.

lauralxj
24 Feb 2010, 8:01 PM
Firebug Infomation:


{"action":"Method","method":"GetTrees","data":["0",{"param1":"value1","param2":"value2"}],"type":"rpc"
,"tid":2}


SERVER Method


[DirectMethod]
public JArray GetTrees(string ID,Object obj)
{
//HOW TO GET obj's Values
}

please tell me how to do!Thanks!

evant
24 Feb 2010, 8:10 PM
By default it parses it as a dictionary, check it out using the debugger.

codeable
17 Mar 2010, 1:53 PM
I have been trying out your implementation and I generally like it. I have two questions/feature requests. (I will do them myself if needed).

1) [Minor]
---------------------------
Add an id to the provider to simplify JS access to the provider.
In JavaScript It would be nice to be able to do:


Ext.Direct.getProvider(id);

I have been adding the id myself, quick and dirty like so:



Namespace.To.Handler.id = "Namespace.To.Handler";
Ext.Direct.addProvider(Namespace.To.Handler);


Likely place to implement it is:



//Ext.Direct.DirectHandler::ProcessRequest

provider = new DirectProvider()
{
Name = this.ProviderName,
Url = context.Request.Path,
Namespace = this.Namespace,
Timeout = this.Timeout,
MaxRetries = this.MaxRetries,
AutoNamespace = this.AutoNamespace,
ID = this.GenerateID(); // or something similar. <-------
};


2) [Major]
---------------------------
Add the ability to inject/autoprocess/overwrite meta data along with the requests. Let me try to explain without the meta data injection, then the proposed solution with the injection. (this example is a bit contrived for demonstration purposes)

Assume I have method:

GetCurrentView(Guid clientInstanceToken);
This method simple returns the name of the current view for a particular browser window instance(same session). The client calls this from JS using its direct provider and passes in some guid that it created when the page was first loaded. This allows the user to have multiple pages open within the same session but render different results.

In the current architecture, one simply has to add the clientInstanceToken to every method to make this work. I would like to have the ability to add this functionality "behind the scenes"; that is, inject the parameter clientInstanceToken to all methods with (perhaps) the MetaDataParameterAttribute("clientInstanceToken") like so:


[DirectMethod,MetaDataParameterAttribute("clientInstanceToken")]
public string GetCurrentView(Guid clientInstanceToken);

or


[DirectMethod,MetaDataParameterAttribute("clientInstanceToken")]
public string GetCurrentView();


Then the direct provider can add the new metadata parameter to the Ext.Provider instance it creates along with a mechanism to retrieve its value. This could be a simple function in JS that the consumer must process:


Ext.Direct.getProvider(id).acquireMetaData = function(args){ //return the metadata here;
}


There are many ways to "present" the meta data to the DirectAction instance, here are a few quick ideas:
1) If the parameter "clientInstanceToken" exists, then simply populate it with the value.
2) If the parameter does not exist, have a dictionary in the DirectHandler that contains the metadata values for the current request:


base.MethodMetaData["GetCurrentView"]

With this type of functionality, any method could contain meta-information that can be applied in an aspect oriented way. Which makes security, identification, concurrency, etc much simpler to implement.

Let me know what you think, I will likely be integrating these ideas sometime next for my next project, so hopefully I hear something before then!

evant
17 Mar 2010, 7:00 PM
1) Easy enough, sure.

2) Interesting idea. The problem is that I can't just implement it in the .NET router. It would actually require changes to the framework to allow the acquisition of the meta data.

codeable
18 Mar 2010, 8:51 AM
I agree that some injection/non-supported code would be required for it to work on the client side. (therefore the feature would be tied to specific versions of Ext) But all in all, it can be done without touching the Ext libraries and would only need to be updated if the Ext.Direct plumbing changed. We don't need to add additional features, we only need to inject parameters before its sent to the server, then strip those back out before presenting it to .NET.

In fact, I have a working example of the client side fully functional without the .NET portion. I simply respond to the "beforecall" event and add the metadata parameters there. It currently works fine with or without the parameter specified:


Path.To.GetCurrentView("GUID-VALUE-HERE",function(data, trans) {
// Process Results
});
Path.To.GetCurrentView(function(data, trans) {
// Process Results
});


The only issues I found while making this work was the callback was lost when all parameters are not specified. I simply recreated the callback after injecting the proper parameters and all works well.

codeable
18 Mar 2010, 9:26 AM
Thought I'd post the code sample to demonstrate how I got it working. This is from my current sandbox, so the names are not the same as in previous examples, but you'll get the idea. [Edit: Viewers take note that this example is not fully implemented and is only a proof of concept. If EvanT doesn't implement it fully, I will later and post my changes]



// -<Temporary .NET Workarounds> ---------------------------
// ID Fix for retrieval
Saturn.Remote.UserHandler.id = "Saturn.Remote.UserHandler";
// Mechanism to get param count
Saturn.Remote.UserHandler.getMethodArgumentLength = function(action, method) {
//TODO: convert to faster mechanism (dictionary-style instead of iteration)
for (var mthd inthis.actions[action]) {
if (this.actions[action][mthd].name == method) {
returnthis.actions[action][mthd].len;
}
}
}
// -</Temporary .NET Workarounds> ---------------------------
// -<Consumer Specifics> ---------------------------
var dummy = 0;
Saturn.Remote.UserHandler.acquireMetaData = function() {
dummy++;
return"Change #"+dummy;
}
// -</Consumer Specifics> ---------------------------
// -<Standard Implementation> ---------------------------
Ext.Direct.addProvider(Saturn.Remote.UserHandler);
// -</Standard Implementation> ---------------------------
// -<Client Side Upgrades> ---------------------------
Ext.Direct.getProvider("Saturn.Remote.UserHandler").on("beforecall", preProcessCall);
// -</Client Side Upgrades> ------------
// -<Ext.Direct .NET Upgrades> ---------------------------
function preProcessCall(provider, transaction) {
//TODO: Currently only supports the first parameter as MetaDataParam
if (Ext.isDefined(provider.acquireMetaData)) {
var mLength = provider.getMethodArgumentLength(transaction.action,transaction.method);
if (transaction.args.length == mLength + 1) { // its fully defined, replace values
transaction.args[0] = provider.acquireMetaData();
transaction.data[0] = transaction.args[0];
} else { // its not full, we need to fix it.
transaction.args.splice(0, 0, provider.acquireMetaData()); // Add the missing parameter
transaction.data = transaction.args.slice(0, mLength); // fix the data
var hs = transaction.args[mLength], scope = transaction.args[mLength + 1]; // fix the callback [duplicate code]
transaction.cb = scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs;
}
}
return true;
}
// -</Ext.Direct .NET Upgrades> ---------------------------

codeable
18 Mar 2010, 12:13 PM
The only other thing I found while ensuring the architecture fits my needs, was the exception handling in the request. I require a bit more info about exceptions so I can handle them on the client side. I have adjusted two basic bits:

1) Added support for InnerException to DirectException.
------------------------------------------------------
Providing the inner exception allows the ability to send the exception to the client. Where it can be processed with some intelligence.

a) Add support for InnerException


public class DirectException : ApplicationException
{
...
public DirectException(string msg, Exception innerException)
: base(msg, innerException)
{
}
...
}


b) Add support for sending the error type to the client


internal class DirectResponse
{
...
[JsonProperty(PropertyName = "implementationexceptiontype")]
public string ImplementationExceptionType
{
get;
set;
}
...
}


c) Pass along the inner exception to the client


// DirectProcessor.ProcessRequest()
private static DirectResponse ProcessRequest(DirectProvider provider, DirectRequest request)
{
DirectResponse r = new DirectResponse(request);
try
{
r.Result = provider.Execute(request);
}
catch (DirectException ex)
{
r.ExceptionMessage = ex.Message;
r.Type = DirectResponse.ResponseExceptionType;
if(ex.InnerException != null)
{
r.ImplementationExceptionType = ex.InnerException.GetType().ToString(); //TODO: convert to JS object
}
}
return r;
}


2) Skipping over any TargetInvocationExceptions thrown.
-------------------------------
Since all exceptions raised in client code will result in the target exception, I decided to simply ignore it and pass the actual exception raised.


// DirectProvider.Execute()
internal object Execute(DirectRequest request)
{
...
try
{
...
}
catch (TargetInvocationException tex)
{
throw new DirectException("Error occurred while calling Direct method: " + tex.InnerException.Message, tex.InnerException);
}
catch (Exception ex)
{
throw new DirectException("Error occurred while calling Direct method: " + ex.Message, ex);
}

evant
18 Mar 2010, 4:28 PM
I definitely think it would be a good idea to be able to attach certain metadata to calls (like a user id). However I'd prefer to wait until later, Direct will be having some work done on it for the 4.0 release, so I'll keep your suggestions in mind for then.

codeable
18 Mar 2010, 7:34 PM
Sounds good to me. I'll just upgrade this version for now and await 4.0!

codeable
19 Mar 2010, 3:29 PM
I've implemented my ideas! The only thing the client-side needs to do is provide an acquireMetaData method. Full client side would be:


// -<Consumer Specifics> ---------------------------
function acquirePageIdentifier(action, method) {
return pageIdentifier;
}
Saturn.Remote.UserHandler.acquireMetaData = acquirePageIdentifier;
// -</Consumer Specifics> ---------------------------

Ext.Direct.addProvider(Saturn.Remote.UserHandler);



The Server side of things has two Attributes: DirectMetaParameter and DirectMetaParameterIgnore. With these two attributes you can define the metadata on the Action level or Method level as these examples will demonstrate.

Implicit parameter called pageContextID


// C#
[DirectMethod, DirectMetaParameter("pageContextID")]
public string GetImplicitMetaParameter()
{
// simply returns the provided metaparameter
object pageContextID = base.GetMetaParameter("GetImplicitMetaParameter", "pageContextID");
return pageContextID.ToString();
}

// JS
Saturn.User.GetImplicitMetaParameter(function(data, trans) {
// handle results
});


Or the same implicit parameter at the Action level (will add to all DirectMethods)


[DirectAction("User"), DirectMetaParameter("pageContextID", false)]
public classUserHandler : DirectHandler
{
[DirectMethod]
public string GetImplicitMetaParameter()
{
// simply returns the provided metaparameter
object pageID = base.GetMetaParameter("GetImplicitMetaParameter", "pageContextID");
return pageID.ToString();
}
}


You can use the ignore to skip a method when the class level attribute is used


[DirectAction("User"), DirectMetaParameter("pageContextID", false)]
public classUserHandler : DirectHandler
{
[DirectMethod, DirectMetaParameterIgnore("pageContextID")]
public string GetImplicitMetaParameter()
{
// there is no pageContextID Available here.
}
}


You can also explicitly provide the metadata parameter like so:


[DirectMethod, DirectMetaParameter("pageContextID", true)]
public string GetImplicitMetaParameter(object pageContextID)
{
// the provided value is overwritten based on the attribute "overwrite" option
return pageContextID.ToString();
}


Anyhow, just thought I'd share my excitement.

Mjollnir26
23 Mar 2010, 2:26 AM
Just a quick note: This implementation doesn't compile out of the Box
with the latest RC of the JSON.NET Lib (3.5.6 RC 6 http://json.codeplex.com/releases/view/37810),
but it's pretty easy to fix (just expanding the Parameter Lists of a few methods).

Going to give it a try now and thanks for your work evant :-)

Lloyd K
23 Mar 2010, 4:03 AM
Ahh yeh I tried that but didn't yet fix it. Thanks for the heads up.

Mjollnir26
23 Mar 2010, 4:40 AM
No Problemo. BTW Fixing the Compile Errors ain't enough, the Newtonsoft Lib must have changed quite a bit compared to the one that's included in the Download. Not even the Echo sample works with the new Lib...
Don't have too much time to investigate that tough and will stick with the provided binary for now. But it would be super sweet to have this fixed...

Mjollnir26
23 Mar 2010, 5:04 AM
Or if there's no time to fix it (i think evant might be a really busy guy) he could tell us which one of the 6 "3.5" Beta-Versions from Codeplex he was using when creating the Router. The shipped assembly says only "3.5" as Version for the Router.

Don't want to be bitchy, it's just that i like to compile everything from source if i can... >:)

evant
23 Mar 2010, 5:32 AM
I can easily update it, but it's only a couple of weeks old. I wonder what changed.

I'll check it out tomorrow.

Mjollnir26
23 Mar 2010, 5:45 AM
So you are saying that u where using the Beta 5 release? (The one from October 2009) ?

evant
23 Mar 2010, 5:51 AM
I'm using whatever is included in the zip, I don't recall the version.

Mjollnir26
23 Mar 2010, 5:55 AM
The (your) zip just says 3.5 but not which RC it is... however...

another thing i just noticed (i 'm just starting to use Direct so please bear with me):

Why is it that a "Read" Operation returns the data in the format suitable for a JSONStore?
Is this specific to your examples or is this by design?

For large amounts of data, that's not gonna work as there would be a tremendous amount of overhead. I'd rather have the response like this:



{
"fields": [
"foo",
"bar",
"test"
],
"data": [
[
"foo",
"bar",
"test"
],
[
"foo",
"bar",
"test"
],
[
"foo",
"bar",
"test"
]
]
}
Could this be accomplished without miss-using the JSON-Store?

evant
23 Mar 2010, 4:23 PM
I'm not sure what you're getting at.

Mjollnir26
24 Mar 2010, 12:20 AM
Ok i'll try to clarify:

1) I downloaded the Router-Package, "installed" the Example-Solution and tested it -> WORKED FINE
But i like to always have the latest version of my libs and to compile myself if i can, so i linked
the Ext.Direct Assembly to the latest (few weeks old) build of Newtonsoft which didn't work for TWO reasons:

First it didn't compile, but fixing that was a piece of cake (or so it seemed), just had to change a few
Parameter-Lists of Methods which didn't do anything in the first place.

Second, after getting it to compile, i tried the Echo-Sample -> BROKE
Seems something has changed internally in the Newtonsoft Assembly and i don't have time (or the knowledge) to investigate that.
That's why i wanted to know the exact Newtonsoft-Version used by you.

2) Which leads my to my second point about the Ext.Direct Server Calls.
The point i was trying to make here is, that IMHO returning "table-ish" Data, (an array of objects which share the same structure)
as JSON-Objects is overhead because each "row" or "record" contains the complete Field definition over and over again.

My current Project will have to deal with big grids, lots of data etc. so i wanted to ask if that behaviour could be changed / extended / overriden
to account for these cases, where JSON-Objects are just not appropriate.

Other than that, thanks for your work and for taking the time to answer :)

Sorry if my writing was too confusing, i'm not a native Speaker.

gianmarco
29 Mar 2010, 9:12 AM
Same problem here, after fixing and compiling Ext.Direct project with JSON.NET 3.5r6 (just downloaded from http://json.codeplex.com), i get a runtime exception.

Someone has fixed the issue?




1) I downloaded the Router-Package, "installed" the Example-Solution and tested it -> WORKED FINE
But i like to always have the latest version of my libs and to compile myself if i can, so i linked
the Ext.Direct Assembly to the latest (few weeks old) build of Newtonsoft which didn't work for TWO reasons:

First it didn't compile, but fixing that was a piece of cake (or so it seemed), just had to change a few
Parameter-Lists of Methods which didn't do anything in the first place.

Second, after getting it to compile, i tried the Echo-Sample -> BROKE
Seems something has changed internally in the Newtonsoft Assembly and i don't have time (or the knowledge) to investigate that.
That's why i wanted to know the exact Newtonsoft-Version used by you.

gianmarco
29 Mar 2010, 12:57 PM
Same problem here, after fixing and compiling Ext.Direct project with JSON.NET 3.5r6 (just downloaded from http://json.codeplex.com), i get a runtime exception.

Someone has fixed the issue?

Ok the problem seems fixed by replacing in DirectProcessor.cs this code:


List<DirectRequest> requests = JsonConvert.DeserializeObject<List<DirectRequest>>(json);
if (requests.Count > 0)
{
JArray raw = JArray.Parse(json);
with this code


if (json.StartsWith("[") && json.EndsWith("]"))
{
List<DirectRequest> requests = JsonConvert.DeserializeObject<List<DirectRequest>>(json);
JArray raw = JArray.Parse(json);
evant, will be nice to have a new version of Router with this patch and updated JSON.NET (currently 3.5r6)

evant
29 Mar 2010, 2:36 PM
I don't mind updating it, but is there any reason you can't just compile it yourselves? The source is all there.

gianmarco
30 Mar 2010, 5:47 AM
i've already compiled it, this is for having this patch applied in the next version, so i don't have to reintegrate it.

gianmarco
30 Mar 2010, 6:18 AM
evant, under what license is the code released?

evant
30 Mar 2010, 7:37 AM
I've updated the router online with:

1) The new id property on the provider.
2) The latest JSON.NET release.
3) The license text (MIT).

gianmarco
30 Mar 2010, 8:55 AM
hmm, the link on the first page point to the old version. Where is the updated version?

evant
30 Mar 2010, 9:18 AM
No, it's correct.

gianmarco
30 Mar 2010, 10:46 AM
There must be some problem (caching? i'm not behind a proxy).
I'll retry tomorrow

angel.ignacio.colmenares
13 Apr 2010, 5:37 PM
hi, great job

only one question:

how to handle Session State in server side code ?

evant
13 Apr 2010, 7:10 PM
Same as you always would. Can you post an example of what you want to do?

angel.ignacio.colmenares
14 Apr 2010, 5:21 AM
thanks for your answer!

in your Grid sample in DirectHandler.ashx.cs i want to create conecction to DB only one time and
keep it in a session var:

SQLiteConnection dbconn ;
object o = Session["dbconn"];
if(o!=null){
dbconn = new SQLiteConnection(connectionString);
Session["dbconn"]=dbconn;
}
else{
dbconn=(SQLiteConnection)o;
}


if( dbconn.State==ConnectionState.Closed ) {
dbconn.Open();
}
....

for now i see that i have to create a new connection object every time because there is not way to use Session State

Mjollnir26
14 Apr 2010, 5:32 AM
Hmmm i just noticed sth strange, not sure if this is a bug...

Just did a "List" Method in one of my Handlers which returns an Array of Business-Objects which each have two DateTime-Proprties on them...
The Objects get serialized to JSON like so:


"Bis":new Date(2556054000000),

This is not that bad, as it shows the correct Date in JS but it breaks silently if

Ext.USE_NATIVE_JSON = true;

Neither the Callback set on the "Load" Method of the DirectStore nor the "Exception" Listener are firing.. i think it silently dies deep in the Bowels of Ext.decode...

For now i've set USE_NATIVE_JSON to false and everything is working fine now but that can't be the final solution. Anybody has any idea about what i can do about the Dates? Would sending them as "long" and then putting the "date" Type on the Store-Field help?

evant
14 Apr 2010, 6:10 AM
@angel

Did you implement System.Web.SessionState.IRequiresSessionState?

Mjollnir26
14 Apr 2010, 6:19 AM
@angel
Caching a Database-Connection in the Session is almost certainly a bad idea,
do you need that for SQLite?

angel.ignacio.colmenares
14 Apr 2010, 6:36 AM
thanks very much
yess !
not it works ok , just wrote :
public class GridHandler : DirectHandler, System.Web.SessionState.IRequiresSessionState
there is some docs to read ?

evant
14 Apr 2010, 6:50 AM
The .NET docs... If you need to access the session in the handler, you need to implement that interface.

Mjollnir26
14 Apr 2010, 7:23 AM
Or you could just do



HttpContext.Current.Session["foo"]

angel.ignacio.colmenares
14 Apr 2010, 9:53 AM
that's not for SQLite, i'am working with Firebird DB.
can you tell me how to avoid using Session for bdConnection? some links to go ?

angel.ignacio.colmenares
14 Apr 2010, 9:58 AM
i mean docs about your work : Ext.Direct !

gianmarco
14 Apr 2010, 1:14 PM
I've updated the router online with:

1) The new id property on the provider.
2) The latest JSON.NET release.
3) The license text (MIT).

Just (re)downloaded the zip, but still doesn't work with the latest json.net version (3.5r6).

Some post ago i've written about the step needed to make the router works with the latest version of json.

Would be nice to have this applied to the "official" router, so i can restart using it instead of my fork.

Bucs
20 Apr 2010, 9:06 AM
Hello, just getting started with Direct router for .Net., after a couple years of rolling my own ashx handlers to return JSON.

Is this latest version of the .Net Direct Router from post #1 (1.0) compatible with the latest release of ExtJS (3.2)?

Thanks.

Mjollnir26
20 Apr 2010, 11:52 AM
I've been using it with the Current Ext-Version and it's working really great.
Just look at the provided Examples, they are a great source of Information.

Regards,
Mjollnir

Mjollnir26
26 Apr 2010, 2:32 AM
@evant, care to elaborate on my Problem with the "new Date()" in Combination with USE_NATIVE_JSON as described here?

http://www.extjs.com/forum/showthread.php?68161-Ext.Direct-.NET-Router&p=457761#post457761

evant
26 Apr 2010, 3:04 AM
Pretty much just need to send it as a long. I could update the code so that it gives you an option on how to serialize the date types, so that they are in
a) new Date(foo/bar/baz)
b) epoch

Mjollnir26
26 Apr 2010, 3:32 AM
Ah ok, will try the "long" Solution then...
As for having two Methods to choose from, that'd be great.

evant
26 Apr 2010, 7:03 AM
In fact, you can already do this. Look at the protected ConfigureConverters function, as well as GetConverters in DirectHandler.cs.

Mjollnir26
26 Apr 2010, 7:17 AM
Shame on me for not finding that earlier and thx to you for pointing it out! :)

Completly unrelated: I modified the Source of DirectMethod (see the Constructor) to not only provide the Number of Params, but to provide more detailed Info about the Params too. This provides a little more "reflection" on the Client-Side and would allow to generate a basic Testing-Console for Direct-Apps... just an idea for now and a bit hackish too... what do you think about it?



using System;
using System.Collections.Generic;
using System.Linq;

using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Ext.Direct
{
internal class DirectMethod
{

/// <summary>
/// Creates an instance of this class.
/// </summary>
/// <param name="method">The method information.</param>
internal DirectMethod(MethodInfo method)
{
this.Method = method;
this.IsForm = Utility.HasAttribute(method, typeof(DirectMethodFormAttribute));
this.Name = Utility.GetName(method);
this.ParseAsJson = Utility.HasAttribute(method, typeof(ParseAsJsonAttribute));

IEnumerable<ParameterInfo> paramInfos = method.GetParameters().OrderBy(i=>i.Position);
this.Parameters = paramInfos.Count();
this.ParamInfos = new JArray();

foreach (ParameterInfo pi in paramInfos)
{
this.ParamInfos.Add
(
JObject.FromObject
(
new
{
name = pi.Name,
position = pi.Position,
type = pi.ParameterType.ToString(),
defaultValue = pi.DefaultValue
}
)
);
}
}

/// <summary>
/// Gets the method info.
/// </summary>
internal MethodInfo Method
{
get;
private set;
}

internal JArray ParamInfos
{
get;
private set;
}


/// <summary>
/// Gets whether the method is a form method;
/// </summary>
internal bool IsForm
{
get;
private set;
}

/// <summary>
/// Gets the name of the method.
/// </summary>
internal string Name
{
get;
private set;
}

/// <summary>
/// Gets the number of parameters for the method.
/// </summary>
internal int Parameters
{
get;
private set;
}

internal bool ParseAsJson
{
get;
private set;
}

internal JContainer GetParseData(JObject requestData)
{
object[] attrs = this.Method.GetCustomAttributes(typeof(ParseAsJsonAttribute), true);
if (attrs.Length > 0)
{
ParseAsJsonAttribute attr = attrs[0] as ParseAsJsonAttribute;
if (attr != null)
{
try
{
JArray data = (JArray) requestData["data"];
if (attr.AsArray)
{
return (JArray)data[0];
}
else
{
return (JObject)data[0];
}
}
catch (Exception ex)
{
}
}
}
return null;
}

/// <summary>
/// Write API JSON.
/// </summary>
/// <param name="jw">The JSON writer.</param>
internal void Write(JsonTextWriter jw)
{
jw.WriteStartObject();
Utility.WriteProperty<string>(jw, "name", this.Name);
Utility.WriteProperty<int>(jw, "len", this.Parameters);

jw.WritePropertyName("paramInfos");
jw.WriteStartArray();
foreach (var info in ParamInfos)
{
jw.WriteRaw(JsonConvert.SerializeObject(info));
}
jw.WriteEndArray();

if (this.IsForm)
{
Utility.WriteProperty<bool>(jw, "formHandler", true);
}
jw.WriteEndObject();
}

/// <summary>
/// Checks whether the passed method is a direct method.
/// </summary>
/// <param name="mi">The method to check.</param>
/// <returns>True if the method is a direct method.</returns>
internal static bool IsMethod(MethodInfo mi)
{
return Utility.HasAttribute(mi, typeof(DirectMethodAttribute));
}

}
}

Bucs
29 Apr 2010, 3:59 PM
Just getting ready to look into Direct .Net Router for a project I am beginning. I was wondering if anyone is using the .Net Router with the EF (3.5) and serializing their EF entities into JSON objects and transferring to the client and loading into ExtJS controls.

Is this working fine? What if the EF entity has nested objects, can those get serialized as well?

Thanks

wolly
10 Jun 2010, 8:42 PM
when i use your router,i build my project use my namespace, but Ext.Direct.addProvider(Sample.Remote.EchoHandler) error!why?

angelrand
27 Jul 2010, 6:20 AM
it's so wonderful work
thanks evan

franklt69
29 Jul 2010, 1:00 PM
question, if I am using handler + session and the session expired how I can redirect?

example:



[DirectMethod]
public OperationSerializer Load()
{
if ((string)HttpContext.Current.Session["MerchantID"] != null)

{
OperationCollection ocoll = new OperationCollection();
string merchantID = (string)HttpContext.Current.Session["MerchantID"];
return new OperationSerializer(ocoll, 0, 0, merchantID);
}
else
{
//i have to return an OperationSerializer, i can't redirect i get: Erro Cannot implicitly convert type 'void' to OperationSerializer'

// return new OperationSerializer(null, 0, 0, "0");
//HttpContext.Current.Response.RedirectLocation = "../login.aspx";
//return HttpContext.Current.Response.End();
}
}



regards
Frank

evant
30 Jul 2010, 1:15 AM
You can't, you're returning the data via Ajax. You'll need to return that info to the client, then do the redirect there.

franklt69
30 Jul 2010, 5:28 AM
thanks Evan but i have this doubt, in this case:

[DirectMethod]
public OperationSerializer Load(){


}

the ajax request from extjs (is a grid, store.load()) is waiting a json of OperationSerializer how i can response something in this method to dectect that the session expired and redirect from the client?


regards
Frank

geewhizbang
3 Aug 2010, 11:37 AM
I have had a hard time finding out how to connect up a WCF service to ExtJS.

Do I need to make an ASHX wrapper for the service? Or can it be accessed directly.

I have been following some code samples that are from the Version 2.x period that connect WCF directly to ExtJS

file: Ajax.svc.cs



using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace LMServices
{
public class LMService : iLMService
{
public int Add(int a, int b)
{
return a + b;
}
}
}


file: iLMServices.cs



using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace LMServices
{
[ServiceContract]
public interface iLMService
{

[OperationContract]
[
WebInvoke
(
BodyStyle = WebMessageBodyStyle.Wrapped,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/Add"
)
]
int Add(int a, int b);
}
}



file: web.config (for service)


<?xml version="1.0"?>
<configuration>

<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior >
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
<behavior name="LMServices.LMServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="jsonBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="LMServices.LMServiceBehavior" name="LMServices.Ajax">
<endpoint behaviorConfiguration="jsonBehavior" address="" binding="webHttpBinding" contract="LMServices.iLMService"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>

Javascript on page


//extjs button with script handler:
xtype: 'button',
text: "Add Values",
handler: function ()
{
Util.Ajax
(
{
params:
{
a: Ext.get('tfA').dom.value,
b: Ext.get('tfB').dom.value
},
fn: "Add",
callBack: function (r, o)
{
Ext.get('txtResult').dom.value = r.responseText;
}
}
);
}
//end button
var Util = {};
Util.Ajax = function (cfg)
{
if (!Ext.isDefined(Util.mask))
{
Util.mask = new Ext.LoadMask(Ext.getBody(), { msg: "Requesting..." });
Ext.lib.Ajax.defaultPostHeader = 'application/json';
}
Util.mask.show();
Ext.Ajax.request
(
{
url: '/LMServices/Ajax.svc/' + cfg.fn,
method: 'POST',
params: Ext.encode(cfg.params),
success: function (response, options)
{
Util.mask.hide();
cfg.callBack(response.options);
},
failure: function (response, options)
{
Util.mask.hide();
if (!Ext.isDefined(cfg.failure))
{
Util.alert("Ajax Error [" + cfg.fn + "]")
}
else
{
cfg.fail(response, options);
}
}
}
);
}


The header for ajax.svc. Without the factory, i get really mostly nothing, but at least with this i am getting errors:


The server encountered an error processing the request. The exception message is 'The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml', 'Json'. This can be because a WebContentTypeMapper has not been configured on the binding. See the documentation of WebContentTypeMapper for more details.'
There are examples on Microsoft's site about WebContentTypeMapper to force the use of Json in the request handler but they don't make much sense so far to me. One way is to force the ExtJS request into using JSON, but the code sample doesn't even compile. It may make more sense to find out why ExtJS is making a raw request (if that is really what is happening) and fix it.


<%@ ServiceHost Language="C#" Debug="true" Service="LMServices.LMService" CodeBehind="ajax.svc.cs" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>

ezBuilder
7 Aug 2010, 9:51 PM
ver. ExtJS 3.2.1+

[CRUD sample]

write error.

post back is [root is undefined]

help~

guodong828
15 Aug 2010, 9:13 PM
ver. ExtJS 3.2.1+
[CRUD sample]
write error.
post back is [root is undefined]
help~
Hello ezBuilder, using evant's Router-1.0, I encountered the same problem with you. My solution is this:
1.Modify the id attribute in company class:

[JsonProperty(PropertyName = "id")]
public int Id
{
get;
set;
}
2.Modify the Save method that returns the id attribute in company class

public int Save()
{
...
return this.Id;
}
3. Modify the Create method in GridHandler.ashx.cs


[DirectMethod]
[ParseAsJson]
public JObject Create(JObject o)
{
Company c = JsonConvert.DeserializeObject<Company>(o["data"].ToString());
c.Id = c.Save();
return new JObject(
new JProperty("data", new JArray(
new JObject(
new JProperty("id",c.Id),
new JProperty("name",c.Name),
new JProperty("employees",c.Employees),
new JProperty("turnover",c.TurnOver),
new JProperty("started",c.Started)
)
))
);
}

I tried writing on this, but unsuccessful:

return new JObject(
new JProperty("data", new JArray(c))
);
Do not know any other more convenient solution

ezBuilder
22 Aug 2010, 8:41 AM
thanks guodong828 (http://www.sencha.com/member.php?115589-guodong828)~

angelrand
23 Aug 2010, 9:14 PM
when use router i implement a class extend DirectHandler like

public class GridHandler : DirectHandler

and follow two method:

public override string ProviderName
{
get
{
return "MyApp.Remote.GridHandler";
}
}

public override string Namespace
{
get
{
return "MyApp";
}
}
in the myapp.js file i add code as follow in the head or within Ext.onready

Ext.Direct.addProvider(MyApp.Remote.GridHandler);
in aspx file code line as
<script src="../js/GridHandler.ashx" type="text/javascript"></script>
GridHandler.ashx and js are in the same folder(at the beginning not in the same folder)
but ERROR appear:
"MyApp is not defined"
what i do not do?

angelrand
24 Aug 2010, 3:43 AM
FT, problem resolved!
but i donnt know why
at the beginning i used ext-base-debug.js
now i change to ext-base.js
so it run well

evan
what difference with these two file?
i thought -debug can give me more information

Seana
1 Sep 2010, 6:01 AM
I maybe doing something wrong, or it maybe that IIS/Cassini are in cahoots to drive me a touch insane, but it seems that after a random allotment of time the DirectProviderCache is being blown away. It seems related to the same issue as...


Uploaded new version 0.6.1. The changes are:

* Fixed: If cached DirectProvider API is flushed by IIS it is regenerated and cached again
* Removed: DirectProviderCache class

Please download it from the first post.
Found here (http://www.sencha.com/forum/showthread.php?72245-Ext.Direct-for-ASP.NET-MVC&p=363655#post363655)

The actual issue is that some indeterminate time interval after starting the application when making an Ext.Direct call (via a store or manual invocation) I would be presented with a KeyNotFoundException on Line 50 of DirectHandler.CS more specifically


provider = cache[this.ProviderName];

When seeing this while the debugger was attached (either to Cassini or IIS7) the cache always has a size of 0. The final technical piece that made me think this may in fact be related to the above quoted forum post was while actively attempting to catch the exception "in the wild" I noticed that Cassini reloaded all the assemblies upon the calling of the direct method that caused the exception, looking very much like it was effectively purging the memory used by the web server handling the app.


So I suppose my first question is, is this automatic cache/memory pruning a possibility or am I possibly doing something wrong and it's just manifesting itself in a similar manner?

I'm thinking should it be that this is in fact an IIS/Cassini behavior, handling it should be as simple as adding an "else if(cache.Count() == 0)" control structure at line 48 of DirectHandler.cs...


~o) Sean

evant
1 Sep 2010, 7:52 PM
I'm not really sure what the post about the MVC router has to do with it, they are separate projects.

But yeah, it could probably do with a refactor. Try this (untested):



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Ext.Direct
{
/// <summary>
/// Class for handling Ext Direct requests
/// </summary>
public abstract class DirectHandler : IHttpHandler
{

public void ProcessRequest(HttpContext context)
{
DirectProvider provider = this.GetProvider(context, this.ProviderName);
string data = string.Empty;
string type = "text/javascript";

if (context.Request.TotalBytes == 0 && string.IsNullOrEmpty(context.Request["extAction"]))
{
data = provider.ToString();
}
else
{
DirectExecutionResponse resp = DirectProcessor.Execute(provider, context.Request, this.GetConverters());
data = resp.Data;
if (resp.IsUpload)
{
type = "text/html";
}
}
context.Response.ContentType = type;
context.Response.Write(data);
}

private DirectProvider GetProvider(HttpContext context, string name)
{
DirectProviderCache cache = DirectProviderCache.GetInstance();
if (!cache.ContainsKey(name))
{
DirectProvider provider = new DirectProvider()
{
Name = name,
Url = context.Request.Path,
Namespace = this.Namespace,
Timeout = this.Timeout,
MaxRetries = this.MaxRetries,
AutoNamespace = this.AutoNamespace,
Id = this.Id
};
this.ConfigureProvider(provider);
cache.Add(name, provider);
}
return cache[name];
}

public bool IsReusable
{
get
{
return false;
}

}

/// <summary>
/// True to ensure that Ext.namespace is called on the provider name to ensure it exists. Defaults to True.
/// </summary>
public virtual bool AutoNamespace
{
get
{
return true;
}
}

/// <summary>
/// Returns the id of the provider.
/// </summary>
public virtual string Id
{
get
{
return string.Empty;
}
}

/// <summary>
/// Retrieves the name of the provider
/// </summary>
public abstract string ProviderName
{
get;
}

/// <summary>
/// Retrieves the namespace for the provider
/// </summary>
public virtual string Namespace
{
get
{
return string.Empty;
}
}

/// <summary>
/// Gets the timeout to use for this provider (in milliseconds).
/// </summary>
public virtual int? Timeout
{
get
{
return null;
}
}

/// <summary>
/// Gets the maximum number of retries for failed requests.
/// </summary>
public virtual int? MaxRetries
{
get
{
return null;
}
}

protected IEnumerable<JsonConverter> GetConverters()
{
List<JsonConverter> list = new List<JsonConverter> { new ResultConverter(), new JavaScriptDateTimeConverter()};
this.ConfigureConverters(list);
return list;
}

/// <summary>
/// Allows the handler to modify the converters used for the Direct operation.
/// </summary>
/// <param name="converters">The list of converters to modify</param>
/// <remarks>Defaults to using the ResultConverter and JavaScriptDateTimeConverter classes.</remarks>
protected virtual void ConfigureConverters(List<JsonConverter> converters)
{
//do nothing
}

/// <summary>
/// Configure the provider with the appropriate set of methods.
/// </summary>
/// <param name="provider">The provider to be configured.</param>
/// <remarks>This method is virtual to allow for custom configurations.</remarks>
protected virtual void ConfigureProvider(DirectProvider provider)
{
provider.Configure(new object[] { this });
}

/// <summary>
/// Configure the provider using reflection by getting the appropriate methods from an assembly.
/// </summary>
/// <param name="provider">The provider to configure.</param>
/// <param name="assembly">The assembly to interrogate.</param>
protected void Configure(DirectProvider provider, Assembly assembly)
{
this.Configure(provider, assembly, null);
}

/// <summary>
/// Configure the provider using reflection by getting the appropriate methods from an assembly.
/// </summary>
/// <param name="provider">The provider to configure.</param>
/// <param name="assembly">The assembly to interrogate.</param>
/// <param name="exclusions">A list of classes to exclude.</param>
protected void Configure(DirectProvider provider, Assembly assembly, IEnumerable<object> exclusions)
{
provider.Configure(assembly, exclusions);
}

/// <summary>
/// Configure the provider given a list of action classes.
/// </summary>
/// <param name="provider">The provider to configure.</param>
/// <param name="items">The list of classes to interrogate.</param>
protected void Configure(DirectProvider provider, IEnumerable<object> items)
{
provider.Configure(items);
}

/// <summary>
/// Method to reset the state of this provider, so that the API will be re-generated
/// </summary>
protected void Reset()
{
DirectProviderCache cache = DirectProviderCache.GetInstance();
if (cache.ContainsKey(this.ProviderName))
{
cache[this.ProviderName].Clear();
}
}

}
}

Seana
2 Sep 2010, 9:19 AM
I'm not really sure what the post about the MVC router has to do with it, they are separate projects.

But yeah, it could probably do with a refactor. Try this (untested):



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Ext.Direct
{
/// <summary>
/// Class for handling Ext Direct requests
/// </summary>
public abstract class DirectHandler : IHttpHandler
{

public void ProcessRequest(HttpContext context)
{
DirectProvider provider = this.GetProvider(context, this.ProviderName);
string data = string.Empty;
string type = "text/javascript";

if (context.Request.TotalBytes == 0 && string.IsNullOrEmpty(context.Request["extAction"]))
{
data = provider.ToString();
}
else
{
DirectExecutionResponse resp = DirectProcessor.Execute(provider, context.Request, this.GetConverters());
data = resp.Data;
if (resp.IsUpload)
{
type = "text/html";
}
}
context.Response.ContentType = type;
context.Response.Write(data);
}

private DirectProvider GetProvider(HttpContext context, string name)
{
DirectProviderCache cache = DirectProviderCache.GetInstance();
if (!cache.ContainsKey(name))
{
DirectProvider provider = new DirectProvider()
{
Name = name,
Url = context.Request.Path,
Namespace = this.Namespace,
Timeout = this.Timeout,
MaxRetries = this.MaxRetries,
AutoNamespace = this.AutoNamespace,
Id = this.Id
};
this.ConfigureProvider(provider);
cache.Add(name, provider);
}
return cache[name];
}

public bool IsReusable
{
get
{
return false;
}

}

/// <summary>
/// True to ensure that Ext.namespace is called on the provider name to ensure it exists. Defaults to True.
/// </summary>
public virtual bool AutoNamespace
{
get
{
return true;
}
}

/// <summary>
/// Returns the id of the provider.
/// </summary>
public virtual string Id
{
get
{
return string.Empty;
}
}

/// <summary>
/// Retrieves the name of the provider
/// </summary>
public abstract string ProviderName
{
get;
}

/// <summary>
/// Retrieves the namespace for the provider
/// </summary>
public virtual string Namespace
{
get
{
return string.Empty;
}
}

/// <summary>
/// Gets the timeout to use for this provider (in milliseconds).
/// </summary>
public virtual int? Timeout
{
get
{
return null;
}
}

/// <summary>
/// Gets the maximum number of retries for failed requests.
/// </summary>
public virtual int? MaxRetries
{
get
{
return null;
}
}

protected IEnumerable<JsonConverter> GetConverters()
{
List<JsonConverter> list = new List<JsonConverter> { new ResultConverter(), new JavaScriptDateTimeConverter()};
this.ConfigureConverters(list);
return list;
}

/// <summary>
/// Allows the handler to modify the converters used for the Direct operation.
/// </summary>
/// <param name="converters">The list of converters to modify</param>
/// <remarks>Defaults to using the ResultConverter and JavaScriptDateTimeConverter classes.</remarks>
protected virtual void ConfigureConverters(List<JsonConverter> converters)
{
//do nothing
}

/// <summary>
/// Configure the provider with the appropriate set of methods.
/// </summary>
/// <param name="provider">The provider to be configured.</param>
/// <remarks>This method is virtual to allow for custom configurations.</remarks>
protected virtual void ConfigureProvider(DirectProvider provider)
{
provider.Configure(new object[] { this });
}

/// <summary>
/// Configure the provider using reflection by getting the appropriate methods from an assembly.
/// </summary>
/// <param name="provider">The provider to configure.</param>
/// <param name="assembly">The assembly to interrogate.</param>
protected void Configure(DirectProvider provider, Assembly assembly)
{
this.Configure(provider, assembly, null);
}

/// <summary>
/// Configure the provider using reflection by getting the appropriate methods from an assembly.
/// </summary>
/// <param name="provider">The provider to configure.</param>
/// <param name="assembly">The assembly to interrogate.</param>
/// <param name="exclusions">A list of classes to exclude.</param>
protected void Configure(DirectProvider provider, Assembly assembly, IEnumerable<object> exclusions)
{
provider.Configure(assembly, exclusions);
}

/// <summary>
/// Configure the provider given a list of action classes.
/// </summary>
/// <param name="provider">The provider to configure.</param>
/// <param name="items">The list of classes to interrogate.</param>
protected void Configure(DirectProvider provider, IEnumerable<object> items)
{
provider.Configure(items);
}

/// <summary>
/// Method to reset the state of this provider, so that the API will be re-generated
/// </summary>
protected void Reset()
{
DirectProviderCache cache = DirectProviderCache.GetInstance();
if (cache.ContainsKey(this.ProviderName))
{
cache[this.ProviderName].Clear();
}
}

}
}


The reference to the MVC Project was there because it was the only thing I had found in searching the forums that came close to the issue I was running into. Thus was there to show what I was basing my theory about the cache being purged on. Thanks for the quick reply Evan, I'll give it a shot!

Thanks!

evant
2 Sep 2010, 6:53 PM
Let me know how you go, if it's fine I'll add it into the build.

Seana
3 Sep 2010, 5:45 AM
Let me know how you go, if it's fine I'll add it into the build.
I've been putting the patch through it's paces in Cassini for the last day with no ill effects to report. I'm deploying it to my IIS test system today and going to hammer on it for a few days, I'll keep you posted on what's happening with it.

codeable
4 Sep 2010, 7:48 AM
I too ran into that caching problem, but instead of "fixing" it, I changed it to raise a more meaningful exception. In my environment, if the application is restarted then all consumers of that particular provider must fetch a new one from the server. (by responding to the exception).

It seems to me that your environment allows for recompile without app restart. (you are using debug builds and/or debug='true'?) So it recompiles part of the website (but leaves Ext.Direct [and others] alone) and continues on. So you get some old and some new assemblies running together. And anything that uses static caching (like the ProviderCache) will have data, while the session/application based caches may not.

Just my two pennies.

Whatty
7 Sep 2010, 6:53 AM
We also ran into this problem and it was related to the IIS application domain being recycled.


There are some known events which cause the application domain to be recycled (search google for complete results) but some of them are

memory usage
web.config being changed
structural changes to the site (i.e. adding or removing a directory)
Due to the nature in which the API handler creates the cache entrys if the application domain is recycled between the call the API handler and the next call the DirectHandler you will get a cache of zero. In our case were we dynamically creating an optimized page JS file which added a directory to the site and caused the APP_DOMAIN to be recycled.

You can verify this by outputting the APP_DOMAIN ID in the APIHandler request



log.Debug("ApiHandler processing request for session (" + context.Session.SessionID + ") in application domain: " + Thread.GetDomain().FriendlyName);


and the DirectHandler Request



log.Debug("DirectHandler processing " + context.Request.RequestType + " request for session (" + context.Session.SessionID + "): prinicpal \"" + Thread.CurrentPrincipal.Identity.Name + "\" in application domain: " + Thread.GetDomain().FriendlyName);


If the APP_DOMAIN ID has changed between calls then your APP DOMAIN got recycled.

Other post have mentioned to catch this situation and recreate the cache which is a viable solution.

Whatty





I maybe doing something wrong, or it maybe that IIS/Cassini are in cahoots to drive me a touch insane, but it seems that after a random allotment of time the DirectProviderCache is being blown away. It seems related to the same issue as...


Found here (http://www.sencha.com/forum/showthread.php?72245-Ext.Direct-for-ASP.NET-MVC&p=363655#post363655)

The actual issue is that some indeterminate time interval after starting the application when making an Ext.Direct call (via a store or manual invocation) I would be presented with a KeyNotFoundException on Line 50 of DirectHandler.CS more specifically


provider = cache[this.ProviderName];

When seeing this while the debugger was attached (either to Cassini or IIS7) the cache always has a size of 0. The final technical piece that made me think this may in fact be related to the above quoted forum post was while actively attempting to catch the exception "in the wild" I noticed that Cassini reloaded all the assemblies upon the calling of the direct method that caused the exception, looking very much like it was effectively purging the memory used by the web server handling the app.


So I suppose my first question is, is this automatic cache/memory pruning a possibility or am I possibly doing something wrong and it's just manifesting itself in a similar manner?

I'm thinking should it be that this is in fact an IIS/Cassini behavior, handling it should be as simple as adding an "else if(cache.Count() == 0)" control structure at line 48 of DirectHandler.cs...


~o) Sean

Seana
14 Sep 2010, 8:42 AM
We also ran into this problem and it was related to the IIS application domain being recycled.


There are some known events which cause the application domain to be recycled (search google for complete results) but some of them are

memory usage
web.config being changed
structural changes to the site (i.e. adding or removing a directory)
Due to the nature in which the API handler creates the cache entrys if the application domain is recycled between the call the API handler and the next call the DirectHandler you will get a cache of zero. In our case were we dynamically creating an optimized page JS file which added a directory to the site and caused the APP_DOMAIN to be recycled.

You can verify this by outputting the APP_DOMAIN ID in the APIHandler request



log.Debug("ApiHandler processing request for session (" + context.Session.SessionID + ") in application domain: " + Thread.GetDomain().FriendlyName);


and the DirectHandler Request



log.Debug("DirectHandler processing " + context.Request.RequestType + " request for session (" + context.Session.SessionID + "): prinicpal \"" + Thread.CurrentPrincipal.Identity.Name + "\" in application domain: " + Thread.GetDomain().FriendlyName);


If the APP_DOMAIN ID has changed between calls then your APP DOMAIN got recycled.

Other post have mentioned to catch this situation and recreate the cache which is a viable solution.

Whatty

This was indeed the case, it had nothing to do with code changes or in place recompiling. While I agree with Codeable about "asking" for a re-cache on the client side being a viable solution, the Library out of the box does not afford this option thus I thought it worth mentioning in the official thread. The change as Evan had posted has worked quite well over the past 2 weeks.

I meant to say so sooner, however have had very little "computer time" since the birth of my son... speaking of which it is time for more coffee ;)

angel.ignacio.colmenares
27 Oct 2010, 8:44 AM
Ext.Direct.Net + ext 3.3.0

Running CRUD Samples - Create produces

"Uncaught TypeError: Cannot read property 'length' of undefined"

after saving new record, new record is marked in grid as 'dirty' but it really saved in db

westy
9 Dec 2010, 6:35 AM
Looking to use Ext.Direct now we're upgrading from Ext 2.2 so thanks for this stack Evan :D
Looks like it'll meet my needs nicely.

Used to use Jayrock with 2.2 and this Ext.Direct stuff looks pretty similar.

Hope it supports nullable types, if not I'll make it do so :)

Cheers,
Westy


Edit:
Hehe, like the 'deliberate' bug in Echo Time sample... thought I was going mad for a second:

var s = String.format('The current date/time is: <b>{0}</b>', data.format('Y-m-d H:s'));

westy
3 Feb 2011, 7:23 AM
Hi,

Started putting together some standards for our web services, and started playing around with this stack again whilst testing dual interfaces to some of our services (i.e. SOAP and Ext.Direct).

A few suggestions, if I may?


Add the constructor to DirectException that takes an innerException.
In the DirectProvider.Execute, change the exception handler to use that constructor.
i.e. throw new DirectException("Error occurred while calling Direct method.", ex);
In DirectProcessor.ProcessRequest, change:
r.ExceptionMessage = (ex.InnerException != null) ? ex.ToString() : ex.Message;


That's it for now.

I have changed my local copy, but am hoping that this stack is still maintained.
If not then I'll may take it on I suppose, and provide a slightly more defensive implementation (i.e. that expects the worst).

Cheers,
Westy

westy
3 Feb 2011, 7:41 AM
Can't seem to edit my post, guess still some kinks in the shiny new forum (which looks great btw).

Wsa just gonna add that this improves the exceptions you get back to the client from the web service, rather than always being "An error was thrown by the target of the invocation" type things.

Cheers,
Westy

westy
9 Mar 2011, 8:00 AM
Another update...

Was shocked to find that if a direct method takes an object as a parameter no conversion is attempted from the JSON passed up. It just tries to make the call passing the dictionary.

I've tried to solve this, by putting the following before the method invocation in DirectProvider.Execute:


// If any of the parameters of the method we are calling are classes
// we need to try and create our instances from our Dictionary.
var methodParams = method.Method.GetParameters();

int i = 0;
foreach (ParameterInfo methodParam in methodParams)
{
// FIXME: If want to support interfaces as parameters will need an attribute
// .....: to give us the class instance to create or something...
if (!methodParam.IsOut && methodParam.ParameterType.IsClass)
{
// Check that our corresponding parameter is a dictionary
if (!typeof(System.Collections.IDictionary).IsAssignableFrom(param[i].GetType()))
{
throw new DirectException(String.Format("Parameter '{0}' should be a JSON object, since needs to be converted to a class instance.", i));
}

// We need an instance of our class
var ctr = methodParam.ParameterType.GetConstructor(System.Type.EmptyTypes);
if (ctr != null)
{
var instance = ctr.Invoke(null);

// Now attempt to copy our values in
System.Collections.IDictionary dic = (System.Collections.IDictionary)param[i];
foreach (System.Collections.DictionaryEntry entry in dic)
{
// Try and find property our class

// Need to make first letter upper-case to match C# conventions
string propName = entry.Key.ToString();
propName = propName.Substring(0, 1).ToUpperInvariant() + propName.Substring(1);

var prop = methodParam.ParameterType.GetProperty(propName);
if (prop == null)
{
throw new DirectException(String.Format("Parameter '{0}' contains a value '{1}' that is not in the class that is on the methods interface.", i, entry.Key));
}

var propSet = prop.GetSetMethod();
if (propSet == null)
{
throw new DirectException(String.Format("Parameter '{0}' contains a value '{1}' that has no public property setter in the class that is on the methods interface.", i, entry.Key));
}

// Set property value
propSet.Invoke(instance, new object[] { entry.Value });
}

// Now overwrite our parameter with our class instance
param[i] = instance;
}
}

++i;
}



Another update expected in a bit, since am getting no result success element when returning null or an empty collection...

Evan, how would you feel about me firming up some of this router and posting it on a public repo somewhere?

Cheers,
Westy


Edit:
I can't help but think I'm missing something here.

If my object is marked with the JsonObject attribute, and the properties suitably marked with JsonProperty attributes, shouldn't JSON.Net just sort this?

Why isn't it?
Are you meant to hand write a JsonConverter instance for each type? Find that hard to believe given those attributes...

westy
10 Mar 2011, 4:20 AM
Hi again.

Ok, I was missing something with my above solution, but so crucially was this router.

I believe I have sorted it, by doing the following:

Removing ComplexObjectConverter attribute from DirectRequest.Data so that all passed objects appear as JsonObject's (can't see what the dictionary was for to be honest).
Removing ParseAsJson attribute since not needed.
Updating DirectProvider to inspect the parameters of the function it has to call; if any have the JsonObject attribute on them then that parameter is deserialized using JSON.Net.


I've also used to opportunity to update the router to use JSON.Net 3.5 r8, which required a tweak to the deserialization of DirectRequest's.

I intend to clean up what I've done when I get the chance and post it somewhere if ok with Evan; have more pressing matters at present though, so may have to wait a while...

Cheers,
Westy

evant
11 Mar 2011, 4:22 AM
I'll setup a github repo. PM your nick!

westy
11 Mar 2011, 6:15 AM
Sent, cheers Evan!

evant
11 Mar 2011, 4:41 PM
Actually:
https://github.com/evantrimboli/Ext.Direct
https://github.com/evantrimboli/ExtDirectSample3

Have at it!

westy
12 Mar 2011, 1:27 AM
Excellent, I hope to get a chance to fork it and push my changes at some point over the weekend.

As I say, I'd like to polish it more but it works :)

krwo
15 Mar 2011, 1:32 AM
I think i found a bug in ComplexObjectConverter.ReadArray. It appears when there is array in array. For instance: [[1]] is deserialized as new object[]{ new object[]{ 1, null } }.
Here's fix:


private object[] ReadArray(JsonReader reader)
{
ArrayList output = new ArrayList();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
return output.ToArray();
}
if (reader.TokenType == JsonToken.StartArray)
{
output.Add(ReadArray(reader));
}
else if (reader.TokenType == JsonToken.StartObject)
{
output.Add(ReadObject(reader));
}
else
{
output.Add(reader.Value);
}
}

throw new JsonReaderException();
}

westy
15 Mar 2011, 2:46 AM
I've got rid of the ComplexObjectConverter completely my updated version.

It's not that polished yet, or properly tested (really need the official Ext 4 DirectProxy for that), but you can find it here (https://github.com/westy/Ext.data.DirectProxy) if you want to try it.

Working fine for my grids at present.

There's also an updated version of Mitchell's DirectProxy there that does what I need wrt overriding the sorter, filter and group encoding.

longsheng
27 Mar 2011, 7:00 PM
hi, evan
In my project there is a version of Newtonsoft.Json.dll 4 , When I use the Ext.Direct.dll, An error occurred.

I think the Ext.Direct should be more general, rather than relying on specific components.

ps:English translation from Google,两句话用了我10分钟~呃

BharatRathi
5 May 2011, 5:02 AM
Hi Evan,

Are there any plans to update .Net Router to support Ext Direct 4.X ?

westy
1 Jul 2011, 1:10 AM
It's working fine for me with Ext 4.
AFAIK the Ext.Direct spec has not changed.


Thought would drop by here and highlight an issue I've just (hopefully) resolved though.
We've been seeing occasional hanging of requests, and it's been proving very hard to reproduce. It seems to happen most often when demoing though, so it's not good.

Believe I've just sorted it though (*fingers crossed*).
The DirectProviderCache as was is not thread-safe. I've corrected in my version on github, slightly tweaking the interface to make it less dictionary like, and more provider cache like.



using System;
using System.Collections.Generic;

namespace Ext.Direct
{

/// <summary>
/// Simple cache for Ext.Direct providers
/// </summary>
public class DirectProviderCache
{
#region Static members
/// <summary>
/// Our singleton instance.
/// </summary>
private static readonly DirectProviderCache _Instance = new DirectProviderCache();

/// <summary>
/// Our cache of providers.
/// </summary>
private static Dictionary<string, DirectProvider> _Providers = new Dictionary<string, DirectProvider>();

/// <summary>
/// Our locking object.
/// </summary>
private static object _SyncLock = new object();
#endregion

#region Static constructor
/// <summary>
/// Static constructor prevent class being marked with BeforeFieldInit.
/// </summary>
static DirectProviderCache()
{
}
#endregion

#region Static properties
/// <summary>
/// Gets the singleton instance of this object.
/// </summary>
/// <returns>The DirectProviderCache instance.</returns>
public static DirectProviderCache GetInstance()
{
return _Instance;
}
#endregion

#region Indexers
/// <summary>
/// Allows providers to be retrieved from the cache.
/// </summary>
/// <param name="providerName">The name of the provider to retrieve.</param>
/// <returns>The DirectProvider, or null if not found.</returns>
public DirectProvider this[string providerName]
{
get
{
if (this.ContainsProvider(providerName))
{
return _Providers[providerName];
}

return null;
}
}
#endregion

#region Public methods
/// <summary>
/// Determines whether this cache contains the specified provider.
/// </summary>
/// <param name="providerName">The name of the provider to look for.</param>
/// <returns><b>true</b> if our cache contains the provider, <b>false</b> otherwise.</returns>
public bool ContainsProvider(string providerName)
{
return _Providers.ContainsKey(providerName);
}

/// <summary>
/// Adds a provider to the cache.
/// </summary>
/// <param name="provider"></param>
public void Add(DirectProvider provider)
{
if (provider == null)
{
throw new ArgumentNullException("provider");
}

// Double lock checking...
if (!this.ContainsProvider(provider.Name))
{
lock (_SyncLock)
{
if (!this.ContainsProvider(provider.Name))
{
_Providers.Add(provider.Name, provider);
}
}
}
}
#endregion
}
}


Cheers,
Westy

gianmarco
1 Jul 2011, 1:29 AM
Hi Evan,

Are there any plans to update .Net Router to support Ext Direct 4.X ?

ExtDirectHAndler (http://gimmi.github.com/extdirecthandler/) has been developed against ExtJS 4 and has support for named parameter that AFAIK is the only new feature introduced with Ext.Direct 4

westy
1 Jul 2011, 2:02 AM
ExtDirectHAndler (http://gimmi.github.com/extdirecthandler/) has been developed against ExtJS 4 and has support for named parameter that AFAIK is the only new feature introduced with Ext.Direct 4

Oh, not seen this before... I'll take a look :)

Whatty
5 Jul 2011, 10:47 AM
Good afternoon,

I am using the above version of the Ext.Direct Router with ExtJS 4.0 and am having problems with the following



"{\"action\":\"AppPreferencesActionImpl\",\"method\":\"updateAppPreferences\",\"data\":[{\"reviewDateRangeID\":2,\"requireSufficientFunds\":0}],\"type\":\"rpc\",\"tid\":2}"

This is initiated from a model.save on the client side

The problem is that the data



\"data\": [{\"reviewDateRangeID\":2,\"requireSufficientFunds\":0}]

is being converted to a dynamic object when the request is being deserialized in the



DirectProcessorImpl

however the direct action is expecting the RAW JSON string



{\"reviewDateRangeID\":2,\"requireSufficientFunds\":0}

Now we have verified that the existing code works if we wrap the data element with quotes, such as



\"data\": ["{\"reviewDateRangeID\":2,\"requireSufficientFunds\":0}"]

Is this a known issue with ExtJS 4.0 and the older versions of the library - if so I am assuming that the newer versions have addressed this issue.

Now we are supporting both ExtJS 3.x and ExtJS 4.0 in the same application - until such a time as when the entire application gets converted - are the new Ext.Direct 4.0 libraries backwards compatible with ExtJS 3.x clients.

Please advise!!!

Whatty

romarybachok
11 Aug 2011, 2:11 PM
Hi everybody, I am newcomer in Ext Direct. How can I receive data like in chat - after clicking on button the typed text will be sent for all pages of my asp net application in every opened tab.
Here is my JS file:


Ext.onReady(function () {
Ext.ns('Ext.ss');
Ext.ss.APIDesc=
{
"url": "Router.cs",
"namespace": "MyApp",
"type": "remoting",
"actions": {
"MyClass": [
{
"name": "GetString",
"len": 1
}]
}
};

Ext.QuickTips.init();

var form = new Ext.form.FormPanel({
renderTo: 'container',
hideLabels: true,
bodyStyle: 'padding: 20px;',
width: 400,
height: 150,
items: {
itemId: 'input',
xtype: 'textfield',
value: '123'
},
buttons: [{
text: 'send',
handler: function () {
var text = form.getComponent('input').getValue();
MyApp.MyClass.GetString(text, function (data) {
form.getComponent('input').setValue(data);
});
}
}]
});
});

and here is my c# class:


using System;

namespace MyApp
{
public class MyClass
{
public String GetString(String s)
{
return s+" after server-side";
}
}
}
And I also need help with configuration of my router... It should be realized as http handler? I don't need a stack, I need simple example using 1 server method. THX)

Dengqiao Huang
30 Aug 2011, 8:08 AM
I use store in ExtJS 4.
I added two records, and then synchronize.
The post data:


{
"action":"Product",
"method":"Add",
"data":[
[
{
"ProductID":0,
"ProductName":"Computer",
"SupplierID":1,
"CategoryID":1,
"QuantityPerUnit":"",
"UnitPrice":3000,
"UnitsInStock":10,
"UnitsOnOrder":10,
"ReorderLevel":0,
"Discontinued":false
},
{
"ProductID":0,
"ProductName":"Computer",
"SupplierID":1,
"CategoryID":1,
"QuantityPerUnit":"",
"UnitPrice":3000,
"UnitsInStock":10,
"UnitsOnOrder":10,
"ReorderLevel":0,
"Discontinued":false
}
]
],
"type":"rpc",
"tid":2
}


The result:


[
{
"type":"exception",
"tid":2,
"action":"Product",
"method":"Add",
"message":"Parameters length does not match"
}
]


The Store Config:


var store=Ext.create(Ext.data.Store,{
model:'Products',
autoLoad:true,
pageSize:20,
remoteFilter:true,
remoteSort:true,
storeId:"ProductStore",
proxy:{
type:"direct",
//directFn:Ext.app.Product.List1,
api:{
read:Ext.app.Product.List,
create:Ext.app.Product.Add,
update:Ext.app.Product.Edit,
destroy:Ext.app.Product.Delete
},
reader:{
type:"json",
root:"data"
},
writer:{
type:"json",
//allowSingle:false
}
}
})

Whatty
1 Feb 2012, 4:41 PM
Good evening,

Does the Ext.Direct stack provide support for polling?

Whatty

westy
2 Feb 2012, 1:24 AM
Does the Ext.Direct stack provide support for polling?


Hi,

Not really what Ext.Direct is all about, or indeed Ajax; the idea is that it makes server-side methods appear as they are on the client-side, handling all the comms and callbacks for you.

You could always write something that polled an Ext.Direct method on the client-side, kinda, but it's not very web2.0/ajaxy is it?

Cheers,
Westy

gianmarco
3 Feb 2012, 12:27 AM
Good evening,

Does the Ext.Direct stack provide support for polling?

Whatty

Yes, see http://docs.sencha.com/ext-js/4-0/#!/api/Ext.direct.PollingProvider

westy
3 Feb 2012, 7:33 AM
Oh yeah, forgot about that provider :)

azinyama
3 Feb 2012, 11:12 AM
Hi, all!!! I'm a newbie with ExtJS 4...
I have a tree store loading data from a database and would like to catch an exception if the there is an error loading the data, or if the asp.net session has expired. Below is my code.
Javascript:

var store = Ext.create('Ext.data.TreeStore',
{
autoLoad : true,
model : 'DisabledTreePanelNode',
root : {
expanded : true
},
proxy : {
type : 'direct',
directFn : _User.Load_TreeItem,
listeners : {
exception : function(proxy, response, operation)
{
Ext.MessageBox.show(
{
title : 'REMOTE EXCEPTION',
msg : operation.getError(),
icon : Ext.MessageBox.ERROR,
buttons : Ext.Msg.OK
});
}
}
},
listeners : {
beforeload : function(s, operation, eOpts)
{
desktop.setSystemStatus('Loading menu item options...', 'icon-loading');
},
load : function(store, node, records, success, eOpts)
{
if (success)
{
desktop.setSystemStatus('Ready...', 'icon-accept');
}
else
{
desktop.setSystemStatus('An error occured while trying to menu item options...', 'icon-error');

Ext.MessageBox.show(
{
title : 'REMOTE EXCEPTION',
msg : 'An error occured while trying to menu item options...',
icon : Ext.MessageBox.ERROR,
buttons : Ext.Msg.OK
});
}
}
}
});


<DirectMethod()> _
Public Function Load_TreeItem() As NodeCollection ' NodeSerializer 'ByVal order As String, ByVal direction As String) As UserSerializer
'Dim userList As JArray = New JArray()
Dim sm As SessionManager = SessionManager.getInstance()

Dim objS As ISession = CType(sm.getSession(ClaimCatch_Main.AppHandler.stridSession), ISession)
objS = Nothing 'set the session object to nothing
If objS Is Nothing Then
Throw New Exception("Session has expired. Login again") 'send this message as exception message. THIS ISN'T WORKING.
End If

Return New NodeCollection()
End Function

I'm getting stuck at trying to get the message "Session has expired. Login again" on the javascript side.

Any help would be appreciated.

azinyama
14 Feb 2012, 4:13 AM
Anyone have answer to my question.

westy
14 Feb 2012, 5:17 AM
Try overriding the direct proxy, and adding some logic to afterRequest.

e.g.


Ext.define('Foo.data.proxy.Direct', {
extend: 'Ext.data.proxy.Direct',


alias: 'proxy.foo-direct',


/**
* Displays an error if the request failed.
* @param {Object} request The Request object.
* @param {Boolean} success True if the request was successful.
*/
afterRequest: function(request, success) {
if (!success && request.operation.hasException()) {
Ext.Msg.show({
title: 'Error',
msg: request.operation.getError(),
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
}
}
});


That's what I do, since with event handlers, other handlers can stop them propagating, or if someone downstream adds them in the same way (i.e. in the config as you're doing) then they get overwritten...

I seem to remember a bug I raised to do with the events not getting properly raised.

Another thought, can you see your exception coming back in Firebug?

Cheers,
Westy

christianpapauschek
30 May 2012, 4:43 AM
Since JSON.NET supports deserialization of generic .NET types, I rewrote the DirectProvider.Execute method to allow deserialization in this generic sense. The trick is to check the expected types of the invoked method via reflection.

Note that I also removed the "SanitizeDates" method since it is no longer necessary. "ParseAsJson" is also not included anymore, but can be added again if needed.



internal object Execute(DirectRequest request)
{
DirectAction action = this.actions[request.Action];
if (action == null)
throw new DirectException("Unable to find action, " + request.Action);


DirectMethod method = action.GetMethod(request.Method);
if (method == null)
throw new DirectException("Unable to find action, " + request.Method);


JArray data = (JArray)request.RequestData["data"];
int parameterCount = request.Data != null ? request.Data.Length : data.Count;


if (parameterCount == 0)
{
if (method.Parameters > 0)
throw new DirectException("Parameters length does not match");
}
else
{
if (parameterCount > 1 && method.IsForm)
{
throw new DirectException("Form methods can only have a single parameter.");
}
else if (parameterCount != method.Parameters)
{
throw new DirectException("Parameters length does not match");
}
}


try
{

object[] param = new object[method.Parameters];
if (request.Data != null)
{
// lets use given request data
param = request.Data;
}
else
{
// Deserialize generic JSON to specific .NET method types
for (int t = 0; t < param.Length; t++)
{
Type paramType = method.ParameterInfos[t].ParameterType;
param[t] = JsonConvert.DeserializeObject(data[t].ToString(), paramType);
}
}


Type type = action.Type;
return method.Method.Invoke(type.Assembly.CreateInstance(type.FullName), param);
}
catch (TargetInvocationException ex)
{
// send application exception to browser
if (ex.InnerException != null)
throw new DirectException(ex.InnerException.ToString());


throw new DirectException(ex.Message);
}
catch (Exception ex)
{
throw new DirectException("Error occurred while calling Direct method: " + ex.Message);
}
}


Hope this helps someone

hieu79vn
26 Jun 2012, 3:49 AM
Hi

I have a data grid A and another grid B: When user click on a row of A, B will show a grid detail with the ID of the row clicked in A.
I have a function called GetBlogList(int rowIDofA) on server (written in C#) which will return a json string.
How can I call this function and pass the parameter when user click on a row of A by using Ext Direct Router?

Furthermore, do you have a completed guide to use this library?

Thank you

azinyama
28 Aug 2012, 4:54 AM
Hi All!!! Still on there exception problem. Had dropped the project for a while. Now I'm back still with the same problem. My code is below.




Ext.define('Test.store.Tariff',
{
extend : 'Ext.data.Store',
id : 'Tariffs',
autoLoad : false,
autoSave : false,
autoSync : true,
remoteSort : true,
pageSize : 100,
paramOrder : 'start|limit|page|search',
model : 'Test.model.Tariff',
proxy : {
type : 'direct',
reader : {
type : 'json',
successProperty : 'success',
totalProperty : 'total',
root : 'data',
idProperty : 'Tariff_RowID'
},
writer : {
type : 'json',
encode : false,
writeAllFields : true
},
api : {
create : _tariffHandler.Create_Tariff,
read : _tariffHandler.Load_Tariff,
update : _tariffHandler.Update_Tariff,
destroy : _tariffHandler.Delete_Tariff
},
listeners : {
exception : function(proxy, response, operation)
{
Ext.MessageBox.show(
{
title : 'Remote Exception',
msg : operation.getError(),
icon : Ext.MessageBox.ERROR,
buttons : Ext.Msg.OK
});
}
},
updateCallback: function (request, success)
{
if (!request.operation.success && request.operation.hasException())
{
console.log(request.operation);
Ext.Msg.show(
{
title : 'Tariff Update Error',
msg : request.operation.getError(),
buttons : Ext.MessageBox.OK,
icon : Ext.MessageBox.ERROR
});
}
}
}
});


ASP.Net


<DirectMethod()> _
<ParseAsJson()> _
Public Function Update_Tariff(ByVal o As JObject) As Tariff
Dim t As Tariff = Nothing

Try
'ASSUME NEXT TWO LINES FAIL AND MOVE TO CATCH AND THROUGH EXCEPTION
t = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Tariff)(o.ToString())
t.Tariff_Update()
Catch ex As DirectException
Throw New DirectException("CUSTOMR EXCEPTION MESSAGE BEING SEND HERE") '<--- CUSTOMR EXCEPTION MESSAGE BEING SENT HERE BUT
End Try

Return t
End Function


It is only error message I keep getting is:

"Error occurred while calling Direct method: Exception has been thrown by the target of an invocation."

stalek
7 Sep 2012, 6:21 AM
Hi,
does anybody has working copy of this Direct provider with grid/store and remote sorting and filtering?
I see sorters config value is converted to complex object on server side (after json conversion).
I see message about different number of params so my method can't be called...
Is it retired project?

Thanks,
Alex

westy
7 Sep 2012, 7:10 AM
It works with my version of Evan's router.

You have create classes on the server-side that represents the sorters and filters, since the router can't automagically sort it out for you... this parameters need to get passed through and interpreted by your data layer.

e.g. (sanitised and simplied versions for brevity - strings with fixed values should obviously be enums, but not posting the complete solution...):


public class SortParam
{
public string Property { get; set; }
public string Direction { get; set; } // One of "ASC"/"DESC"
}

public class FilterParam
{
public class FilterDetails
{
public string Comparison { get; set; } // One of "eq", "lt" or "gt" for date values.
public string Type { get; set; } // One of string, boolean, date, numeric, list
public string Value { get; set; }
public string[] ListFilter { get; set; }
}

public FilterDetails Data { get; set; }
public string Field { get; set; }
}

public class SearchCriteriaParam // This what Ext.Direct posts for paged grids, say.
{
public long? Page { get; set; }
public long? Start { get; set; }
public long? Limit { get; set; }

public SortParam[] Sort { get; set; }
public FilterParam[] Filter { get; set; }
}




Think that's enough posted for now...

Hope that helps.
Westy

stalek
7 Sep 2012, 7:53 AM
Super!
Thanks for so quick response. I use paged grid (so called infinite grid :) ) and I will try it later this evening.
Does it mean if I provide SearchCriteriaParam as the only param on method definition to load data (Read from CRUD) it's converted automatically from set of sent attributes and will be passed as this object?

Thanks,
Alex

westy
7 Sep 2012, 8:15 AM
You'll need it as a param on your method yeah.

Can't remember if anything needed on client... possibly paramOrder and/or paramsAsHash need setting as appropriate; I forget.

stalek
7 Sep 2012, 1:20 PM
westy,
life is not as simple as I thought after your suggestion :)
When I run such simple test case like below I see incorrect value on Data property of ResponseObject:


[TestMethod()]
public void DeserializeObjectTest()
{
string value =
// {"action":"IncidentListService","method":"Read","data":[1,0,100,[{"property":"TimeOfCall","direction":"DESC"}],null],"type":"rpc","tid":1}
"{\"action\":\"IncidentListService\",\"method\":\"Read\",\"data\":[1,0,100,[{\"property\":\"TimeOfCall\",\"direction\":\"DESC\"}],null],\"type\":\"rpc\",\"tid\":1}";
Type type = typeof(DirectRequest);
JsonSerializerSettings settings = null;
DirectRequest actual = JsonConvert.DeserializeObject(value, type, settings) as DirectRequest;
}




Number of parameters on Data property is higher then number of params passed in JSON.
This is because Data property is decorated with conversion attribute like below and has some code provided on Direct level that does wrong conversion:


[JsonConverter(typeof(ComplexObjectConverter))] public object[] Data
{
get;
set;
}

Because of this it causes the exception on Direct level when I try to call method with manually provided list of param...

stalek
10 Sep 2012, 1:09 AM
I found source of the problem of course. It's because incorrect conditions flow in code below:


private object[] ReadArray(JsonReader reader)
{
ArrayList output = new ArrayList();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
return output.ToArray();
}
if (reader.TokenType == JsonToken.StartArray)
{
output.Add(ReadArray(reader));
}
if (reader.TokenType == JsonToken.StartObject)
{
output.Add(ReadObject(reader));
}
else
{
output.Add(reader.Value);
}
}


throw new JsonReaderException();
}


I changed it to this version below with minimal changes (although it should be as effective as switch/case):


private object[] ReadArray(JsonReader reader)
{
ArrayList output = new ArrayList();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
return output.ToArray();
}
else
if (reader.TokenType == JsonToken.StartArray)
{
output.Add(ReadArray(reader));
}
else
if (reader.TokenType == JsonToken.StartObject)
{
output.Add(ReadObject(reader));
}
else
{
output.Add(reader.Value);
}
}


throw new JsonReaderException();
}

The issue was (is) after reading array object the TokeType is set to EndArray and then it executes last operation in message body that is Add(reader.Value)

Hope it helps anybody...