C# Equivalent of Spring #ModelAttribute - c#

I am a Java/Spring dev new to C#/Entity Framework and was wondering if there was a C# equivalent to Springs #ModelAttribute Annotation
ex:
In a java controller/servlet I can do
#ModelAttribute("form")
public IContactForm getContactForm() {
return new ContactForm();
}
or something like
#ModelAttribute("list")
public List getItems() {
return new ArrayList( ... );
}
and in the view/markup - reference said attribute by
<form:form path="myField">
or ( pseduo )
<select>
for (String s : list) {
<option value="${s}" />
}
</select>
Then, if i were to post said form I could create the ContactForm object through the use of #ModelAttribute() IContactForm form
ex:
public void handleJsonPost( #Valid #ModelAttribute("form") IContactForm form) {
String x = form.getAField();
}
So, to reiterate the quesiton, does .NET/Entity Framework have a built in functionality like Springs #ModelAttribute or is there a Nuget package I can download? ...or anything at all?

This is done using the Razor view engine. I suggest you reference this link to get started and scroll down to "Task 2 - Creating the Edit View" if you want to dive straight into forms.

Related

How can one make a generic Blazor Component which can take in varying lists of objects as parameters?

<label for="client-list" class="label" style="display:inline">Client</label> <i class="fas fa-user-plus"></i> <br />
<input class="input field" value="#(GetClientName(#ClientID))" #oninput="#ClientSearchUpdated"/>
#if (AvailableClients != null)
{
<ul>
#foreach (var client in AvailableClients)
{
<li id="#client.Id" #onclick="#(e => SetClientID(e, client.Id))">#client.FullName</li>
}
</ul>
}
I want to be able to take the above markup/code and turn it into a reusable component that will create an input field that when typed in, will display a list of results based on what is typed in. These results will be of the type of List that is passed in. eg: if a List<Person> is passed in, it would search the DB for people matching the search terms that a user types in. In the generic version, the List holding the objects to be searched wouldn't be AvailableClients, nor the functions getting / updating information specific to clients, of course.
At the end of this, my goal is to be able to replace the above code fragment with:
<SearchableDropdown DropdownItems="AvailableClients"></SearchableDropdown>
(The fields that are searched are currently determined by the sproc used in each of the DataAccessObjects at the moment)
The problem I've come across trying to develop such a generic component is that I'm not super familiar with generics to begin with (I understand the base concepts, what the syntax for using generics is, etc, but I haven't created a lot of generic code), and especially not when it comes to integrating that concept with Blazor.
What I've tried so far is:
Using Inheritance to accept a generic List<ISearchableDropdownItem>, and the objects in my system would implement this Interface, which would have two members: ID and DropdownDisplayInfo which would allow for the dropdown to send information about which item is clicked, as well as give each item something to display for each item in the Search Results.
The problem with this approach is that I then have to create Interfaces for the DataAccess layer & the Services which I've created in my Blazor application as well. This is a long, cascading problem that will lead me down an interface-creation rabbit hole. I'm not even 100% sure if this solution will work in the end.
Use an #typeparam TDropdownItem directive to allow the type used throughout the component to be of that which is passed in.
The obvious problem with this is that it will still put a lot of responsibility on the person utilizing the SearchableDropdown component to give the appropriate markdown for the RenderFragment, plus that still leaves the problem of having generic, "GetObjectName(ObjectID)" and "ObjectSearchUpdated" functions for the component.
Is there a decently straightforward way of implementing this that I'm just completely missing? Am I even remotely on the right track, and it's just going to take a bunch of refactoring of existing code to make things work?
If it is not possible to implement interface approach, maybe you can try create a common proxy object to pass arguments.
For example, you may try to pass Id, Name and Delegate as a proxy list with LINQ.
Prepare your arguments within OnInitialized or wherever you initiate it.
Here is a simple concept which shows how you can use delegation.
Delegation, data and proxy class initialization:
public delegate void OnClick(UIEventArgs eventArgs, int id, string name);
private class ExampleData
{
// let's assume that it has different naming convention for your case
public int IdValue {get;set;}
public string Fullname {get;set;}
}
private class SearchableDropdownItem
{
public int Id {get;set;}
public string Name {get;set;}
public OnClick OnClicked {get;set;}
}
Example proxy creation:
public void Prepare()
{
var dataList = new List<ExampleData>();
for(int i=0; i<3; i++)
{
dataList.Add(new ExampleData(){
IdValue = i+1,
Fullname = "test" + (i + 1)
});
}
var searchableItemList = dataList.Select(x => new SearchableDropdownItem(){
// here is how you can set callback and fields
Id = x.IdValue,
Name = x.Fullname,
OnClicked = new OnClick(SetClientID)
}).ToList();
}
public void SetClientID(UIEventArgs eventArgs, int id, string name)
{
// implement your code
}
And pass searchableItemList to component and use it:
<SearchableDropdown ItemList="searchableItemList"></SearchableDropdown>
...
#foreach (var item in searchableItemList)
{
<li id="#item.Id" #onclick="#(e => item.OnClicked(e,client.Id,client.Name))">#item.Name</li>
}

Set TextBox value from ViewBag list in MVC5 C#

How can I set TextBox value in MVC5 from ViewBag which contains a list? As you can see my list is in Viewbag.photos and I want to have each value of photo.id in my TextBox and then pass it to controller
#foreach (var photo in ViewBag.photos)
{
#if (#photo.comment != null)
{
<h6>#photo.comment</h6>
}
else
{
<h6> - </h6>
}
#Html.TextBox("photoID", #photo.id)
}
Trying to do that I get an error:
Error CS1973 'HtmlHelper>' has no applicable method
named 'TextBox' but appears to have an extension method by that name.
Extension methods cannot be dinamically dispached.
Maybe there's another workaround?
This is happening because ViewBag.photos is a dynamic object. The compiler cannot know its type, so you have to manually cast it to its original type.
For example:
#Html.TextBox("photoID", (int)photo.id)
As a side note (I'm not sure whether this will prevent your code from working, but it's good practice anyway), you also have bit too many #s: to cite Visual Studio, once inside code, you do not need to prefix constructs like "if" with "#". So your final code will look like:
#foreach (var photo in ViewBag.photos)
{
if (photo.comment != null)
{
<h6>#photo.comment</h6>
}
else
{
<h6> - </h6>
}
#Html.TextBox("photoID", (int)photo.id)
}
You should also consider using ViewModels instead of ViewBag to pass data between your controllers and your views.

Evaluating nested property calls from a string

I am currently reading the "500 Lines or Less" book, the chapter for creating a Template Engine from Ned Batchelder.
Their example is using Python. In their template engine they are building code as a string and then they are calling exec (docs) to evaluate the string as Python code.
def get_globals(self):
"""Execute the code, and return a dict of globals it defines."""
# A check that the caller really finished all the blocks they started.
assert self.indent_level == 0
# Get the Python source as a single string.
python_source = str(self)
# Execute the source, defining globals, and return them.
global_namespace = {}
exec(python_source, global_namespace)
return global_namespace
This is very convenient, because they can easily evaluate expressions in the template such as {{object.property.property}}
With C# as my main programming language I am wondering how can this be achieved (in the context of building a template engine as in the book)?
Research and thoughts
First I don't believe there is an exec equivalent in C#.
One way I can think of it is to recursively use Reflection to get the List of properties of an object (handling checks for Null References), but I don't like this from performance point of view.
Another way is to use Roslyn's ScriptEngine class (which I haven't used so correct me if I am wrong). But I am afraid that this won't be good because this is supposed to be a library and it won't be able to be used with older versions of C# and .NET. Example
Q: First I don't believe there is an exec equivalent in C#.
As for compling C# code, CS-Script library can be used to achieve this in various ways.
For example:
dynamic script = CSScript.Evaluator
.LoadCode(#"using System;
using Your.Custom.Relevant.Namespace;
public class Executer
{
public object Execute()
{
return SomeStaticClass.array[123];
}
}");
int result = script.Execute();
//shorter way
int a = (int)CSScript.Evaluator.Evaluate("some.namespace.SomeStaticClass.array[123]");
Read more here: http://www.csscript.net/
CS-Script isn't made for templating.
Unless you create it yourself by manipulating the strings before you compile them.
But how can I pass some Context for the template engine
You can pass a context into a function like this:
dynamic script = CSScript.Evaluator
.LoadCode(#"
using System;
using Namespace.Of.The.Context;
public class Executer {
public string Execute(Context ctx) {
return ctx.Person.Firstname + ctx.Person.Lastname;
}
}");
int result = script.Execute(new Context(new Person("Rick", "Roll")));
Q: Can I call CSScript from a normal C# application lets say a Web App?
A: Yes.
S-Script currently targets Microsoft implementation of CLR (.NET
2.0/3.0/3.5/4.0/4.5) with full support on Mono.
Basically if it runs C#, it can be compiled accordingly to the .net-framework that the library is executed on, so if your project is ran on .net4.5, any feature of that .net version is available including any external references in your project too.
You can use Microsoft.CSharp.CSharpCodeProvider in order to compile code on fly.
https://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx
Like this:
static void Main(string[] args)
{
string source =
#"
namespace Test
{
public class Test
{
public void HelloWorld()
{
System.Console.WriteLine(""Hello World"");
}
}
}
";
var options = new Dictionary<string, string> { {"CompilerVersion", "v3.5"} };
var provider = new CSharpCodeProvider(options);
var compilerParams = new CompilerParameters{GenerateInMemory = true, GenerateExecutable = false };
var results = provider.CompileAssemblyFromSource(compilerParams, source);
var method = results.CompiledAssembly.CreateInstance("Test.Test");
var methodInfo = method.GetType().GetMethod("HelloWorld");
methodInfo.Invoke(method, null);
}

Custom keyword coloring in Visual Studio 2010+

I'm trying to add custom coloring for only certain keywords in my Visual Studio editor for C# code. I want to be able to color any type that implements IDisposable as a different color. Ideally I'd like to create a simple list of classes/interfaces that derive from IDisposable in some sort of configuration that I can edit. (Although if you said there was a method/plugin that would automatically find all disposable types and color them independently that would be the Holy Grail).
I've done a ton of research and it looks like an "editor classifier" extension might do the trick. However I created one that merely tries to color the word "Stream" and although it does hit my code that attempts to highlight that word, it does not end up highlighted in the editor.
I have added my VS extension to Github here
This really seems like this should be fairly straightforward but I have gone down many alleys on this one only to find dead-ends. Is there a simpler way to do this, or is my extension broken?
Update
Very strange. I just ran my extension again and although it does not highlight the text in the editor it highlights all instances of "Stream" in the popup text when you hover over a type/variable! Is there any way to get it to apply to the editor?
Depending on wether you are using Jetbrains Resharper or not you may write a plugin for that. That way you are able not only to add visual notification of IDisposable on a variable but also provide quickfixes if, and only if, it is not beeing called, which is what i am assuming you want to catch. Mind you that i can imagine that there's already a R# plugin for that. I know i've considered this too, but i was too lazy to write a plugin for that.
Don't get me wrong btw - If you're not using r# yet you should consider trying it out.
Among others you'd be working with this: API-QuickFix
There are also ways to define custom keywords, as resharper does, given by a custom markup and apply quickfixes to that.
PS: No i don't work at jetbrains. it's just that good :)
UPDATE:
potential VS Extension fix?
check this one out: MSDN Link Highlighting Text
I tried opening your github project but couldn't so i thought i'll just check msdn instead. it seems you are deriving from the wrong class to fulfill your needs?
MSDN keyword "Editors - Extending the Editor - Walkthrough: Highlighting Text"
I know SO wants code on the site, but msdn links going down is rather unlikely and with the given information the content can be found easily enough :)
I'm a bit late to the party, but hey, why not throw my 2 cents in.
As you've explained in your question, your project has two basic parts:
Finding the classes that implement IDisposable
Highlighting them
The first is by far the hardest, though not impossible. A word-list based approach is probably the simplest, though it should be possible with Roslyn to figure out on the fly which classes inherit IDisposible.
You could also always resort to loading the project's compiled .exe/.dll in the background after a build and figuring out what the types are there, but you'd still have to write some magic glue code to figure out what short class names in the code referred to what actual full-name classes in the assembly.
The second part, highlighting, is quite easy once you know how to do it (it helps that I've spent the last several months working full-time on extending VS). Of course, with Visual Studio, nothing is as simple as it looks (despite the efforts of Microsoft to try to make it user-friendly). So, I've built a sample extension that highlights just classes named "Stream" within C# files to get you started.
The relevant code is below, and the full project source is on GitHub). It starts with a classification-tagger provider:
[Export(typeof(ITaggerProvider))]
[ContentType("CSharp")]
[TagType(typeof(ClassificationTag))]
[Name("HighlightDisposableTagger")]
public class HighlightDisposableTaggerProvider : ITaggerProvider
{
[Import]
private IClassificationTypeRegistryService _classificationRegistry = null;
[Import]
private IClassifierAggregatorService _classifierAggregator = null;
private bool _reentrant;
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
if (_reentrant)
return null;
try {
_reentrant = true;
var classifier = _classifierAggregator.GetClassifier(buffer);
return new HighlightDisposableTagger(buffer, _classificationRegistry, classifier) as ITagger<T>;
}
finally {
_reentrant = false;
}
}
}
Then the tagger itself:
public class HighlightDisposableTagger : ITagger<ClassificationTag>
{
private const string DisposableFormatName = "HighlightDisposableFormat";
[Export]
[Name(DisposableFormatName)]
public static ClassificationTypeDefinition DisposableFormatType = null;
[Export(typeof(EditorFormatDefinition))]
[Name(DisposableFormatName)]
[ClassificationType(ClassificationTypeNames = DisposableFormatName)]
[UserVisible(true)]
public class DisposableFormatDefinition : ClassificationFormatDefinition
{
public DisposableFormatDefinition()
{
DisplayName = "Disposable Format";
ForegroundColor = Color.FromRgb(0xFF, 0x00, 0x00);
}
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { };
private ITextBuffer _subjectBuffer;
private ClassificationTag _tag;
private IClassifier _classifier;
private bool _reentrant;
public HighlightDisposableTagger(ITextBuffer subjectBuffer, IClassificationTypeRegistryService typeService, IClassifier classifier)
{
_subjectBuffer = subjectBuffer;
var classificationType = typeService.GetClassificationType(DisposableFormatName);
_tag = new ClassificationTag(classificationType);
_classifier = classifier;
}
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (_reentrant) {
return Enumerable.Empty<ITagSpan<ClassificationTag>>();
}
var tags = new List<ITagSpan<ClassificationTag>>();
try {
_reentrant = true;
foreach (var span in spans) {
if (span.IsEmpty)
continue;
foreach (var token in _classifier.GetClassificationSpans(span)) {
if (token.ClassificationType.IsOfType(/*PredefinedClassificationTypeNames.Identifier*/ "User Types")) {
// TODO: Somehow figure out if this refers to a class which implements IDisposable
if (token.Span.GetText() == "Stream") {
tags.Add(new TagSpan<ClassificationTag>(token.Span, _tag));
}
}
}
}
return tags;
}
finally {
_reentrant = false;
}
}
}
I've only tested this on VS2010, but it should work for VS2013 too (the only thing that might be different is the class classification name, but that's easy to discover with a well-placed breakpoint). I've never written an extension for VS2012, so I can't comment on that, but I know it's quite close to VS2013 in most respects.
So, one possible solution(I believe this one works):
1) Create your own content type which inherits from csharp.
2) Create new TextViewCreationListener which will swap out all "csharp" content types with your own one, thus potentially "disarming" all the other classifiers.
3) Register your classifier to handle your own content type.
Here is some of the code:
[Export(typeof(IVsTextViewCreationListener))]
[ContentType("csharp")]
[TextViewRole(PredefinedTextViewRoles.Editable)]
class TextViewCreationListener : IVsTextViewCreationListener {
internal readonly IVsEditorAdaptersFactoryService _adaptersFactory;
[Import] internal IContentTypeRegistryService ContentTypeRegistryService = null;
[ImportingConstructor]
public TextViewCreationListener(IVsEditorAdaptersFactoryService adaptersFactory) {
_adaptersFactory = adaptersFactory;
}
#region IVsTextViewCreationListener Members
public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) {
var textView = _adaptersFactory.GetWpfTextView(textViewAdapter);
var myContent = ContentTypeRegistryService.GetContentType(MyContentType);
if(myContent == null)
{
ContentTypeRegistryService.AddContentType(MyContentType, new[] {"csharp"});
myContent = ContentTypeRegistryService.GetContentType(MyContentType);
}
// some kind of check if the content type is not already MyContentType.
textView.TextBuffer.ChangeContentType(myContent, null);
}
#endregion
}
And now, just modify your IClassifierProvider to register with your own content type, as such: [ContentType(MyContentType)]
Iin your own IClassifier, you can basically do your own calculation and once you think you can't handle the stuff, you could pass the control to other classifiers.
If you use MEF and import IClassifierAggregatorService, you can get a "MASTER-classifier" which will run all the logic for you. I haven't implemented it yet, but I've suggestes something similiar in the past, and it seemed to work. Alternatively you could maybe use [ImportMany] with List<IClassifier> and filter out the csharp ones?!

