How to pass a dynamic DataModel to an Action? - c#

I dynamically build a ASP.NET MVC3 View with some inputfields, but cause of the dynamic creation of the UI, I do not know which inputfields will be available.
After clicking on a button (search) I want to pass the actual inputdata as an Model(ViewModel) to an Action. Here`s the Problem, I dont know which properties does the Model have and how to pass this dynamic Model/Object to the Action.
I tried to simply do it like so:
public ActionResult StartQuery(dynamic request)
{
var test = request;
//...
}
but don`t know how to handle this dynamic object. If this is the correct way, do I have to use reflection for this or does anybody can give me a tip how to correctly push dynamic data to an Action?
Thanks in advance,
Cordell
-EDIT
Even if I know the input IDs it doesn`t worked out. Keep getting DynamicBinderExceptions.
public ActionResult StartQuery(dynamic request)
{
string test = request.ArticleNo;
string test2 = request.ArtNoOfSuppl;
string test3 = request.ArticleGrp;
//...
These are the exact IDs for the test, I cannot build on the names, the could change.

Don't use dynamic, if you know the names you can fetch them directly from the request:
public ActionResult StartQuery()
{
string test = Request["ArticleNo"];
string test2 = Request["ArtNoOfSuppl"];
string test3 = Request["ArticleGrp"];
...
}
If you don't know them you could still loop through Request.Params collection and based on your rules find the parameters you need.

Related

Display Each Element From Array Using HTML Helper in C#

This is probably a very simple problem, but I am extremely new to C# / MVC and I have been handed a broken project to fix. So it's time to sink or swim!
I have an array of strings that is being passed from a function to the front end.
The array looks something like
reports = Directory.GetFiles(#"~\Reports\");
On the front end, I would like it to display each report, but I am not sure how to do that.
This project is using a MVC, and I believe the view is called "Razor View"? I know that it's using an HTML helper.
In essence, I need something like
#HTML.DisplayTextFor(Model.report [And then print every report in the array]);
I hope that makes sense.
If you want to display the file name array you can simply use a foreach:
#foreach(var report in Model.Reports){ #report }
Note that you should add the Reports property to your view model:
public class SampleViewModel
{
public string [] Reports { get; set; }
}
You could use ViewData or TempData but I find that using the view model is the better way.
You can then populate it:
[HttpGet]
public ActionResult Index()
{
var model = new SampleViewModel(){ Reports = Directory.GetFiles(#"~\Reports\")};
return View(model);
}
And use it in the view as you see fit.
Here is a simple online example: https://dotnetfiddle.net/5WmX5M
If you'd like to add a null check at the view level you can:
#if(Model.Reports != null)
{
foreach(var report in Model.Reports){ #report <br> }
}
else
{
<span> No files found </span>
}
https://dotnetfiddle.net/melMLW
This is never a bad idea, although in this case GetFiles will return an empty list if no files can be found, and I assume a possible IOException is being handled.

Controller Method Get : Get specify attributes from a Class

my question is simple, I´m try to do a controller that pass some attributes of a Model
This is the method:
[HttpGet("getAllUserInfo")]
public async Task<ActionResult<object>> GetAll() {
var test = await _context.Users.ToListAsync(); //here I have the all Info of all users
//In the return I want pass some attributes of user, creating a new object (ex: The user have a password but I don´t want to show that)
return Ok(new { test.getType().Name, test.getType().Mail });
//this above is the part of the code I don´t understand
}
Is there any alternative to this scenario or what are the possible ideas to make this possible?
Sorry if there are any spelling mistakes in the title and document, but I think the idea where I have difficulty understanding is there
Are you just trying to project a list of objects into a new list of different objects? You can do that with .Select(). For example:
return Ok(test.Select(t => new { Name = t.Name, Mail = t.Mail }));

Value cannot be null. Parameter name: String

Please take a look at the following code. It's in handler.asxh.
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
new RequestManagementFacade().PinRequest(Int32.Parse(context.Request.QueryString["requestId"]), (Boolean.Parse(context.Request.QueryString["isPinned"])));
}
This is showing the following error:
Value cannot be null. Parameter name: String
There is value being passed as I have checked the context request query string, however, the code breaks at this stage.
This handler will connect to the business logic layer.
There is value being passed as i have checke dthe context request query string
I strongly suspect your diagnostics are incorrect then. Values don't magically go missing - you need to question your assumptions. This is easy to debug through though. I would suggest changing your code to:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
string requestId = context.Request.QueryString["requestId"];
string isPinned = context.Request.QueryString["isPinned"];
var facade = new RequestManagementFacade();
facade.PinRequest(Int32.Parse(requestId), Boolean.Parse(isPinned));
}
It's then really simple to step through and find out what's going on.
It is likely that either context.Request.QueryString["requestId"] or context.Request.QueryString["isPinned"] is not returning a valid string value. Check that both values are passed in the query string with the proper IDs, those being of course requestId and isPinned.
Okay solved when passing the values to the handler i inserted it as
"PinRequest.ashx?="+requestId+isPinned"
Which gave me the result 2True
So i realised the hiccup was with not including the string names
"PinRequest.ashx?requestId=" + this._requestId + "&isPinned=" + this._isPinned
Thanks for you help guys
LeviBotelho Thank you made me check something i was missing out when checking as its javascript
experienced the error while using Int32.Parse(myString) to convert string to int and afterwards assign the value to an object's attribute. Using another method for converting(Convert.ToInt32(myString)) string to int worked for me.

Capturing old values for model in MVC3?

I'm wanting to capture the old values within a model so I can compare with the new values after submission, and create audit logs of changes a user makes.
My guess is doing it with hidden input boxes with duplicated old value properties would be one way. But wondering if there are any other good alternatives?
Thanks
In the save method, just go and get the original object from the database before saving the changes, then you have your old and new values to compare against? :)
This sounds like standard auditing. You should not worry about what has changed just capture EVERYTHING and who made the change. Unless there is some sort of real time reporting that needs to be done.
Possible auditing implementations:
CQRS, in a nutshell it tracks every change to a given object. The downside is it's an architecture that is more involved to implement.
The Rolling ledger. Each insert is a new row in the database. The most current row is used for display purposes, but with each update, a new row is inserted into the database.
Yet another approach is to save it off into an audit table.
All get the job done.
You could also store the original model in the view bag and do something like this...
// In the controller
public ActionResult DoStuff()
{
// get your model
ViewBag.OriginalModel = YourModel;
return View(YourModel);
}
// In the View
<input type="hidden" name="originalModel" value="#Html.Raw(Json.Encode(ViewBag.OriginalModel));" />
// In the controller's post...
[HttpPost]
public ActionResult DoStuff(YourModel yourModel, string originalModel)
{
// yourModel will be the posted data.
JavaScriptSerializer JSS = new JavaScriptSerializer();
YourModel origModel = JSS.Deserialize<YourModel>(originalModel);
}
I didn't get a chance to test this, just a theory :)
Exactly what mattytommo says is the preferred method all around
Instantiate new view model for creating a new entity
public ActionResult Edit(int id) {
var entity = new Entity(id); // have a constructor in your entity that will populate itself and return the instance of what is in the db
// map entity to ViewModel using whatever means you use
var model = new YourViewModel();
return View(model);
}
Post changes back
[HttpPost]
public ActionResult Edit(YourViewModel model) {
if (ModelState.IsValid) {
var entity = new YourEntity(model.ID); // re-get from db
// make your comparison here
if(model.LastUserID != entity.LastUserID // do whatever
... etc...
}
return View(model);
}

Persist data using JSON

I'm tryping to use JSON to update records in a database without a postback and I'm having trouble implementing it. This is my first time doing this so I would appreciate being pointed in the right direction.
(Explanation, irrelevant to my question: I am displaying a list of items that are sortable using a jquery plugin. The text of the items can be edited too. When people click submit I want their records to be updated. Functionality will be very similar to this.).
This javascript function creates an array of the objects. I just don't know what to do with them afterwards. It is called by the button's onClick event.
function SaveLinks() {
var list = document.getElementById('sortable1');
var links = [];
for (var i = 0; i < list.childNodes.length; i++) {
var link = {};
link.id = list.childNodes[i].childNodes[0].innerText;
link.title = list.childNodes[i].childNodes[1].innerText;
link.description = list.childNodes[i].childNodes[2].innerText;
link.url = list.childNodes[i].childNodes[3].innerText;
links.push(link);
}
//This is where I don't know what to do with my array.
}
I am trying to get this to call an update method that will persist the information to the database. Here is my codebehind function that will be called from the javascript.
public void SaveList(object o )
{
//cast and process, I assume
}
Any help is appreciated!
I have recently done this. I'm using MVC though it shouldn't be too different.
It's not vital but I find it helpful to create the contracts in JS on the client side and in C# on the server side so you can be sure of your interface.
Here's a bit of sample Javascript (with the jQuery library):
var item = new Item();
item.id = 1;
item.name = 2;
$.post("Item/Save", $.toJSON(item), function(data, testStatus) {
/*User can be notified that the item was saved successfully*/
window.location.reload();
}, "text");
In the above case I am expecting text back from the server but this can be XML, HTML or more JSON.
The server code is something like this:
public ActionResult Save()
{
string json = Request.Form[0];
var serializer = new DataContractJsonSerializer(typeof(JsonItem));
var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(json));
JsonItem item = (JsonItem)serializer.ReadObject(memoryStream);
memoryStream.Close();
SaveItem(item);
return Content("success");
}
Hope this makes sense.
You don't use CodeBehind for this, you use a new action.
Your action will take an argument which can be materialized from your posted data (which, in your case, is a JavaScript object, not JSON). So you'll need a type like:
public class Link
{
public int? Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Url { get; set; }
}
Note the nullable int. If you have non-nullable types in your edit models, binding will fail if the user does not submit a value for that property. Using nullable types allows you to detect the null in your controller and give the user an informative message instead of just returning null for the whole model.
Now you add an action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DoStuff(IEnumerable<Link> saveList)
{
Repository.SaveLinks(saveList);
return Json(true);
}
Change your JS object to a form that MVC's DefaultModelBinder will understand:
var links = {};
for (var i = 0; i < list.childNodes.length; i++) {
links["id[" + i + "]"] = list.childNodes[i].childNodes[0].innerText;
links["title[" + i + "]"] = list.childNodes[i].childNodes[1].innerText;
links["description[" + i + "]"] = list.childNodes[i].childNodes[2].innerText;
links["url[" + i + "]"] = list.childNodes[i].childNodes[3].innerText;
}
Finally, call the action in your JS:
//This is where I don't know what to do with my array. Now you do!
// presumes jQuery -- this is much easier with jQuery
$.post("/path/to/DoStuff", links, function() {
// success!
},
'json');
Unfortunately, JavaScript does not have a built-in function for serializing a structure to JSON. So if you want to POST some JSON in an Ajax query, you'll either have to munge the string yourself or use a third-party serializer. (jQuery has a a plugin or two that does it, for example.)
That said, you usually don't need to send JSON to the HTTP server to process it. You can simply use an Ajax POST request and encode the form the usual way (application/x-www-form-urlencoded).
You can't send structured data like nested arrays this way, but you might be able to get away with naming the fields in your links structure with a counter. (links.id_1, links.id_2, etc.)
If you do that, then with something like jQuery it's as simple as
jQuery.post( '/foo/yourapp', links, function() { alert 'posted stuff' } );
Then you would have to restructure the data on the server side.

Categories

Resources