Problems deserializing Dictionarys in MVC 3 for AJAX request (an approach that works out of the box with classic ASP.NET Webforms) - ajax

I've been successfully WebForms for AJAX calls with relatively complex set of parameters (called using jQuery.ajax). We're attempting to try using the same approach in MVC 3 but seem to be falling at the first hurdle with MVC failing to deserialize Dictionary arrays successfully.
The approach that works without issue in ASP.NET WebForms "classic" is below:
[WebMethod]
public static JQGrid.JQGridData GetListForJQGrid(int? iPageSize, int? iPage, int? iMaxRecords, string sSortField, string sSortOrder,
Dictionary<string, string> dSearchOptions, Dictionary<string, object>[] aOriginalColumnDefinition, string[] aExtraDataColumns)
And below is the MVC 3 equivalent: (nb exactly the same name/parameters - different return type but I don't think that is relevant)
[HttpPost]
public JSONResult GetListForJQGrid(int? iPageSize, int? iPage, int? iMaxRecords, string sSortField, string sSortOrder,
Dictionary<string, string> dSearchOptions, Dictionary<string, object>[] aOriginalColumnDefinition, string[] aExtraDataColumns)
With the WebMethod all the data deserializes perfectly. However, when the MVC method is called all the simple parameters deserialize fine but for some unknown reason the array of Dictionary's arrives as an array of nulls.
So, off the back of that a number of questions:
Has anyone else experienced problems with MVC 3 deserialization of arrays of dictionaries?
Does MVC 3 by default not use System.Web.Script.Serialization.JavaScriptSerializer which is I think what ASP.NET WebMethods use under the bonnet?
Can I force MVC 3 to use System.Web.Script.Serialization.JavaScriptSerializer instead of what it is using?
Or am I missing something / should my approach be slightly different? Please note that at least for now we'll need to share the client side code between classic ASP.NET WebMethods and MVC 3 and so we want that to remain as is if possible.
Finally, I can see there is a possible workaround that could be used looking at this question: POST json dictionary . Is this workaround the only game in town or have things improved since this question was posed?
jQuery AJAX call:
$.ajax(_oJQGProperties.sURL, //URL of WebService/PageMethod used
{
data: JSON.stringify(oPostData),
type: "POST",
contentType: "application/json",
complete: DataCallback
});
Example JSON.stringify(oPostData):
{
"dSearchOptions":{},
"aOriginalColumnDefinition":
[
{"name":"ID","sortable":false,"hidedlg":true,"align":"right","title":false,"width":40},
{"name":"URL","sortable":false,"hidedlg":true,"align":"left","title":false,"width":250,"link":"javascript:DoSummat(this,'{0}');","textfield":"Name"},
{"name":"Description","sortable":false,"hidedlg":true,"align":"left","title":false,"width":620}
],
"aExtraDataColumns":["Name"],
"_search":false,
"iPageSize":-1,
"iPage":1,
"sSortField":"",
"sSortOrder":"",
"iMaxRecords":0
}

I don't have any experience with binding to a dictionary array, but one possible solution is to use a custom model binder. Scott Hanselman has a blog post on this subject that you might find useful: Splitting DateTime - Unit Testing ASP.NET MVC Custom Model.

Long time getting to update this but I thought I'd share where we got to. The problem turned out to be a bug - details of which can be found here:
Bug:
http://connect.microsoft.com/VisualStudio/feedback/details/636647/make-jsonvalueproviderfactory-work-with-dictionary-types-in-asp-net-mvc
Workaround:
POST json dictionary
We used the stated workaround which has been fine. I'm not too clear as to when the fix will be shipped and where exactly the bug lay. (Is it .NET dependant / MVC dependant etc) If anyone else knows I'd love to find out :-)
Update
I haven't heard still if this is shipped (I assume it goes out with MVC 4?) but in the interim this may be an alternative solution:
http://www.dalsoft.co.uk/blog/index.php/2012/01/10/asp-net-mvc-3-improved-jsonvalueproviderfactory-using-json-net/
Update 2
This has now been shipped as a fix with MVC 4. The issue remains unresolved in MVC 3 and so I've now written it up as a blog post here:
http://icanmakethiswork.blogspot.com/2012/10/mvc-3-meet-dictionary.html

