I find myself creating Add/Edit/Delete/List GUI's so often that I'm sick and tired of it.
There must be some free package that solves this, right?
What I would like is something like this:
{
MyApplicationUser user = MyApplication.GetUserByID(1234);
EditForm form = new EditForm("Title: Edit User"); //this is the magic object
form.addFieldsFromObject(user);
}
function onFormSubmit(eventArgs e){
MyApplicationUser user = form.GetSubmittedData();
MyApplication.SaveUser(user);
}
AddFieldsFromObject would automatically create a html form with fields mathing the datatype of the public properties of the object I feed it with.
There are a number of frameworks that try to solve this problem. ASP.NET Dynamic Data may be a good place to start. It uses a template-based system to provide basic CRUD (Create, Retrieve, Update, Delete) user interfaces with very minimal custom code.
ASP.NET MVC also does a pretty good job with its editor models:
// View code
#using(Html.BeginForm(...)) {
#Html.EditorForModel()
}
// Action code
public ActionResult ShowForm(int userId)
{
var model = // get model from user ID;
return View(model);
}
public ActionResult SaveForm(Model model)
{
if(ModelState.IsValid)
{
// Save model
}
}
LightSwitch tries to solve this same problem by auto-generating basic scaffolding code for you to produce an experience similar to Microsoft Access. But since it's using actual C# code, you can alter the code to provide more functionality if you find that your needs have grown beyond the original scope of the project.
Related
Hi I cant find the way to pass data between Action methods within one controller. The simplified code example is as follows:
public class HomeController : Controller
{
int latestID;
// GET: Home
public ActionResult Index()
{
latestID = 50000;
return View();
}
// GET: Generate
public ActionResult Generate()
{
// using the latestID here
return View();
}
}
Index loads when the app starts and latestID is set to 50000. But when I click button mapped to API GET request Generate, the latestID is suddenly null. I tried to implement TempData and Session but with no luck. Any help would be greatly appreciated!
I feel there are 2 possibilities here i.e. the value to be retrieved is for a user session or its global across all the users using the website.
If the value is user specific then it can be managed through session stage management like Viewbag. Or if this value is expected to be maintained on the server side alone then it needs to be retrieved again through some persistence mechanism like database or memory cache.
If the value is common across all the users using the Website then it can be achieved through the Dependency Injection of a singleton object (this can be a database manager or cache manager again or a simple in memory object). The static objects can be used as well but it wouldn't be ideal as the application wouldn't support horizontal scalability across instances.
I'm working on a project in which some users can be in the role AdminReader. Those users can see everything, but will not be able to save/edit any data.
I know I can do it this way:
public JsonResult ChangeStatus(int? id)
{
// AdminReader validation
if (base.User.isAdminReader)
{
return Json(new
{
Message = "You don't have privileges to alter data.",
Success = false,
}, JsonRequestBehavior.AllowGet);
}
// Function code
But I don't want to insert the above code inside all project functions.
I thought I could decorate my methods like we use [HttpGet]. I've also read this SO post.
Then I dropped the idea.
But then I found about Exception Handler Attribute and a logging action filter.
Is it possible to somehow combine the public void OnActionExecuting(ActionExecutingContext filterContext) with my AdminReader validation?
I don't know if it is the right way to go about my problem. Also, I'm not sure it could work really. What's the best practice in this situation?
Any suggestion is welcome, thanks in advance.
There are many ways to do this.
Yes, it's true that attributes are just metadata. However, the MVC framework has code in it that recognizes certain metadata and performs actions on it. Examples include the two attributes you mentioned (ActionFilters and ExceptionFilters), there's also AuthorizationFilters, which may be what you actually want.
AuthorizationFilters run before ActionFilters, near the start of the MVC pipeline, which allows them to block access before the page actually renders. But, if you don't need that, you can just use this point to do specific things before the page renders.
However, having said that, you are still going to need to have code on each page that controls what the user can and can't do based on their role. There is no magic way around that. Whenever you want to control what a user can do on a page based on access, you need code that does that in each section where control is required.
It's not clear from your example what you are trying to do, since the return value from a page is typically the HTML to render, but it looks like you want to return some kind of status message. I don't see how that can be replicated to all pages, since the pages themselves need to render.
I'm not entirely sure I understood your question, so sorry if this is off: but if you wanted to perform your AdminReader logic, you could write your own custom attribute like below:
public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
// Perform your unauthorized action here.
}
}
}
And then throw the attribute on any method where it applies (or you could throw it on the entire Controller class, if it applied to everything). Like so:
// The RoleSettings is a class of constants I defined that just contain strings
[AccessDeniedAuthorize(Roles = RoleSettings.AdminRole]
[HttpPost]
public ActionResult MyEditMethod()
{
// Perform actions if they are in the AdminRole
// If not authorized, it will do whatever you defined above in the
// AccessDeniedAuthorizeAttribute
}
For reasons I really don't want to go into (it makes me sad), I am trying to generate dynamic javascript in a MVC5 partial view. I will be using model data to create a bunch custom javascript functions. This is to support legacy apps on another platform, so I really don't have many options here for rearchitecting this.
How can I change the media type of the partial view to application/javascript? Do I need to write my own JavaScriptPartialView class? I would have expected the media type to be a property of the PartialView class, but it doesn't seem to be.
[Route("~/api/legacy/{id:int}.js?lang={lang?}")]
protected async Task<PartialViewResult> GetLegacyScript(int id, string lang)
{
var model = await _legacyService.GetModel(id.ToString(), lang);
var pvw = PartialView(model);
// ??? do what to make this application/javascript?
}
I'm trying to understand the MVC pattern and I get the general idea that the Model is responsible for maintaining the state, the View is responsible for displaying the Model and the Controller is responsible for modifying the Model and calling the appropriate View(s). I wanted to try and implement a simple ASP.NET MVC login page that uses OpenID in order to get some understanding of how it all works.
I've downloaded DotNetOpenAuth-3.4.6 and I was looking through the samples, specifically their MVC sample. Unfortunately, the sample doesn't actually have a model, only a controller:
namespace OpenIdRelyingPartyMvc.Controllers
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
public class UserController : Controller
{
private static OpenIdRelyingParty openid = new OpenIdRelyingParty();
public ActionResult Index()
{
if (!User.Identity.IsAuthenticated)
{
Response.Redirect("~/User/Login?ReturnUrl=Index");
}
return View("Index");
}
public ActionResult Logout()
{
FormsAuthentication.SignOut();
return Redirect("~/Home");
}
public ActionResult Login()
{
// Stage 1: display login form to user
return View("Login");
}
[ValidateInput(false)]
public ActionResult Authenticate(string returnUrl)
{
var response = openid.GetResponse();
if (response == null)
{
// Stage 2: user submitting Identifier
Identifier id;
if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
{
try
{
return openid.CreateRequest(Request.Form["openid_identifier"]).RedirectingResponse.AsActionResult();
}
catch (ProtocolException ex)
{
ViewData["Message"] = ex.Message;
return View("Login");
}
}
else
{
ViewData["Message"] = "Invalid identifier";
return View("Login");
}
}
else
{
// Stage 3: OpenID Provider sending assertion response
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false);
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
case AuthenticationStatus.Canceled:
ViewData["Message"] = "Canceled at provider";
return View("Login");
case AuthenticationStatus.Failed:
ViewData["Message"] = response.Exception.Message;
return View("Login");
}
}
return new EmptyResult();
}
}
}
Perhaps the sample is too simple to actually involve a model, since all of the state information is contained within the Cookie. Subsequently, I've implemented a simple database which has a single Users table:
Users
+ user_id
+ open_id
+ last_login
I presume that I would need a LoginModel:
public class LogInModel
{
[Required]
[DisplayName("OpenID")]
public string OpenID { get; set; }
}
A DisplayModel:
public class DisplayModel
{
[DisplayName("User ID")]
public string UserID{ get; set; }
[DisplayName("OpenID")]
public string OpenID { get; set; }
[DisplayName("Last Login")]
public DateTime LastLogin{ get; set; }
}
Additionally, I may need a ModifyModel, but the DisplayModel can be reused and possibly renamed to UserModel (to properly reflect the use of the model).
So now I have several questions:
Is the controller responsible for verifying user input or is that done when the input is passed to the model (i.e. calling Identifier.TryParse on the openid_identifier)?
I want to allow the user login, change their information (i.e. the user_id) and view their information (i.e. user_id, open_id and last_login). How many models do I need (ModifyModel, DisplayModel and LogInModel)?
What is the lifetime of a model? Does the model exist somewhere statically or is it just created by the controller and passed to the view?
Would it be better to add the database as a model instead of making the above models?
1) It could be yes, but a better approach would be to use Data Annotations on the ViewModel.
2) One model will do. A model should represent an overall object, in this case a "User". If the information required for each View differs greatly, then seperate them out into View Models.
3) Not sure what you mean. MVC (and ASP.NET in general) is based on the HTTP protocol, and is thus stateless. So when you hit a URL, a Controller is assigned, then objects are newed up as the code requires it - this includes a database connection. Then when the request is finished, everything is gone (managed resources anyway). Try not to get confused with the word "model". It's not a physical entity, rather an area of your programming model.
4) Generally, your "model" is your Repository/DAL/ORM, which wraps your underlying database, and represents your domain model. Your View's shouldn't be concerned with the domain. That is the job of your Controller/Model. Your View should work with what it needs, and no more. This is why as a rule of thumb, never bind directly to an ORM model, use a ViewModel.
Edit - in response to questions in comments:
I'm sorry... for 1, Data Annotations suggest the validation is performed on the model, but OpenId.Identifier also provides some functions that can validate input (e.g. TryParse), so would it be more consistent to do all of the validation on the model or is the "place" of validation usually not that strict?
Put the Data Annotations on the ViewModel which are representations of the Model created to make life easier for the View. Data Annotations should not be put on your actual model entities (Entity Framework, L2SQL, etc). Data Annotations should be used for input validation (password comparing, length of characters, phone numbers, email addresses, etc). Business Validation should be done in the domain. I would say that OpenId is a service and not part of the domain. If they have some validation functions, you could wrap those calls in custom data annotations and place them on your ViewModel. That would be a clean and consistent approach.
For 3, I agree that the HTTP protocol is stateless, but if I understand correctly cookies are one way to maintain a state and the model provides an alternate way.
Your right, cookies are one way to maintain state. A common use for this is the Forms Authentication ticket. Another one is session. TempData uses Session (in a smart way - automatic ejection). The model is not an "alternate way" - it does not maintain state. Will talk more about that below.
In application programming a state (i.e. an object) is usually persistent throughout the lifetime of the application, so if I were to create a Model in a console app it would exist for as long as there is a reference to it. So when you say that the "objects are newed," does that mean that the Model object is already instantiated (like in a console app) and I just "update" it or does that mean that I construct a new Model?
In a console app - your right. As long as the console application is running, the objects are alive. But in a ASP.NET MVC Web Application, the "parent" is a ASP.NET Worker Thread, assigned when a request comes in. All of the required objects (controller, database connection, repository, domain objects) are "children" of this thread, if that makes sense. Once that thread is gone, so is all the related objects.
There is no "magic instantiation" of the model, again - the Model is an overall view/representation of your domain, which usually consists of the domain model (entities, business logic) and the repository.
The one exception is "model-binding". When you submit a Form to an [HttpPost] action that is strongly-typed to a "model" (should be a ViewModel). ASP.NET MVC (via reflection) will construct this "model" based on the fields in the HTTP POST.
When you do things like "UpdateModel", all that is doing is updating an object you supply with what came into the action method via the model binding. No actual database is being updated.
Don't know what else i can say. You seem to be having confusion about the "model". May i suggest you grab a copy of Steven Sanderson's Pro ASP.NET MVC 2 Framework book. It's fantastic, explains everything from the ground up - in simple terms, then ramps up the pace so your an expert by the end of the book.
My first try of MVC. Am trying to implement a simple example. Inspiration from here. Have I got this pattern (yet!)?
View: "Hey, controller, the user just told me he wants the first person"
Controller: "Hmm, having checked his credentials, he is allowed to do that... Hey, model, I want you to get me the first person"
Model: "First person... got it. Back to you, Controller."
Controller: "Here, I'll collect the new set of data. Back to you, view."
View: "Cool, I'll show the first person to the user now."
View:
namespace WinFormMVC
{
public partial class Form1 : Form
{
controller cont = new controller();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = cont.checkPermissionsAndGetFirstPerson();
}
}
}
Controller:
public class controller
{
public string checkPermissionsAndGetFirstPerson()
{
string returnValue = "";
if (checkPermissions())
{
model m = new model();
returnValue = m.getFirstPerson();
}
return returnValue;
}
public bool checkPermissions()
{
return true;
}
}
Model:
public class model
{
public string getFirstPerson()
{
return "Bill Smith";
}
}
Hmm... I am not sure if I'd call this MVC... As with ASP.NET WebForm, this form is more like an MVP pattern.
As per my understanding, in MVC, controller is the one responsible for managing all resources and flow of the code. In your example, you basically creating a Windows Form first (the view) and then attach a controller to it which is more of a MVP sort of things.
In a classical MVC pattern, the Model, once instantiated, will be linked to the View and when the model changes, the view will get notified (possibly through Observer / PubSub pattern).
Button click, etc. from the View will be routed to the controller which will coordinate those sort of stuffs.
see: this.
I would describe MVC more like this:
Request (MVC url routing, some event passed from previous UI etc)
Controller - check credentials, get data, return Model
Model - represents the data passed back from the Controller
View - render the Model returned by the Controller. Depending on the Model may display UI to initialise new Controller actions. May also pass Model back to next Controller action.
I think it can be a little confused because in many Model implementations (such as Linq) they provide data definition and access, but it's still the Controller that knows where to start (even if it's the Model that knows how to save its own changes).
So, your code should be something like:
//Controller:
public class PersonController
{
public PersonAction Detail(int personId)
{
Person returnValue;
//get person from DB and populate returnValue
return new PersonAction( returnValue );
}
}
//Model:
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
}
//View:
public partial class PersonDetailView : MVCForm<Person>
{
public Form1( Person model ):base(model) {
textBox1.Text = model.FirstName + " " + model.LastName;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = model.FirstName + " " + model.LastName;
}
}
What this example is missing is the framework that makes this all possible - there are two significant parts to that:
Something that takes/parses parameters and based on that calls a controller's action method. For instance in Asp.net MVC this is the routing handlers - the call above would be the request url: ~/Person/Detail/personId
Something that takes the result from the action (PersonAction in the example above) and finds the correct view to display. In this example it would open a PersonDetailView form and pass the Person model to it.
There are lots of example frameworks for an MVC implementation for WinForms - one of them may be a good starting point.
To adopt the MVC pattern you want to implement the following:
The view hooks up to the Model and listens for changes.
The controller hooks up to the view and handles specific events i.e. button presses etc
The model processes requests made by the controller and notifies the view.
I agree with Jimmy you would want to integrate something like the Observer pattern to this type of system so its possible for the Model to inform the View when it changes so the View can then update itself accordingly.
The difference with MVP is it introduces a Presenter class which monitors the Model on behalf of the View, in other words the View doesn't know about the Model, it only knows about its Presenter. The Presenter responds to changes in the Model and updates the View accordingly.
I think there are a few corrections to be made to your narrative. Strinctly speaking the view does not contact the controller, the user contacts the controller directly. In a web app it looks like this.
User: Hey web app, can I have the resource at /people/1
Routing engine: That means you person controller. get the first user.
User controller: Model, I need you to get me user record for the authenticated user, and the person record for the first person.
User Controller: Right. I know the authenticated user is an admin, and person one does exist. So return the Admin view for person to the user.
Rendering engine: Build up the html from the view (template) and person object (data)
This is difficult to do in webforms. I guess the best way is to have a page for each controller, and have that page dynamiclly choose and display a user control. One for each view. It is important in MVC that the view is dumb. All requests are handled directly by controllers, which then select which view to use.
Your checkPermissionsAndGetFirstPerson method is probably doing too much. Authorization and fetching data should probably be separate operations.
Also, if your Form1 class is your view, it probably shouldn't be constructing the controller. That seems backwards.
Most information you'll find on MVC in .NET will be for ASP.NET MVC web applications. You might want to check out the WinFormsMVC project on CodePlex to see how someone else has tackled this problem.
http://winformsmvc.codeplex.com/
Here's another WinForms MVC implementation on CodePlex. Looks like it's got a bit more documentation.
http://koosserymvcwin.codeplex.com/
You might find this tutorial very helpful: http://nerddinnerbook.s3.amazonaws.com/Intro.htm. Written by Scott Guthrie, it explains the MVC workflow very well. Part 4 should have the basics you're looking for.