I have read this link: https://www.future-processing.pl/blog/view-code-reuse-techniques-in-asp-net-mvc/
I can not use any of those helper ways...
I have to show on multiple mvc sites this string:
1612-1
That is an inquiry number: 16 is the day of month, 12 the month of year and 1 is the database id. I am sure that will not be the final impl but for now we take it as given.
public class MyViewModel
{
public string City { get; set; }
public string PostalCode { get; set; }
public List<string> ActionItemDescriptions { get; set; }
public string InquiryNumber { get; set; }
}
Where would you create the InquiryNumber?
If I put it inside the razor view I cant reuse it.
Seems business logic to me , so it belongs in the business layer.
Then, from within your controller you:
call the business component which returns the inquiry number
store the number in your view model
pass the view model to the view.
One way you could get an inquiry number, without using a helper, is this:
In a controller, have the following action method:
public ActionResult GetInquiryNumber()
{
// TODO : The code to get the inquiry number.
return Content("1612-1");
}
You can then call that method in any view you like, using the following:
#{ Html.RenderAction("GetInquiryNumber", "Home"); }
Obviously you will need to come up with your own method, and controller, names.
This isn't the ideal way of passing data to a view (using a viewmodel is preferable), but the above approach is an option to you.
Related
I have a class which looks like this:
public class ApplicationFormModel
{
protected ApplicationFormModel()
{
CurrentStep = ApplicationSteps.PersonalInfo;
PersonalInfoStep = new PersonalInfo();
}
public PersonalInfo PersonalInfoStep { get; set; }
public IEducationalBackground EducationalBackgroundStep { get; set; }
public IAboutYou AboutYouStep { get; set; }
public IOther OtherStep { get; set; }
}
where IEducationalBackground, IAboutYou, and IOther are interfaces. I do not use this class directly, but I use derived classes of this one which upon instantiation create the proper instances of EducationalBackgroundStep, AboutYouStep, and OtherStep.
In my view, I am using Razor Helpers such as
#Html.TextBoxFor(model => (model.EducationalBackgroundStep as ApplicationFormModels.EducationalBackgroundAA).University, new {#class = "form-control", type = "text", autocomplete = "off"})
The field 'University', for example, is NOT part of the Interface and I therefore need the cast to access it. Everything is fine for properties of the interface itself, but those which I need to cast for do not end up having the correct ID and Name properties.
For example, instead of EducationalBackgroundStep_University as ID, I only get University. This causes the form to not include this value when submitting it.
I did not have this issue before when I used a base class instead of an interface, but then I had to include the EducationalBackgroundStep, AboutYouStep, and OtherStep in each derived class (and have it then of the correct derived type), but that is what I wanted to avoid.
Is there any way around this? Thank you very much!
The issue with the ID generation is because you are using casting (x as y) and the TextBoxFor expression handler can't determine what the original model property was (more to the point, it doesn't make sense to use the original model property as you're not using it any more, you're using the cast property)
Example fiddle: https://dotnetfiddle.net/jQOSZA
public class c1
{
public c2 c2 { get; set; }
}
public class c2
{
public string Name { get; set; }
}
public ActionResult View(string page, bool pre = false)
{
var model = new c1 { c2 = new c2 { Name = "xx" } };
return View(model);
}
View
#model HomeController.c1
#Html.TextBoxFor(x=>Model.c2.Name)
#Html.TextBoxFor(x=>(Model.c2 as HomeController.c2).Name)
The first textboxfor has ID c2_Name while the second has just Name
You have two options:
1) use concrete classes rather than interfaces for your viewmodel
2) don't use TextBoxFor and instead use TextBox and specify the ID manually (but then you'll lose refactoring)
#Html.TextBox("c2_Name", (Model.c2 as HomeController.c2).Name)
This will give you the ID you're expecting, but as #StephenMuecke rightly points out, this might not bind correctly when you do the POST - so you may still be stuck... but at least it answers the question.
#freedomn-m explained to me why my code wouldn't work and he put me on the right track to find a solution, so he gets the accepted answer.
The workaround I used is the following - so I now have the following classes:
public class ApplicationFormViewModel {
public PersonalInfo PersonalInfoStep { get; set; }
// constructors which take the other classes and
// initialize these fields in an appropriate manner
public IEducationalBackground EducationalBackgroundStep { get; set; }
public IAboutYou AboutYouStep { get; set; }
public IOther OtherStep { get; set; }
}
// in our case, XX can be one of 3 values, so we have 3 classes
public class ApplicationFormXX {
public PersonalInfo PersonalInfoStep { get; set; }
// constructor which take the ApplicationFormViewModel and
// initialize these fields in an appropriate manner
public EducationalBackgroundXX EducationalBackgroundStep { get; set; }
public AboutYouXX AboutYouStep { get; set; }
public OtherXX OtherStep { get; set; }
}
To the main View I send the ApplicationFormViewModel and for each of the fields, I call a separate Partial View.
The Partial views render the common fields which are present in the Interfaces and then, depending on the type of the object held by the interface, it calls a different partial view which accepts the correct Model.
Example:
In the main View I have (NOTE: The actions return a partial view):
#model Applications.Models.ApplicationFormModels.ApplicationFormViewModel
// CODE, CODE, CODE
#Html.Action("RenderEducationalBackgroundStep", "ApplicationFormsLogic", routeValues: new {model = Model})
In the Partial View of for the EducationalBackgroundStep, I have:
#model ApplicationFormModels.ApplicationFormViewModel
// CODE, CODE, CODE
#{
var educationalBackgroundType = Model.EducationalBackgroundStep.GetType();
if (educationalBackgroundType == typeof(EducationalBackgroundXX))
{
<text>#Html.Partial("~\\Views\\Partials\\ApplicationForm\\Partials\\ApplicationSteps\\EducationalBackground\\_EducationalBackgroundXX.cshtml", new ApplicationFormModels.ApplicationFormModelXX { EducationalBackgroundStep = Model.EducationalBackgroundStep as EducationalBackgroundXX })</text>
}
// OTHER ELSE IF CASES
}
And then, the _EducationalBackgroundXX.cshtml partial view expects a model like this:
#model ApplicationFormModels.ApplicationFormModelXX
This way, no casting is required and everything works fine with the ModelBinder. Again, thank you #freedomn-m for setting me on the right track.
NOTE: In practice I need more fields than the ones presented here (for navigation and some custom logic), so actually all of these classes inherit an abstract base class (this makes it redundant to have the PersonalInfoStep declared in each of the classes, for example, because it can be inherited from the abstract base class). But for the intents and purposes of this method, what's present here suffices.
I have one view in my ASP.net MVC 2.0 project, I want to list the list of employee that I create method GetProfileCustomer() in CustomerModels and GetTransaction() in TransactionModels.
How can I import two different of models in a single view?
I'm fairly new to MVC as well, and I've struggled with the similar issues if I understand the question correctly.
I've found that you get much cleaner controller code if you design your ViewModels to be as close as possible to the data that the view is using. Your ViewModels can contain lists of other things including other model objects. Something like:
public class TransactionViewModel
{
public string dataelement1 { get; set; }
public int dataelement2 { get; set; }
//and so on...
//The Lists
public IList<Employee> EmpList { get; set; }
public IList<OtherModel> SomethingElse { get; set; }
//and so on...
}
In your controller, you construct and initialize your ViewModel
something like...
TransactionViewModel TVM = new TransactionViewModel();
//assign basic attributes here..
//make a list
TVM.Emplist = (from blah in context select blah).ToList();
//send it to the view
return View(TVM);
Hope this helps and happy to hear any feedback...
I've just finished my first ASP.NET MVC (2) CMS. Next step is to build website that will show data from CMS's database. This is website design:
http://img56.imageshack.us/img56/4676/portal.gif http://img56.imageshack.us/img56/4676/portal.gif
#1 (Red box) - displays article categories. ViewModel:
public class CategoriesDisplay
{
public CategoriesDisplay() { }
public int CategoryID { set; get; }
public string CategoryTitle { set; get; }
}
#2 (Brown box) - displays last x articles; skips those from green box #3. Viewmodel:
public class ArticleDisplay
{
public ArticleDisplay() { }
public int CategoryID { set; get; }
public string CategoryTitle { set; get; }
public int ArticleID { set; get; }
public string ArticleTitle { set; get; }
public string URLArticleTitle { set; get; }
public DateTime ArticleDate;
public string ArticleContent { set; get; }
}
#3 (green box) - Displays last x articles. Uses the same ViewModel as brown box #2
#4 (blue box) - Displays list of upcoming events. Uses dataContext.Model.Event as ViewModel
Boxes #1, #2 and #4 will repeat all over the site and they are part of Master Page. So, my question is: what is the best way to transfer this data from Model to Controller and finally to View pages?
Should I make a controller for
master page and ViewModel class that will wrap all this classes together OR
Should I create partial Views for
every of these boxes and make each
of them inherit appropriate class
(if it is even possible that it
works this way?) OR
Should I put this repeated code in
all controllers and all additional
data transfer via ViewData, which
would be probably the worse way :) OR
There is maybe a better and more
simple way but I don't know/see it?
Thanks in advance,
Ile
EDIT:
If your answer is #1, then please explain how to make a controller for master page!
EDIT 2:
In this tutorial is described how to pass data to master page using abstract class: http://www.asp.net/LEARN/mvc/tutorial-13-cs.aspx
In "Listing 5 – Controllers\MoviesController.cs", data is retrieved directly from database using LINQ, not from repository. So, I wonder if this is just in this tutorial, or there is some trick here and repository can't/shouldn't be used?
To get data to my Master Page:
I don't like using an abstract class to get data to the master page. I prefer composition over inheritance.
I don't like to use the ViewData dictionary because it's not strongly typed.
I would create Views, ViewModels and Actions for each section. Then call Html.RenderAction(...) For example:
I would create CategoriesDisplay.aspx with only the html for the redbox. I would pass that your CategoriesDisplay model. Then in my controller:
public class CategoryController : Controller
{
public ActionResult DisplayCategories()
{
var model = new CategoriesDisplay();
...
return View(model);
}
}
Then in my Master Page:
<% Html.RenderAction<CategoryController>(c => c.DisplayCategoreis()); %>
This will render the CategoriesDisplay view inline within the Master Page. Which in turn allows you have SOC (Seperation of Concerns), clean and manageable code.
I fought with this as well. Initially I did a lot of dumping of extra data into the ViewData, which ended up having to be casted back (made some extensions that eased this, but still not great).
I would go with your choice #1 and make a ViewModel that wraps all of the classes you would need.
My repository returns a list of Accounts.
Each account has a date and a MoneySpent decimal amount. So, I have my list of Accounts and in my controller I'm trying to process this list a little.
I want to have an object which contains the string name of all the months in my Account list and a Sum of all money spent for that month.
Here is what I have tried:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Detail(int id)
{
var recentAccounts = accountRepository.GetAccountsSince(DateTime.Now.AddMonths(-6));
var monthlyTotals = from a in recentAccounts
group a by a.DateAssigned.Month.ToString("MMM") into m
select new
{
Month = m.Key,
MonthSum = m.Sum(a => a.MoneySpent)
};
return View();
}
Does this seem like the right way to calculate monthlyTotals?
Also, I've been using strongly typed views with ViewModels for each view, so what type should I make monthlyTotals so I can add it as a field on my ViewModel and pass it to my View?
Looks right to me.
When I need to pass data like this to my view, I create a class for it. So your class would look like this:
public class MonthlyTotal
{
public string Month { get; set; }
public decimal MonthSum { get; set; }
}
and your SELECT clause would look like this:
select new MonthlyTotal
{
Month = m.Key,
MonthSum= m.Sum(a => a.AmountAssigned)
}
I would probably break out that logic into a service layer class holding business logic. Along with that, if the view is expecting a structure different than the model, you would transform your results in your service method returning a custom model type.
Using an anonymous type won't work since the view code won't know what properties it has. I suggest creating a view-only model in the Models directory.
public class MonthlySumModel
{
public string Month { get; set; }
public decimal Sum { get; set; }
}
Then create a new model value in the select statement:
select new MonthlySumModel
{
Month = m.Key,
Sum = m.Sum(a => a.MoneySpent)
};
You can then use this model as the type for the view.
Does it make sense create an object that contains only those properties that the user will input on the webpage, use that for binding in the controller, and then map to the full Entity Object? Or should you just use the entity object, and use Include and Exclude to make restrictions on what gets bound on input?
I have come to like the idea of using interfaces to segregate which properties should be included when the object is updated.
For example:
To create and update an person object:
interface ICreatePerson
{
string Name { get; set; }
string Sex { get; set; }
int Age { get; set; }
}
interface IUpdatePerson
{
string Name { get; set; }
}
class Person : ICreatePerson, IUpdatePerson
{
public int Id { get; }
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
}
Then, when binding model, just use the appropriate interface as the type and it will only update the name property.
Here is an example controller method:
public ActionResult Edit(int id, FormCollection collection)
{
// Get orig person from db
var person = this.personService.Get(id);
try
{
// Update person from web form
UpdateModel<IUpdatePerson>(person);
// Save person to db
this.personService.Update(person);
return RedirectToAction("Index");
}
catch
{
ModelState.AddModelErrors((person.GetRuleViolations());
return View(person);
}
}
See this article (and the comments) for a very good discussion of the options.
I recommend using a separate presentation model type in most cases. Aside from the issue of binding (which is important, but there are other ways around this issue), I think that there are other reasons why using presentation model types is a good idea:
Presentation Models allow "view-first" development. Create a view and a presentation model at the same time. Get your user representative to give you feedback on the view. Iterate until you're both happy. Finally, solve the problem of mapping this back to the "real" model.
Presentation Models remove dependencies that the "real" model might have, allowing easier unit testing of controllers.
Presentation Models will have the same "shape" as the view itself. So you don't have to write code in the view to deal with navigating into "detail objects" and the like.
Some models cannot be used in an action result. For example, an object graph which contains cycles cannot be serialized to JSON.