I ran into this issue too. After finding this SO post, I thought about upgrading to MVC4, but it's too risky to do all at once in my environment so scratch that.
This link posted in Johnny Reilly's answer looked promising, but it required flattening my dictionary to a string. Because my MVC model is bidirectional (it's used for reads and writes), and I really wanted that dictionary structure I decided to pass on that too. It would have been a real pain to keep two properties for one value. I would have needed to add more tests, watch out for edge cases, etc.
Johnny's JsonValueProviderFactory link seemed promising too, but a bit arcane. I'm also not entirely comfortable monkeying around with a part of MVC like that. I had only a few hours to figure this problem out so I passed on this too.
Then I found this link somewhere, and thought "Yes! this is more like what I want!". In other words attack the model binding problem by using a custom binder. Replace the buggy one with something else, and use MVC's built-in capability to do so. Unfortunately, this did not work as my use case was List of T, and T was my model. This totally did not work with the sample. So I hacked away at it and ultimately failed.
Then, I got a lightbulb moment - JSON.NET does not have this problem. I use it all the time for doing all sorts of things, from cloning objects, to logging, to REST service endpoints. Why not model binding? So I ultimately ended up with this and my problem was solved. I think it should work with just about anything - I trust JSON.NET =)
/// <summary>
/// Custom binder that maps JSON data in the request body to a model class using JSON.NET.
/// </summary>
/// <typeparam name="T">Model type being bound</typeparam>
/// <remarks>
/// This binder is very useful when your MVC3 model contains dictionaries, something that it can't map (this is a known bug, fixed with MVC 4)
/// </remarks>
public class CustomJsonModelBinder<T> : DefaultModelBinder
where T : class
{
/// <summary>
/// Binds the model by using the specified controller context and binding context.
/// </summary>
/// <returns>
/// The bound object.
/// </returns>
/// <param name="controllerContext">The context within which the controller operates. The context information includes the controller, HTTP content, request context, and route data.</param><param name="bindingContext">The context within which the model is bound. The context includes information such as the model object, model name, model type, property filter, and value provider.</param><exception cref="T:System.ArgumentNullException">The <paramref name="bindingContext "/>parameter is null.</exception>
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
HttpRequestBase request = controllerContext.HttpContext.Request;
request.InputStream.Position = 0;
var input = new StreamReader(request.InputStream).ReadToEnd();
T modelObject = JsonConvert.DeserializeObject<T>(input);
return modelObject;
}
}
To apply the binder, I added an attribute to my model parameter. This causes MVC3 to use my binder instead of the default. Something like this:
public ActionResult SomeAction(
[ModelBinder(typeof(CustomJsonModelBinder<List<MyModel>>))] // This custom binder works around a known dictionary binding bug in MVC3
List<MyModel> myModelList, int someId)
{
One caveat - I was using POST with content type "application/json". If you're doing something like form or multipart data instead it will probably crash horribly.

Related

What is IViewLocationExpander.PopulateValues() for in Asp.Net Core MVC

I'm using ASP.NET MVC CORE. I have implemented my own ViewLocationExpander so that I can structure my project the way I want and place my views where I like.
This is accomplished by implementing a class that inherits from IViewLocationExpander and most of the work occurs in the following method:
ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
Everything is working pretty sweet but the interface defines a 2nd method that I don't know how to properly implement:
PopulateValues(ViewLocationExpanderContext context)
I've read articles all over the internet about this interface but no one has really provided much info on what exactly this method is for other than saying vague things about how it helps with caching.
I'd really appreciate it if someone could explain how this method is used by the framework and how I can use it appropriately to aid caching if that is indeed what it is for.
Maybe the following additional info taken directly from a GitHub MVC issue can answer your question:
Caching includes the Values dictionary in its lookup. Unless the PopulateValues() method adds distinct information to ViewLocationExpanderContext.Values, the ExpandViewLocations() method will be called just once per original file name i.e. the initial information is cached from then on.
On top of that, the particular example posed by OP can help understand even better, at least that's what happened to me:
His project has views with the same name under two different directory
trees (say Foo and Bar)
Depending on some data extracted by current action context, the view to locate should be under either one of those trees
Without any code in PopulateValues(), view engine will ask once to locate the view, then use view "standard" data (e.g. ControllerName, ActionName, Area, etc.) in order to cache the actual location where view is found.
So, in OP case, once a view location is cached (say e.g. from Foo directory tree) everytime a view with same name is needed it will always be from that tree, there'll be no way to detect if the one in the other Bar tree should have been actually picked up.
The only way for OP is to customize PopulateValues() by adding specific, distinctive view details to Values dictionary: in current scenario, those are the info extracted from current action context.
That additional info are used two-fold: ExpandViewLocations() might use them when invoked in order to determine proper location, while view engine will use them to cache view location once found.
Dec. 2021 update
Official doc page is more descriptive. From Remarks section:
Individual IViewLocationExpanders are invoked in two steps:
(1) PopulateValues(ViewLocationExpanderContext) is invoked and each expander adds values that it would later consume as part of ExpandViewLocations(ViewLocationExpanderContext, IEnumerable<String>). The populated values are used to determine a cache key - if all values are identical to the last time PopulateValues(ViewLocationExpanderContext) was invoked, the cached result is used as the view location.
(2) If no result was found in the cache or if a view was not found at the cached location, ExpandViewLocations(ViewLocationExpanderContext, IEnumerable<String>) is invoked to determine all potential paths for a view.
Haven't messed around with it enough to be able to give you a concrete answer, but have a look at IViewLocationExpander.PopulateValues(ViewLocationExpanderContext context) on the ASP.NET MVC GitHub repo:
public interface IViewLocationExpander
{
/// <summary>
/// Invoked by a <see cref="RazorViewEngine"/> to determine the values that would be consumed by this instance
/// of <see cref="IViewLocationExpander"/>. The calculated values are used to determine if the view location
/// has changed since the last time it was located.
/// </summary>
/// <param name="context">The <see cref="ViewLocationExpanderContext"/> for the current view location
/// expansion operation.</param>
void PopulateValues(ViewLocationExpanderContext context);
// ...other method declarations omitted for brevity
}
Readability format:
"Invoked by a RazorViewEngine to determine the values that would be consumed by this instance of IViewLocationExpander. The calculated values are used to determine if the view location has changed since the last time it was located.
Parameters:
context: The ViewLocationExpanderContext for the current view location expansion operation."
I've had a look at some classes which implement this interface - some declare the method but leave it empty, others implement it.
NonMainPageViewLocationExpander.cs:
public void PopulateValues(ViewLocationExpanderContext context)
{
}
LanguageViewLocationExpander.cs:
private const string ValueKey = "language";
public void PopulateValues(ViewLocationExpanderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// Using CurrentUICulture so it loads the locale specific resources for the views.
#if NET451
context.Values[ValueKey] = Thread.CurrentThread.CurrentUICulture.Name;
#else
context.Values[ValueKey] = CultureInfo.CurrentUICulture.Name;
#endif
}
The article "View Location Expander in ASP.NET Core and MVC 6" provides an example. Here's an excerpt of the explanation:
You can add as many view location expanders as you want. IViewLocationExpander interface has 2 methods, PopulateValues and ExpandViewLocations. PopulateValues method allows you to add values that can be later consumed by ExpandViewLocations method. The values you put in PopulateValues method will be used to find cache key. ExpandViewLocations method will be only invoked if there is no cache result for the cache key or when framework is unable to find the view at the cached result. In the ExpandViewLocations method, you can return your dynamic view locations. Now you can register this view location expander in Startup.cs file,
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new MyViewLocationExpander());
});
Basically the method can populate values into context.Values that will later be used to determine if a cached list should be used or if the ExpandViewLocations will be called....

