I've written a little MVC3 site that let's certain users review Active Directory Accounts.
For audits we are required to keep track off our 'user reviews'. So, once a month I put everything in an SQL database. With the state 'to be checked'.
This looks like this:
I'd like to have a way that people can quickly approve them by just checking a textbox and saving it.
How would I go about this?
The 'ReviewState' is a separate object (StateID, StateText, Description, Active).
Possibilities are: Unchecked, Approved, Denied, Silently Approved, ...
Create a ReviewState model and a strongly typed partial view for it containing StateId, StateText, Description etc).
Your parent model should contain a list of ReviewStateModel's. In the main view, loop through that and render a partial for each model in the list.
You may need to add an Id so that you can identify each review model on the server side.
Create a ViewModel to be used in your view:
public class AccountViewModel
{
public AccountInfo Account { get; set; }
public ReviewState Review { get; set; }
}
This way you can add a checkbox for Approve like this:
#Html.CheckboxFor(x => x.Active);
You will get this model back to your post action. The only thing left is extract data and update database.
I might not be accurate with property names here and code is written from my head but I think you get the point
Related
I have been looking at C# MVC for a while now and really getting in to it. I have come across how to pass 2 or more Domain Models in to a View but what i need to ask is security. I have read about Over Posting and that is can be stopped with Binding inclusions and exclusions but how does this effect a ViewModel with 2 or more Model inside it. Is there still the possibility of hidden fields in the HTML that resolve to the Domain Models and are they editable.
I have been looking to make ViewModels where i give it certain things i want the user to edit but it just seems over complicated, so is there a better and easier way of doing this.
public TicketViewModel {
private Ticket _ticket;
public TicketViewModel(Ticket Ticket)
{
_ticket = Ticket;
}
public string Title
{
get
{
return _ticket.Title;
}
}
public List<Comment> Comments
{
get
{
return _ticket.Comments;
}
}
}
As you can see I create a new ViewModel with the Ticket Domain Model but the main concern that i have is returning a List of Domain Model Comments as i only want them to be read only.
Any help would be greatly appreciated.
For security:
If i get what you are asking about then
First of all the user can edit everything on the client side and we cannot do anything for this.
But for your concern if you have not added code on server side than no one will be able to change your data using your application , also you can add validation on server side.
For adding List in a ViewModel:
public class MainViewModel
{
public string Title{get;set;}
public List<Comment> ListComments {get;}
}
"i have is returning a List of Domain Model Comments as i only want them to be read only"
you can use this,
public List<Comment> Comments { get; }
Consider the ReadOnlyCollection, and the method AsReadOnly provided by List.
Your comment property can be rewritten as -
public List<Comment> Comments
{
get
{
return _ticket.Comments.AsReadOnly();
}
}
If you don't want something to be editable, just don't modify it.. I think this is unrelated to MVC.. You are in control of server code, so if you want comments to be readonly, just don't modify them in your server code. All these "models" exist as long as your HTML is being rendered on the server.
Client by itself won't be able to modify your database, only to modify it's HTML or send requests to your server.. It's your server code that actually modifies things.
Looking at this, and not knowing the context it is in, I would suggest using the domain model itself in your view and not wrapping it in the TicketViewModel. The ViewModel doesn't seem to add anything.
For the read only comments: I don't think it matters whether the collection in the ViewModel is editable. What matters is that you don't render editable controls in your view. And most important that the code in your Save (or whatever it is called) action ignores whatever comments are posted.
I have an MVC 5 project and part of that is a registration. Presently the form submits using the standard form submit action and my model binder does its job gloriously and all is good.
However, I now need to add a dynamic element to my form such that a user needs to be able to add on the fly invitations to add team members via a textbox for an email address and a dropdown list for that team members role... and an add another button to add another textbox and dropdown list for the next team member... and the next... ad infinitum...
Upon form submission, my model binder is going to fall over and not know what to do with these dynamic elements, so I need suggestions for how to get my model binder to deserialize these items into an array of email & role (role binds to an enum).
I've had it suggested that client side script can catch the submission and serialize the items to a text string and put that in a hidden form field that I can then deserialize manually on my controller action, which seems like it'll work okay, but it feels dirty.
What is the most straightforward way of achieving this? Is there anything built into .NET that will automatically handle this somehow if I configure my view correctly?
Your model can store your "dynamic" fields in a collection like:
public List<string> Emails { get; set; }
In your view you just create subsequent indexed names (exact naming depends on how you're generating this new fields):
model.Emails[0] // name="Emails[0]"
model.Emails[1] // name="Emails[1]"
In your controller action, there should be nothing special the model binder has to do. It will easily be able to handle the collections.
There is an old article from Phil Haack about binding to a List:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
I guess you could add a ICollection<Invitation>-Property to your model and set the Name of the form fields as explained in the article.
Although personally I would bind the data to a JavaScript-ViewModel and post JSON back to the Server using Ajax.
Upon clicking the "Add more" button, simply inject the textbox and dropdown and let the form serialize like usual.
On the server, accept a list of invitations:
public class Invitation
{
public string EmailAddresss {get; set;}
public int InvitationType {get; set; }
}
public ActionResult Register(MyCurrentModel dto, Invitation[] invitations)
{
//..
}
I'm creating a system using ASP.NET MVC for filling out online job applications. It's really just an experiment to familiarize myself with the MVC framework. I'm definitely not a security expert so I just wanted to get some advice here since this web app will be dealing with sensitive information.
The system has a wizard-like set of views. You go from one view to the next filling out the relevant information until the end when you finally submit it.
Each job application is stored in the database with a GUID primary key. Right now I am simply storing that GUID in a hidden field in the view of each page. This makes it easy to update the model in the controller like so:
[HttpPost]
public ActionResult ExampleAction(FormCollection formValues)
{
Guid appId = new Guid(Request.Form["ModelId"]); // the GUID stored in the hidden field
ExampleModel example = db.Entities.Single(e => e.ModelId == appId);
UpdateModel(example, formValues);
db.SaveChanges();
return RedirectToAction("ExampleAction2", new { appId = appId.ToString() });
}
I know this is not secure because anyone with any kind of development experience knows you can edit the values of hidden fields in any browser's developer tools. So what's the recommended way to securely get the same results?
You should store it in the Session object. That way, you can call it at anytime from anywhere and it will never display on any of your views.
The doc: http://msdn.microsoft.com/en-us/library/ms178581.aspx
I know this has been answered, just for interest sake I want to leave another option. How you could go about it is parsing the information you get from the first view as a JSON string that you can store in a cookie and then later serialize it when you need it.
This is asuming that the information is safe to store client side. Otherwise it could be an encrypted cookie I suppose. This would address the concerns for cloud computing and load balancers etc.
Why not post to each step instead of using RedirectToAction? That way you can create a model that contains each step.. for example:
class JobApplicationViewModel
{
public Guid ApplicationGuid { get; set; }
public StepOne Step1 { get; set; }
public StepTwo Step2 { get; set; }
public int CurrentStep { get; set; }
}
The step 1 view can post to step 2.. for example:
[HttpPost]
public ViewResult StepTwo(JobApplicationViewModel viewModel)
{
ServiceLayer.Update(viewModel);
return View(viewModel);
}
Then, the StepTwo view posts to StepThree, StepThree posts to Step4, etc. This makes sense logically, as you're only allowing people to get to any step beyond step 1 by posting the form.
I am doing big project in Web Forms and entered a big mess of code(can be solved with little refactoring), so I decided to take a peek at mvc, seems like everyone favores it over web forms.
I have dll file with all LINQ to SQL classes and methods for existing sql db and I started teaching myself by reacreating this project in mvc.I first recreated my homepage from webforms to razor, so far so good and I recreated one of the user controls to partial view and looped it.
Partial view is strongly typed with one of L2S class, the thing is there is some raw data in it.Like for example My model for partial view is my L2S class PostDetails: it consist od some data ready from output like: Title, Permalink, ViewsNumber etc. but it also cointains some raw data like UserId(I need to call method from dll to get username),DateTimeCreated(on which I also need to call method to get time difference), In webforms I would do this in codebehind but I'm not sure where to do it in mvc, maybe in controller and pass it in ViewData.I am also asking this for future, not just this case.
You should perform those actions in the controller. The controller is exactly what it sounds like, it controls the data flow between the model and the view.
Here is an example using your PostDetails:
PostDetailsModel
String Title {get;set;}
String Permalink {get;set;}
Int ViewNumber {get;set}
Int UserId {get;set}
DateTime DateTimeCreated {get;set;}
GetDetailsView: this will be requested by your user, and will be a visual representation of the PostDetailsModel (however you want to format that). When this View is requested, a call is made to your controller....
PostDetailsController
//This method will (by default) come from calling [BASEURL]/PostDetails/GetDetails
public ActionResult GetDetails()
{
var details = new PostDetailsModel();
details.UserId = GetUserId();
details.ViewNumber = GetViewNumber();
....
//By default this looks for a view in the PostDetails folder
//by the name of your method (GetDetails)
return View(details);
}
Notice how the controller is the router between the model and the view, basically. A note, however, it would be better to fill your model from methods contained within some sort of business layer (however you implement that). Something like var details = BL.GetDetails();
Also, when the user makes requests to save data, then you can do that with another method that takes the data (whether it be the PostDetailsModel or an int or...) and does whatever it needs to do, then it can redirect back to the display action (or wherever you need it to go)
There is a wealth of information on MVC with a simple google search. Here is Microsoft's overview, but the wikipedia article is very succinct if you just want the basics
In MVC, All your requests will be handled by an action method in a controller. and then controller returns a view. So you can get the data in your controller (or a different layer which will be called from the controller) and pass that data to your view.
Strongly typed views are a clean way of doing things. Create a ViewModel for your scenario. Mostly ViewModels looks similar to the Entities. Ex : For Displaying the details about a customer, i will create a viewModel called "CustomerViewModel"
public class CustomerViewModel
{
public string CustomerId { set;get;}
public string FirstName { set;get;}
}
Then in my CustomerController, I will have a get method for the customer
public class CustomersController : Controller
{
public ActionResult Details(int id)
{
CustomerViewModel objCustomer=new CustomerViewModel;
objCustomer.FirstName="Samuel";
//Instead of hardcoding the values here , you can get it
// from the database using the id and assign to the properties
return View(objCustomer);
}
}
And you will have a view called "Details.chtml" in your Views\Customer
folder which is strongly typed to your CustomerViewModel
#model CustomerViewModel
<div>
<h2>#Model.FirstName</h2>
<p>#Model.CustomerId</h2>
</div>
This can be accessed like http://yourdomain.com/Customer/Details/25
Personally i prefer to keep my controller actions thin. so i write the GetFromDatabase code in a seperate service layer and i just call that method from my action method
I think that you'll find this article very useful:
MVC Overview
It explains in detail, as to what each component should be used for:
Models. Model objects are the parts of the application that implement
the logic for the application s data domain. Often, model objects
retrieve and store model state in a database. For example, a Product
object might retrieve information from a database, operate on it, and
then write updated information back to a Products table in SQL Server.
Views. Views are the components that display the application s user
interface (UI). Typically, this UI is created from the model data. An
example would be an edit view of a Products table that displays text
boxes, drop-down lists, and check boxes based on the current state of
a Products object.
Controllers. Controllers are the components that handle user
interaction, work with the model, and ultimately select a view to
render that displays UI. In an MVC application, the view only displays
information; the controller handles and responds to user input and
interaction. For example, the controller handles query-string values,
and passes these values to the model, which in turn queries the
database by using the values.
I think I'm missing some fundamentals on how MVC forms work. I have a search form on my home page that has five or six different fields a user can search on. So I have this POSTing to my results action just fine. The Result action looks like this:
[HttpPost]
public ActionResult Results(SearchModel model)
{
ResultsModel results = new ResultsModel();
results.ResultList = SearchManager.Search(model).ToList();
return View("Results", results);
}
I've simplified the above method for this post, but the idea is the same. So this all works fine. My results page shows up with the list of results and my user is at the following URL:
http://www.site.com/results
So...now I want to do something fairly common. I have two dropdown lists on the results page. "Sort by" and "# of results per page". How do I do that and send the full set of model data back to the controller so I can query with the new parameters? In reality, the SearchModel class has about 60 different fields. Potentially all of that data could be contained in the model. How do you persist that to a page "post back"?
This same question has me a little stumped about how to do paging as well. My paging links would go to a URL like:
http://www.site.com/results/2
But that assumes that we're responding to a GET request (I don't want 60 fields of data in the querystring) and that the model data is passed between GET requests, which I know isn't the case.
As I said, I think I'm missing some fundamentals about working with MVC 3, models and form posts.
Can anyone help point me in the right direction here? I'll be happy to edit/update this post as needed to clarify things.
EDIT: I also wanted to point out, I'd like to avoid storing the view model in a Session variable. This site will eventually end up being load balanced in a web farm and I'm really trying to avoid using Session if possible. However, if it's the only alternative, I'll configure another session state provider, but I'd prefer not to.
You can add your current SearchModel parameters to the route values for your form. Several versions of BeginForm allow you to pass in an object/RouteValuesDictionary.
#Html.BeginForm("Action", "Controller", new { SearchModel = Model }, FormMethod.Post)
This should pass-through your current SearchModel values so you can re-use them to get the next page. You need to have a controller action defined that will accept any current-page form values as well as the SearchModel.
I have not done this with form posts, but from what I have done and from what the docs say, this is where I would start. Of course, this also means that each of your page number "links" on the page will need to be doing posts. That is really inconvenient for users if they want to be able to use the Back button in the browser.
In this context, you can try to define a route that allows the page number to appear as a part of the URL -- "Action/Controller/{page}". However, I am not sure how that will work given that the form is doing a post.
Response to Comment:
Yeah, you can use Route Values to add the SearchModel to each page link, but as I said in the comment above, since the links will do a "get," your users will see the SearchModel serialized as a part of the link.
Either way, using Route Values is your answer to getting back your original SearchModel without using hidden fields, Session, or TempData.
Your SearchModel class needs to contain your search criteria and your results. Something like below. If you use a PagedList for your results then it will contain the current page, total pages, total items, etc. You can limit the amount of information in your page by only writing the search criteria that contain values.
public class SearchModel
{
public string Product { get; set; }
public string Sku { get; set; }
public string Size { get; set; }
public string Manufacturer { get; set; }
// etc...
public PagedList ResultsList { get; set; }
}
[HttpPost]
public ActionResult Results(SearchModel model)
{
model.ResultList = SearchManager.Search(model).ToList();
return View(model);
}
One of the options I'm coming up with here is to implement a distributed caching system that supports acting as a custom session provider (i.e. Memcached or Windows Server AppFabric), thereby allowing me to use TempData (and Session) in a load balanced environment like so:
[HttpPost]
public ActionResult Results(SearchModel model)
{
ResultsModel results = new ResultsModel();
results.ResultList = SearchManager.Search(model).ToList();
TempData["SearchModel"] = model;
return View("Results", results);
}
[HttpGet]
public ActionResult Results(int? page)
{
SearchModel model = (SearchModel)TempData["SearchModel"];
ResultsModel results = new ResultsModel();
results.ResultList = SearchManager.Search(model).ToList();
TempData["SearchModel"] = model;
return View("Results", results);
}
Any thoughts on this approach? Seems like a lot to have to go through just to get search parameters passed between requests. Or maybe I was just spoiled with this all happening behind the scenes with WebForms. :)
This seems to be another interesting option for Webforms spoiled guy ;) Persisting model state in ASP.NET MVC using Serialize HTMLHelper
Some kind of ViewState incarnation. It is part of MVC Futures . Not sure how long it is in Futures project and why it cannot get into main lib.