Convert Model to ViewModel without automapper - c#

Hi this is my convert a Ilist of model to a Ilist of ViewModel method
public static IList<PostViewModel> ConvertToPostViewModelList(this IList<Post> posts)
{
return posts.Select(ConvertToPostViewModel).ToList();
}
and also this is the ConvertToPostViewModel
public static PostViewModel ConvertToPostViewModel(this Post post)
{
var blogPostViewModel = new PostViewModel
{
Id = post.Id,
Body = post.Body,
Summary = post.Summary,
Title = post.Title,
Category = post.Category,
CreationDate = post.CreationDate,
SelectedCategory = post.CategoryId,
SelectedTag = post.TagId,
Tag = post.Tag,
UrlSlug = post.UrlSlug
};
return blogPostViewModel;
}
what is the problem with this , I got this error View :
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Blog.Domain.Model.Post]', but this dictionary requires a model item of type 'System.Collections.Generic.IList`1[Blog.Web.UI.ViewModels.PostViewModel]'.
then ?? I convert the Ilist of Model to ViewModel via this :
return posts.Select(ConvertToPostViewModel).ToList();
then what is going on ??
what I have done in action
public ActionResult Posts()
{
var blogPost = _blogRepository.GetAllPost();
var blogPostViewModel = blogPost.ConvertToPostViewModelList();
return View("Posts", blogPostViewModel);
}
and in View
#model IList<Blog.Web.UI.ViewModels.PostViewModel>

Two possibilities:
The method you posted is not the one being matched. You can verify this easily by throwing an exception in your controller action and seeing if it gets thrown. This could be the result of either: (a) an overloaded Posts controller action, where the other is being matched; or, (b) a custom route that is intercepting the request.
You originally returned the domain object in testing, but after changing the controller action to set the model to the PostViewModel, you forgot to recompile your MVC project. Try recompiling your solution and see if the results change.

Related

Object as a parameter for an action controller?

Is it possible for an action controller to accept a literal object. For example, I have several views in which I would like to post various models from to a single controller that can then determine the incoming model object for further processing.
Model sample:
public class Model1
{
// properties, etc.
}
public class Model2
{
// properties, etc.
}
public class Model3
{
// properties, etc.
}
controller sample:
[HttpPost]
public ActionResult ProcessModel(Object anyModel)
{
// determine the model
if((anyModel as Model1) != null)
{
var model1 = anyModel as Model1;
// continue with code
}
else if((anyModel as Model2) != null)
{
var model2 = anyModel as Model2;
// continue with code
}
// continue with model check, etc.
}
I've tried, but my controller does not appear to be picking up the model as my object parameter remains empty. Is this possible?
Have a quick read about how model binding works... The model binder (which takes whatever is posted to your Action and turns it into the anyModel parameter uses the type of the parameter to determine what to do.
Since the type is Object it can't do anything.
My guess (depending on what you're trying to achieve) is that you can have several Action overloads each with a different type of Model as the parameter which then call common code.
[HttpPost]
public ActionResult ProcessModel(Model1 anyModel){}
[HttpPost]
public ActionResult ProcessModel(Model2 anyModel){}
[HttpPost]
public ActionResult ProcessModel(Model3 anyModel){}
That said it's a bit odd to have one action which takes lots of different models. There's a good chance you're better off doing something else.
Your question might gather a better answer if you say what you're trying to achieve
The Default Asp.NET ModelBinder cannot bind generic Objects this way. You should take a look here to understand how the model will be build back in the server by the DefaultModelBinder: Understanding ASP.NET MVC Model Binding.
Given that your form has many Models, you should encapsulate them into a ViewModel to do this kind of operation.
The ViewModel should looks like this:
public class MyViewModel
{
public Model1 Model1 {get; set;}
public Model1 Model2 {get; set;}
public Model1 Model3 {get; set;}
}
And the controller:
[HttpPost]
public ActionResult ProcessModel(MyViewModel myViewModel)
{
// determine the model
if(myViewModel.Model1 != null)
{
// continue with code
}
else if(myViewModel.Model2 != null)
{
// continue with code
}
// continue with model check, etc.
}
Recently I faced the same issue and resolved it as below:
Step 1: From javascript pass 2 parameter :
First, pass model name as String for identification which model is coming
Second, Pass data from javascript using JSON.stringify(data). where your data can be from Model1, Model2 , Model3 etc.
Step2: In your controller:
[HttpPost]
public ActionResult ProcessModel(string modelName, string anyModel)
{
switch(modelName) {
case "Model1":
var modelValue= JsonDeserialize<Model1>(anyModel);
// do something
break;
case "Model2":
var modelValue= JsonDeserialize<Model2>(anyModel);
// do something
break;
}
}
You Need One method like below:
public T JsonDeserialize<T>(string jsonModel){
return JsonConvert.DeserializeObject<T>(jsonModel, jsonSettings);
}
JsonConvert need namespace "Newtonsoft.Json".
You also need to declare jsonSettings as below
JsonSerializerSettings jsonSettings= new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
DefaultValueHandling = DefaultValueHandling.Ignore
};
This solution is kind of workaround. There is one more solution. you can check that also:
How can I make a Controller Action take a dynamic parameter?
Hope this helps.

Where should SelectListItem lists go?

In an MVC application, I have some DropDownLists. In my controller I create the IEnumerable<SelectListItem>s and transfer them to my View. Unfortunately, if there is a validation error, I need to recreate those lists, otherwise the view rendering fails.
In the controller action method I'm doing:
var possibilities = _repo.Objects.Select(o=>new SelectListItem(...));
viewmodel.Possibilities = possibilities;
return View(viewmodel);
The view-model has the Possibilities property defined like this:
IEnumerable<SelectListItem> Possibilities { get; set; }
And in my view I access it:
#Html.DropDownListFor(vm=>vm.ThePossibility, vm.Possibilities)
The problem is that when the form post action method is called, the view model passed to it has a null for Possibilities, so when I call:
if(!ModelState.IsValid)
return View(model);
The view doesn't get rendered.
I understand why the propery is null on the post action method, but what's the best way of correcting this? I'd rather not reinitialize those lists.
Thanks.
If you don't want to re-initialize the lists, you will have to cache them somewhere, such as the session or somewhere else.
Frankly, in most cases, it's just simpler to rebuild them. You will have to re-assign them every time.
You should look into using the Post-Redirect-Get pattern; There is a nice pair of attributes described in this blog post that make this very easy to do in ASP.Net MVC.
I usually cache these somewhere or provide a static class for getting common lists. You can then provide access to these in your model with a getter.
For example:
IEnumerable<SelectListItem> _possibilities
IEnumerable<SelectListItem> Possibilities
{
get
{
if (_possibilities == null)
_possibilities = CommonLists.Possibilities();
return possibilities;
}
}
Accessors and JSON (NetwonSoft) are your friends.
In a nutshell
When you set the IEnumerable<SelectListItem> property of your model, serialize it to a public string property.
When your public string property is being set, and the IEnumerable<SelectListItem> is not defined (e.g. null), deserialize the string property to the IEnumerable<SelectListItem> property of your model.
In your view, embed the serialized string so that it is persisted between posts to the server. (Eg. #Html.HiddenFor(...))
Model
public class MyViewModel
{
public IEnumerable<SelectListItem> Selections
{
get
{
return selections;
}
set
{
selections= value;
// serialize SelectListItems to a json string
SerializedSelections = Newtonsoft.Json.JsonConvert.SerializeObject(value);
}
}
IEnumerable<SelectListItem> selections;
public string SerializedSelections
{
get
{
return serializedSelections;
}
set
{
serializedSelections = value;
if(Selections == null)
{
// SelectListItems aren't defined. Deserialize the string to the list
Selections = Newtonsoft.Json.JsonConvert.DeserializeObject<IEnumerable<SelectListItem>>(value);
}
}
}
string serializedSelections;
}
Razor View
<form>
...
#Html.HiddenFor(m => m.SerializedSelections)
...
#Html.DropDownListFor(m => m.SomeProperty, Model.Selections)
</form>

How to pass List in Redirecttoaction

I want to pass more then one parameter from RedirectToAction method
how can I pass?
My One Action Method
[HttpPost, ActionName("SelectQuestion")]
public ActionResult SelectQuestion(string email,List<QuestionClass.Tabelfields> model)
{
List<QuestionClass.Tabelfields> fadd = new List<QuestionClass.Tabelfields>();
for (int i = 0; i < model.Count; i++)
{
if (model[i].SelectedCheckbox == true)
{
List<QuestionClass.Tabelfields> f = new List<QuestionClass.Tabelfields>();
fadd.Add(model[i]);
}
}
return RedirectToAction("Question", new { email = email, model = fadd.ToList() });
}
My another Action Method
[HttpGet]
public ActionResult Question(string email,List<QuestionClass.Tabelfields> model)
{
}
I am not getting values in model.
You cannot pass a collection of complex objects in urls when redirecting.
One possibility would be to use TempData:
TempData["list"] = fadd.ToList();
return RedirectToAction("Question", new { email = email});
and then inside the Question action:
var model = TempData["list"] as List<QuestionClass.Tablefields>;
The way that I solved this problem was to serialize the list to a JSON object using the JsonConvert method from the Newtonsoft.Json nuget package. Then the serialized list can be passed as a parameter and then deserialized again to re-create the original list.
So in your SelectQuestion method you would use this code:
return RedirectToAction("Question",
new {
email = email,
serializedModel = JsonConvert.SerializeObject(fadd.ToList())
});
And in your Question method, you would use this code to deserialize the object.
[HttpGet]
public ActionResult Question(string email, string serializedModel)
{
// Deserialize your model back to a list again here.
List<QuestionClass.Tabelfields> model = JsonConvert.DeserializeObject<List<QuestionClass.Tabelfields>>(serializedModel);
}
Important, this adds the model as a query string parameter to your url, so only do this with really simple small objects, otherwise your url will be too long.
This is probably not even active anymore, but I'll leave how I did it here to maybe help someone else.
I solved this using a simple Redirect instead of a RedirectToAction:
List<int> myList = myListofItems;
var list = HttpUtility.ParseQueryString("");
myList.ForEach(x => list.Add("parameterList", x.ToString()));
return Redirect("/MyPath?" + list);
Then, on your other method:
public ActionResult Action(List<int> parameterList){}
RedirectToAction method Returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action.
You should either keep the data in a temporary storage like TempData / Session . TempData uses Session as the backing storage.
If you want to keep it real Stateless, you should pass an id in the query string and Fetch the List of items in your GET Action. Truly Stateless.
return RedirectToAction("Question", new { email = email,id=model.ID });
and in your GET method
public ActionResult Question(string email,int id)
{
List<QuestionClass.Tabelfields> fadd=repositary.GetTabelFieldsFromID(id);
//Do whatever with this
return View();
}
Assuming repositary.GetTabelFieldsFromID returns a List of TabelFields from the Id

Pass tuple with two object collections to view?

I want to pass two collections of objects. First is Post, second is Gallery. However I get error and I don't know how to fix this.
I've done this when passing two single objects and it is working fine, but now I need to pass two collections of those objects and it gives me error.
Error
The model item passed into the dictionary is of type
'System.Tuple2[System.Linq.IQueryable1[photoBlog.Models.Gallery],System.Linq.IQueryable1[photoBlog.Models.Post]]',
but this dictionary requires a model item of type
'System.Tuple2[System.Collections.Generic.IEnumerable1[photoBlog.Models.Gallery],System.Collections.Generic.IEnumerable1[photoBlog.Models.Post]]'.
Controller
public ActionResult Index()
{
photoBlogModelDataContext _db = new photoBlogModelDataContext();
var posts = _db.Posts.OrderByDescending(x => x.DateTime).Take(4);
var galleries = _db.Galleries.OrderByDescending(x => x.ID).Take(4);
return View(Tuple.Create(galleries, posts));
}
View
#model Tuple<IEnumerable<photoBlog.Models.Gallery>, IEnumerable<photoBlog.Models.Post>>
#foreach (var item in Model.Item1)
{
#item.Name
}
I think you should modify your controller method to this:
public ActionResult Index()
{
photoBlogModelDataContext _db = new photoBlogModelDataContext();
var posts = _db.Posts.OrderByDescending(x => x.DateTime).Take(4).ToArray();
var galleries = _db.Galleries.OrderByDescending(x => x.ID).Take(4).ToArray();
return View(Tuple.Create(galleries, posts));
}
From your error message, it appears that the queries are not resolved yet when your view is rendered. By also doing ToArray or ToList, you will force the query to hit the database before you return from the controller method.
You need to create a new Entity in your domain.Model
Here is an example I provided today :
MVC3: button to send both form (model) values and an extra parameter
Later Edit :
namespace App.Domain.Model
{
public class Tuple
{
public IEnumerable<photoBlog.Models.Gallery> Gallery{ get; set; }
public IEnumerable<photoBlog.Models.Post> Post{ get; set; }
}
}
you will send the Tuple object to your view as follows :
#model: (Domain.Model.)Tuple <--- (your exact model path )
#foreach (var item in Model.Gallery)
{
#item.Name
}
Another posibility would be using viewbag ... but i strongly suggest you do it the correct way,i.e. the MVC way
Just try this
var posts = _db.Posts.OrderByDescending(x => x.DateTime).Take(4).ToList();
var galleries = _db.Galleries.OrderByDescending(x => x.ID).Take(4).ToList();

How do I transfer ViewModel data between POST requests in ASP.NET MVC?

I have a ViewModel like so:
public class ProductEditModel
{
public string Name { get; set; }
public int CategoryId { get; set; }
public SelectList Categories { get; set; }
public ProductEditModel()
{
var categories = Database.GetCategories(); // made-up method
Categories = new SelectList(categories, "Key", "Value");
}
}
Then I have two controller methods that uses this model:
public ActionResult Create()
{
var model = new ProductEditModel();
return View(model);
}
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (ModelState.IsValid)
{
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
return View("Success");
}
else
{
return View(model); // this is where it fails
}
}
The first time the user goes to the Create view, they are presented with a list of categories. However, if they fail validation, the View is sent back to them, except this time the Categories property is null. This is understandable because the ModelBinder does not persist Categories if it wasn't in the POST request. My question is, what's the best way of keeping Categories persisted? I can do something like this:
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (ModelState.IsValid)
{
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
return View("Success");
}
else
{
// manually populate Categories again if validation failed
model.Categories = new SelectList(categories, "Key", "Value");
return View(model); // this is where it fails
}
}
But this is an ugly solution. How else can I persist it? I can't use a hidden field because it's a collection.
I would use the repository to fetch whatever data is needed and don't think it's an ugly solution:
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (!ModelState.IsValid)
{
// manually populate Categories again if validation failed
model.Categories = Repository.GetCategories();
return View(model);
}
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
// I would recommend you to redirect here
return RedirectToAction("Success");
}
To further refactor this I would recommend you watching the excellent Putting Your Controllers on a Diet video presentation by Jimmy Bogard.
I typically implement my lists (for drop downs) as a readonly property. When the View gets the value the property is self contained on what it needs to return the values.
public SelectList Categories
{
get
{
var categories = Database.GetCategories(); // made-up method
return new SelectList(categories, "Key", "Value");
}
}
If necessary you can grab the currently selected item (i.e. validation failed) from the property containing the id that was posted and bound to the instance of your class.
In my case I have a BaseModel class where I keep all those property list as class attributes.
Like in the following sample:
public IEnumerable<SelectListItem> CountryList
{
get
{
return GetCountryList().Select(
t => new SelectListItem { Text = t.Name, Value = Convert.ToString(t.CountryID) });
}
}
GetCountryList() is a function that ask a Singleton for data. This would only happen once in the app lifecycle
Another way for doing this, and if those lists are pretty big, would be to have a static utility class with the lookup table that returns the SelectListItem.
If you need to access a list that change from time to time then simply dont use a Singleton class.

Categories

Resources