Model Binder of Json.Net not being used when i post an object

To clarify...
I configure my WebApiConfig like so:
config.Formatters.JsonFormatter.SerializerSettings.Binder = new TypeNameSerializationBinder("namespace.{0}, assembly");
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
This should allow me to bind derived classes to base class.
And binder does work when WebApi serializes objects to JSON, and sends them to client, but when I post them back to server, the binder isn't used (BindToType method never gets called), and my objects get bound to base class.
When i serialize/deserialize objects manually with this settings it all works fine.
Any ideas?
I had the same problem when trying to deserialize complex objects with a custom JsonConverters. I needed this because I'm using DbGeometry for storing users locations.
I broke my head on this a couple of days, I really thought I was doing something wrong, because every time I posted an geometry to the Web API, the complex type parameter was set to null. This while JsonConverter was perfectly able to convert the json to an filled object.
My workaround for this is written below. I don't like that I can't just use the parameter as I'm supposed to do. But it works, at last.
[HttpPost]
public MyComplexType SaveMyComplexType()
{
var json = Request.Content.ReadAsStringAsync().Result;
var myComplexType = JsonConvert.DeserializeObject<MyComplexType>(json);
//todo: validation and save to db
return myComplexType;
}
After some research, I found that this is a bug in ASP.NET Web Api. When the url encoded parameters are parsed, it just creates a new JsonSerializer (without passing global settings).
I filed it here
http://aspnetwebstack.codeplex.com/workitem/609