Using jquery templating with c#'s dynamics?

I didn't think this would be that hard, but I am trying to pass a class that has a dynamic for a property (which is currently being set as an expando object in the c#) into the View. In this view, a good bit is getting rendered w/ some jQuery Templating and I thought that if I had this in the c# code:
public dynamic SomeProperty {get;set;}
...
SomeProperty = new ExpandoObject();
SomeProperty.SomeValue = "5";
return View(TheClassThatContainsSomeProperty);
Such that in the jquery templating I could do:
${SomeProperty.SomeValue}
...and that it would hopefully work. It doesn't... If you inspect the response, you can see that it essentially gets sent over as a dictionary:
SomeProperty: [{SomeValue, Value:5}]
0: {Key:SomeValue, Value:5}
which leads to (I guess) my next question: Is there an easy way to access dictionaries in jquery templating? I did try this:
${SomeProperty["SomeValue"]}
to no avail either. At this point the only thing I know to do is to leverage the ability to put a function in the template (as copied here from the jquery website):
Template:
<tr><td>${getLanguages(Languages, " - ")}</td></tr>
Code:
function getLanguages( data, separator ) {
return data.join( separator );
}
So am I over complicating this? Can I easily either 1) access a dynamic value from jquery template or 2) Easily lookup a value from a dictionary in jquery template?
ExpandoObject derives from IEnumerable<KeyValuePair<string, Object>>, and most serializers will recognize a dynamic as this type when you assign an ExpandoObject. This is why you see an array type on the javascript side with named Key::Value pairs.
ExpandoObject Class (System.Dynamic) # MSDN
One alternative to using ExpandoObject is to use C# anonymous types. When serialized to json, these map field by field as you expect.
Anonymous Types (C# Programming Guide) # MSDN
It is possible to access values declared with dynamic from jQuery, but most likely you won't be returning a MVC View() with the model to be consumed with jQuery, as any server-side view template engine (razor, etc.) can already perform the same template activities with less overhead. Instead, jQuery templates are better used with Ajax calls.
Here are code examples demonstrating three cases where a variable declared dynamic on the server is consumed with jQuery templates in the browser.
The first example uses an anonymous type for the member field SomeValue, and has a jQuery template that treats it as a member object.
The second example uses an array of anonymous types for the member field SomeValue and has a template that uses {{each}} syntax to enumerate the items. Note that this is a scenario where things can go badly with dynamic, as you get no strongly-typed support and must either know the correct type or discover it at the time you access it.
The third example uses an ExpandoObject for the member field SomeValue, and has a jQuery template like the first example (single member object). Note that in this case, we need to use a helper function pivotDictionaryMap() to pivot Key::Value pairs into object members.
Starting with a blank C# MVC3 Web Project, we need to modify three files to demonstrate these examples.
Inside _Layout.cshtml, add script includes for jQuery templates and a proper version of jQuery in your <head> element.
<script src="#Url.Content("~/Scripts/jquery-1.8.2.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.tmpl.js")" type="text/javascript"></script>
Inside HomeController.cs, we'll add some methods that return json ActionResults. Also, for brevity we'll just declare a class SomeModelType here; and note that a proper application would probably have this class declared in its Models.
using System.Dynamic; // up top...
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult SomeDataSource(int id)
{
dynamic d = new { innerId = 99, innerLabel = "inside object" };
SomeModelType obj = new SomeModelType(id, "new object");
obj.SomeValue = d;
return Json(obj, "text/plain");
}
public ActionResult SomeDataSourceWithArray(int id)
{
dynamic d1 = new { innerId = 99, innerLabel = "inside object (first array member)" };
dynamic d2 = new { innerId = 100, innerLabel = "inside object (second array member)" };
SomeModelType obj = new SomeModelType(id, "new object");
obj.SomeValue = new object[] {d1, d2};
return Json(obj, "text/plain");
}
public ActionResult SomeDataSourceWithExpando(int id)
{
dynamic d = new ExpandoObject();
d.innerId = 99;
d.innerLabel = "inside object";
SomeModelType obj = new SomeModelType(id, "new object");
obj.SomeValue = d;
return Json(obj, "text/plain");
}
}
public class SomeModelType
{
public SomeModelType(int initId, string initLabel)
{
Id = initId;
Label = initLabel;
}
public int Id { get; set; }
public string Label { get; set; }
public dynamic SomeValue { get; set; }
}
Finally, in the default view, we will add script tags for the templates and the javascript necessary to consume them. Note the use of $.post() and not $.get(), as a JsonResult in MVC disallows GET requests by default (you can turn these on with an attribute).
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<script id="someDataTemplate" type="text/x-jquery-tmpl">
Item <b>${Id}</b> is labeled "<i>${Label}</i>" and has an inner item with id <b>${SomeValue.innerId}</b> whose label is "<i>${SomeValue.innerLabel}</i>".
</script>
<h3>SomeDataSource Example #1 (Single Item)</h3>
<div id="someData">
</div>
<script id="someDataArrayTemplate" type="text/x-jquery-tmpl">
Item <b>${Id}</b> is labeled "<i>${Label}</i>" and has these inner items:
<ul>
{{each SomeValue}}
<li><b>${innerId}</b> has a label "<i>${innerLabel}</i>".</li>
{{/each}}
</ul>
</script>
<h3>SomeDataSource Example #2 (Array)</h3>
<div id="someArrayData">
</div>
<script id="someDataTemplateFromExpandoObject" type="text/x-jquery-tmpl">
Item <b>${Id}</b> is labeled "<i>${Label}</i>" and has an inner item with id <b>${SomeValue.innerId}</b> whose label is "<i>${SomeValue.innerLabel}</i>".
</script>
<h3>SomeDataSource Example #3 (Single Item, Expando Object)</h3>
<div id="someDataFromExpandoObject">
</div>
<script type="text/javascript">
function pivotDictionaryMap(src)
{
var retval = {};
$.each(src, function(index, item){
retval[item.Key] = item.Value;
});
return retval;
}
</script>
<script type="text/javascript">
$(document).ready(function() {
// Ajax Round-Trip to fill example #1
$.post("/Home/SomeDataSource/5", function(data) {
$("#someDataTemplate").tmpl(data).appendTo("#someData");
}, "json");
// Ajax Round-Trip to fill example #2
$.post("/Home/SomeDataSourceWithArray/67", function(data) {
$("#someDataArrayTemplate").tmpl(data).appendTo("#someArrayData");
}, "json");
// Ajax Round-Trip to fill example #3
$.post("/Home/SomeDataSourceWithExpando/33", function(data) {
data.SomeValue = pivotDictionaryMap(data.SomeValue);
$("#someDataTemplateFromExpandoObject").tmpl(data).appendTo("#someDataFromExpandoObject");
}, "json");
});
</script>
I won't mark my own answer as "correct" just on premise that I don't like this answer. If someone figures out what I was trying to do above, I'll gladly give you the points.
function getDisplayValue(data, toMatchOn) {
var _return = $.grep(data, function (n, i) { return (n.Key == toMatchOn); })[0];
if (_return != null)
return _return.Value;
return "";
}
and in the jquery template:
${getDisplayValue(DisplayFields, 'Something')}
So I was able to get this to work with the method described above so here it is as a possible solution but I don't know javascript well enough to know how bad of a performance hit this could be creating (and as I research it, I'll update this answer) but I thought javascript dictionaries were optimized against their key value, so the fact that MVC doesn't serialize expando as a true javascript dictionary seems to make this answer very inefficient. And the fact that I originally took this tack with the dynamic c# object was that I originally thought this would serialize down into a cleaner form. Anyway, this works but Occam's Razor is just making this feel way too complicated.
Not sure if this will help or not, but have a look at this gist. It is hard to tell from your code snippets but if you are turning that ExpandoObject into JSON, then try wrapping it on a DynamicJsonObject first, as done in the gist.
Code from the Gist copy/pasted for those who don't want to click the link:
// By default, Json.Encode will turn an ExpandoObject into an array of items,
// because internally an ExpandoObject extends IEnumerable<KeyValuePair<string, object>>.
// You can turn it into a non-list JSON string by first wrapping it in a DynamicJsonObject.
[TestMethod]
public void JsonEncodeAnExpandoObjectByWrappingItInADynamicJsonObject()
{
dynamic expando = new ExpandoObject();
expando.Value = 10;
expando.Product = "Apples";
var expandoResult = System.Web.Helpers.Json.Encode(expando);
var dictionaryResult = System.Web.Helpers.Json.Encode(new DynamicJsonObject(expando));
Assert.AreEqual("{\"Value\":10,\"Product\":\"Apples\"}", expandoResult); // FAILS (encodes as an array instead)
Assert.AreEqual("{\"Value\":10,\"Product\":\"Apples\"}", dictionaryResult); // PASSES
}
You're right that performance is going to be sketchy if you're iterating over an array to find a key. But you should be able to comfortably convert the key-value-pair array (that the server sends back) into a "true" Javascript dictionary/map. Eg:
var kvps = [ {key: "test", value: "expando"}, {key: "hello", value: "world" } ];
var map = {};
kvps.forEach(function(kvp) { map[kvp.key] = kvp.value; } );
console.log( JSON.stringify(map) );
{"test":"expando","hello":"world"}
Of course, if you're nested objects, then you'd have to apply recursion to the above approach to make it work.

Categories