Static methods, non static members - Data Access Layer/Business Layer

OK, i am creating a web application. I am using MVC3. I have ViewModel for every view and also I have Data Model that supports viewModel and does the actuall CRUD operation in my sql table, while my viewModel validates and alters data as necessary.
Here is a question. The way I have been coding is
public class MyClassViewModel
{
public string member1{get;set;}
public int member2{get;set;}
public static GetAllMembers(MyClassViewModel obj, out string strErrMsg)
{
// code goes here, read operation
}
public static UpdateMyClass(MyClassViewModel obj, out string strErrMsg)
{
// code goes here, write operation.
}
}
Both My ViewModel and DataModels have been coded this way, My Controller on HttpPost just do something like this..
MyClassViewModel.UpdateMember(obj,out strErrMsg)
Since its mvc on every post it creates on a strongly typed view, a new object of my ViewModel, hence non static members are different and are not shared between sessions.
I am using Linq and therefore on each static method of my DataModel i use
var db = new MyApplicationDataContext()
to get my linq classes and work on them. This solves my open/close reader problems.
My question is, are there any issues regarding concurrency with this design? I know I might have problems in changing my data layer if it is not designed via interface, but I highly doubt that is necessary based on my application.
You are essentially using the factory design pattern. That's OK pattern to use for this; each static method has its own context, and that's OK too. You always have to worry about concurrency; however, the LINQ to SQL architecture has concurrency detection built in and throws a ChangeConflictException when a concurrent change has been made in reaction to this.
I would also highly recommend that you are disposing your contexts properly at the end of each static method call, because you could leave open connections to the database which can cause performance problems.
Also, another problem you may run into is interlinking data; you cannot link two objects together by reference that were created from different contexts. And in LINQ to SQL, there is no way to explicitly detach an object...

Async communication between ViewModel and Model using MVVM-Light messaging?

I am starting off writing an app for WP7 that involves retrieving data via an HTTPWebRequest, I am trying to find the best way to get the data back to the ViewModel once the async request has returned with the data.
The Model and ViewModel are split into seperate projects in their own namespaces, so am not sure if the MVVM-Light can work in this way. Or should I just use a lightweight IoC implementation and delegates/events to call back to the ViewModel once the call in the Model has returned?
Try this out for size: http://amazedsaint.blogspot.com/2010/10/asynchronous-delegate-command-for-your.html. Great for testing as well, as none of your methods need to be Asynchronous, you just execute your Async Command.
I edited it slightly to extend the DelegateCommand so I could call RaiseCanExecuteChanged() method, as below:
public class AsyncDelegateCommand : DelegateCommand, ICommand
{
BackgroundWorker _worker = new BackgroundWorker();
Func<bool> _canExecute;
/// <summary>
/// The constructor
/// </summary>
/// <param name="action">The action to be executed</param>
/// <param name="canExecute">Will be used to determine if the action can be executed</param>
/// <param name="completed">Will be invoked when the action is completed</param>
/// <param name="error">Will be invoked if the action throws an error</param>
public AsyncDelegateCommand(Action action,
Func<bool> canExecute = null,
Action<object> completed = null,
Action<Exception> error = null
) : base(action, canExecute)
{
...
}
}
Hope that's of help.
Are you sure you're making the request in the right place? Typically the Model contains simply the data that you want to store/transfer and the View Model would handle transforming that Model's properties so that a View can display (or edit) that Model. I would imagine that Model objects are what you're going to get back from the HttpWebRequest, and so you may want to consider using a Service class to handle retrieving the data, though that may be overkill for your application.
Either way, I'd highly reccomend that you handle the request and the parsing of the response in the same place; otherwise you are spreading the logic across multiple places, which is just going to get confusing and hard to maintain.
If you need some help understanding MVVM, here are a number of resources that you may find helpf
Laurent Bugnions MIX10 presentation: Understanding the MVVM Pattern
Josh Smith's MSDN Magazine article: WPF Apps With The Model-View-ViewModel Design Pattern
Shawn Wildermuth's MSDN Magazine article: Model-View-ViewModel In Silverlight 2 Apps
Dan Wahlin's blog post: Getting Started with the MVVM Pattern in Silverlight Applications

Where to store feedback UI data in ASP.NET MVC 2?

I'm really new to both ASP.NET MVC and the MVC pattern generally.
For context, I'm currently running .NET 4.0, MVC 2.
I know that MCV 2 has a bunch of built in features for:
validation (both client and server side, through a variety of ways)
error handling (via attributes and other methods)
But what should be used to return feedback to the user that is neither an error nor validation?
For example, I have a simple form that returns a csv (using myController.base.file()) IF data is found.
If data is not found, I'd like to return the View with the Model as-is plus a message, like, "no data found, try a different date range"
Now,
should that kind of feedback message be stored in the model itself?, or
is there a cleaner mechanism for this?
Is that what the ModelStateDictionary is for?
UPDATE
just to clarify. I know there may be many ways to do what I want, but what's the correct MVC way of doing it.
Keep in mind the feedback data is not an error, and I don't want to redirect to some other view.
I think what might clear the air is the idea of a ViewModel. Except for the most simple scenarios, you'll find more than one kind of model in your project. They can go by many names, but I find these names helpful:
Pure domain models (Models)
This is where you have your ideal representations of our data model.
Application models (ViewModels)
These models account for the realities of displaying your domain model in a view. They contain data specifically needed for a specific view. It's perfectly acceptable to put something like a status message in this kind of a model.
I would recommend this insightful blog post which shows when, why and how to use ViewModels.
Example:
public class WidgetDataExportViewModel {
public DateTime StartDate {get;set;}
public DateTime EndDate {get;set;}
public MyEnum SomeOtherProperty {get;set;}
public string StatusMessage {get;set;}
public IEnumerable<Widget> Widgets {get;set;}
}
If you're talking about a message that you want to code for somewhere in your view, you ought to have that on your model and have your view consume it.
If you want to be able to handle system messages generally in the same way across your application (with a message at the top or side of the window, e.g.), you might create a utility method that puts the information in ViewData with a special key that could get picked up by your master page. If you use TempData instead, the message could be persisted across a redirect.
I have previously used ModelState.AddModelError successfuly to show a summary message. Just make sure you use a key that is not a Model field name.
Note: I have adapted my design from Steven Sanderson's book (see the index on RulesException)
Good luck
Validation errors directly stemming from user actions on your model (e.g. too short a password)
is at home as close to the model as possible.
General error messages of the "no data found" kind are easier addressed through having a dedicated Error View of some sort.
Added:
If I understand you correct, you prefer to stick with one view; be it validation gold or burning SQL servers ;-)
Not madly MVC2 experienced mysef, but being consistent and always create own models, never what you might be served by what(not) ORM you're using (Linq<->SQL, EF) should give you the fine-graininess you need.
If my memory serves me correct I think the Nerddinner could be enlightening; and if you really
want to take the plunge, why don't go Deep Inside ASP.NET MVC 2 Model Metadata and Validation
I've gotten my MVC mainly from books, but the blogosphere has definitely started producing golden material on the more "foreign" parts of ASP.NET MVC.
My real aha moment regarding the M of MVC came while reading
Steve Michelotti's ASP.NET MVC View Model Patterns (a great source for MVC2 info all round)

Resources