is there a better way to implement role based acton and view in mvc than if/else? - asp.net-mvc-3

So I have three roles, administrators, companies and employees in my mvc .net application that uses asp.net membership in a separate database. I moved the .net membership in a different database for now because everytime I modify the model, the .net membership tables are getting deleted.
Anyway, I am handling different roles using if/else in the action method. For example, in Index() action, I check if the user is in administrators role, then create model and linq query based on that. If user in companies role, different query and if user in employees role, different query. check code below. The model created after the if condition is passed to the View.
I feel like this is not the best way to handle roles. Is this the best way to handle roles? I am considering different areas as well, but I use same views for the different roles, i think it may not be productive.
Any suggestion/idea greatly appreciated.
[Authorize]
public class CompanyController : Controller
{
private MyDBContext db = new MyDBContext();
//
// GET: /Company/
public ViewResult Index()
{
var viewModel = new CompanyIndexViewModel();
if (Roles.IsUserInRole("administrators")) {
viewModel = new CompanyIndexViewModel { Companies = db.Companies.ToList() };
}
else if (Roles.IsUserInRole("companies")) {
viewModel = new CompanyIndexViewModel { Companies = db.Companies.Where(c => c.Username.ToLower().Equals(this.User.Identity.Name.ToLower())).ToList() };
}
else if (Roles.IsUserInRole("employees")) {
string userName = this.User.Identity.Name.ToLower();
var companies = db.Companies.Where(c => c.Appointments.Any(a =>
a.Employee.Username.ToLower() == userName)).ToList();
viewModel = new CompanyIndexViewModel { Companies = companies.ToList() };
}
return View(viewModel);
}
....

There is two things I would do:
Firstly, what StanK said and move it out of the controller action. However, I would move it out of the Controller all together. This sort of logic shouldn't really reside in the Controller to begin with (whether it be in an action, or a private method in the controller).
Think about it this way: What if your logic for who gets to see what companies changes.. you'll have to change it in all sorts of different places.
Secondly, I would create a constructor for the CompanyIndexViewModel that took in a list of Company instead of initializing it inline like that. Does the CompanyIndexViewModel contain anything else besides companies?
// your controller
public ViewResult Index()
{
var viewModel = CompanyIndexViewModel(CompanyService.GetCompaniesForCurrentUser());
return View(viewModel);
}
Ideally, you would also have your controller depend on an interface representing the "CompanyService", and have that injected into your controller.
Take a look at this blog which outlines using Ninject with MVC 3. It's ridiculously simple to setup for something that is so powerful for you later on.
If you take one thing away from what I've said above, it's probably best to start with moving your logic out of the controller.

I would move the code that builds the list of Companies to it's own method to tidy up the controller action , which would also make the logic that determines the list of Companies for the current user re-useable.
e.g.
private List<Company> GetCompaniesForCurrentUser()
{
var userName = this.User.Identity.Name.ToLower();
if (Roles.IsUserInRole("administrators"))
return db.Companies.ToList();
if (Roles.IsUserInRole("companies"))
return db.Companies.Where(c => c.Username.ToLower().Equals(userName)).ToList();
if (Roles.IsUserInRole("employees"))
return db.Companies.Where(c => c.Appointments.Any(a =>
a.Employee.Username.ToLower() == userName)).ToList();
throw new AuthorizationException("User " + userName + " is not authorised.");
}
public ViewResult Index()
{
var viewModel = new CompanyIndexViewModel { Companies = GetCompaniesForCurrentUser() };
return View(viewModel);
}

Related

.NET MVC3/Holding temp model

I have a situation where i have to take input(form) from user. After continue button is pressed next view page is displayed. But after continue is pressed i don't want to store the model in the DB. I have to display some details(combining some tables) according to input given by the user earlier and again get some data from user. Only then i want to store the model in the respective tables.
How can i perform this? I tried getting Model from user and passing to the function that generates next page. Is this is way to do it? or there is other way around?
Store the model submitted by the first form in session.
[HttpPost]
public ActionResult ContinueForm1(Model1 model1)
{
if(ModelState.IsValid)
{
Session["Model1"] = model1;
return View("Form2");
}
return View();
}
[HttpPost]
public ActionResult ContinueForm2(Model2 model2)
{
if(ModelState.IsValid)
{
... model2 is already here, get the model1 from session
... and save to datatbase finally return a different view or redirect to some
... other action
}
return View();
}
You are heading down the right track.
You need to grab the model that is passed back from the first view - preferably you are using ViewModels here rather than binding directly to your db models. Have a look at http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/ and Why should I use view models? as to why these are good things.
The easiest way to do this is to pass the model in as an argument to your method e.g.
Assuming that your views are using the same ViewModel ( which may or may not be true) then you can send the viewmodel straight to your new view - else you can copy the elements into a new viewModel and send that.
e.g.
[HttpPost]
public ViewResult Step1(MyViewModel viewModel)
{
//Do some validation here perhaps
MySecondViewModel secondViewModel = new MySecondViewModel{
Id = viewModel.Id,
// etc. etc.
};
return View("Step2", secondViewModel);
}
Then you can carry on as you need until you have to persist the entity to the database.
NB as you do not need to do anything special in the form to make it post the model as an argument as long as the view is strongly typed to that ViewModel.

how to fill a viewmodel EF 4.1

I am working on asp.net MVC 3 project. I am using EF 4.1 code first approach. I have entity class called disputes. It maps to a table in database name tblDisptes. It has three properties names Lastviewedby, Lastupdatedby, LastRespondedBy ... all three integers. I have created a viewmodel 'disputeviewmodel' with three more properties Lastviewedbyname, Lastupdatedbyname, LastRespondedByname and a property named dispute. Now my repository function returns list of disputes. how to convert this list to List of disputeviewmodel so that these three properties are filled with the names ?
Please suggest.
Your view model doesn't really need a property named dispute. A view model should not reference your domain models.
As far as the mapping is concerned one possibility is to manually do it but that could quickly become cumbersome with more complex models:
public ActionResult Foo()
{
IEnumerable<disputes> disputes = ... fetch from repo
IEnumerable<disputeviewmodel> disputeViewModels = disputes.Select(x => new disputeviewmodel
{
Lastviewedbyname = x.Lastviewedby,
Lastupdatedbyname = x.Lastupdatedby,
LastRespondedByname = x.LastRespondedBy
});
return View(disputeViewModels);
}
So a better approach would be to use AutoMapper:
public ActionResult Foo()
{
IEnumerable<disputes> disputes = ... fetch from repo
IEnumerable<disputeviewmodel> disputeViewModels = Mapper.Map<IEnumerable<disputes>, IEnumerable<disputeviewmodel>>(disputes);
return View(disputeViewModels);
}
Why your Dispute doesn't look like that?
class Dispute
{
public User LastViewedBy;
public User Lastupdatedbyname
public User LastRespondedByname
}
That's how it should look. Then problem solved. When you query for Disputes you already have Users (with there usernames) ready.

Model binding in controller when form is posted - why to use view model instead of class from domain model?

I'm still reasonably new to ASP.NET MVC 3. I have come across view models and their use for passing data from a controller to the view. In my recent question on model binding two experts suggested that I should use view models for model binding as well.
This is something I haven't come across before. But both guys have assured me that it is best practise. Could someone maybe shed some light on the reasons why view models are more suitable for model binding?
Here is an example situation: I have a simple class in my domain model.
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
And this is my controller:
public class TestController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult ListParents()
{
return View(testParentRepository.TestParents);
}
public ViewResult EditParent(int testParentID)
{
return View(testParentRepository.TestParents.First(tp => tp.TestParentID == testParentID));
}
[HttpPost]
public ActionResult EditParent(TestParent testParent)
{
if (ModelState.IsValid)
{
testParentRepository.SaveTestParent(testParent);
TempData["message"] = string.Format("Changes to test parents have been saved: {0} (ID = {1})",
testParent.Name,
testParent.TestParentID);
return RedirectToAction("ListParents");
}
// something wrong with the data values
return View(testParent);
}
}
So in the third action method which gets invoked when an HTTP POST arrives I used TestParent for model binding. This felt quite convenient because the browser page that generates the HTTP POST request contains input fields for all properties of TestParent. And I actually thought that's the way the templates that Visual Studio provides for CRUD operations work as well.
However the recommendation that I got was that the signature of the third action method should read public ActionResult EditParent(TestParentViewModel viewModel).
It sounds appealing at first, but as your models and view actions get increasingly complex, you start to see the value of using ViewModels for (most) everything, especially input scenarios.
Case 1 - Most web frameworks are susceptible to over-posting. If you are binding straight to your domain model, it is very possible to over-post data and maliciously change something not belonging to the user. I find it cleaner to bind to an input view model than have long string lists of white lists or black lists, although there are some other interesting ways with binding to an interface.
Case 2 - As your input grows in complexity, you'll run into times when you need to submit and validate fields not directly in the domain model ('I Agree' checkboxes, etc)
Case 3 - More of a personal thing, but I find model binding to relational domain objects to be a giant pain at times. Easier to link them up in AutoMapper than deal with MVC's modelbinder for complicated object graphs. MVC's html helpers also work more smoothly against primitive types than deep relational models.
The negatives of using ViewModels is that it isn't very DRY.
So the moral of the story is, binding to domain models can be a viable solution for simple things, but as the complexity increases, it becomes easier to have a separate view model and then map between the two.

MVC3 with EF 4.1 and EntityState.Modified

Updating an object with MVC3
I have a model that I can modify, please see the sample below:
[HttpPost]
public ActionResult Edit(Company c)
{
if (ModelState.IsValid)
{
db.Entry(c).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(c);
}
The model has other fields that are not showing in the view and cannot be modified by the user, but when I click the submit button the fields that were not showing in the view got set to null.
Can I somehow let EF know not to modify certain fields? Thanks.
Generally it is better not to bind to the entity object directly, rather create an edit model and bind to that.
After all.. whats to stop someone posting back values you don't want changed with this approach?
The main problem here is the fact that mvc model binding changes the properties in the model before its in a context therefore the entity framework doesn't know which values have changed (and hence which should be updated)
You've mitigated that slightly with db.Entry(c).State = EntityState.Modified; but that tells the entity framework that the whole record has been updated.
I would normally do the following:
Bind to a model specifically for this controller first
Create an instance of the entity class you want to update, set the Id accordingly and attach it to the context
Update the properties on the entity to be the same as the model you binded to (object is attached and therefore entity framework is tracking which columns are being changed now)
SaveChanges
Step 3 is a bit tedious therefore consider using a tool like automapper to make things easier
Edit:
[HttpPost]
public ActionResult Edit(Company c)
{
if (ModelState.IsValid)
{
Company dbCompayObjct = new Company { companyId = c.companyId };
db.Company.Attach(dbCompayObjct);
dbCompanyObjct.CompanyName = c.CompanyName;
dbCompanyObjct.City = c.City;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(c);
}
You are apparently overwriting your existing record with an incomplete record. When you use the method above, it will completely replace the existing one.
You either need to fill in all the fields you don't want to replace with the existing values, or you need to get the existing record and modify the fields you want to modify, then save it.
Reflection is not always evil, sometimes it's your friend:
public ActionResult Edit(Company c)
{
if (ModelState.IsValid)
{
Company UpdateC = db.Company.find(c.CompanyID);
foreach (var property in typeof(Company).GetProperties())
{
var propval = property.GetValue(c);
if (propval != null)
{
property.SetValue(UpdateC, propval);
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(c);
}

Using the entity modal with mvc -mvvm

Hi there I am hoping someone can point me in the right direction.
I want to create an mvc applicaton I have worked my way through the music store example and still am not 100% sure the correct way to do things.
Lets say I want to create an application that stores cooking receipes.
I have a 3 tables
RecipeTable
RecipeID
RecipeName
RecipeIngredients
RecipeIngredientID
RecipeID
IngredientID
Measurement
IngredientTable
IngredientID
IngredientName
All have PK & FK mappings very basic, I create a new mvc application and use the entity framework to create a new entity e.g. RecipeDB
My next step is I create a new model for each of the tables and give the properties my desired displaynames and specify required fields extra.
Do I then create a viewmodel e.g. RecipesViewModel that looks something like
public class RecipesViewModel
{
public int RecipeID { get; set; }
public string RecipeName { get; set; }
public List<RecipeIngredients> { get; set; }
}
I now create the controller (Ithink) but I am not really sure how to bind that to database entity.
I know you can call the database by doing something like RecipeEntities db = new recipeEntites(); however binding the results to the vm I am little confussed on how to do that.
Am I heading in the right direction so far?
You could use AutoMapper. It's a great tool allowing you to convert from one type to another and in your case from the model to the view model.
public ActionResult Foo()
{
RecipeDB model = _repository.GetRecipies();
RecipesViewModel viewModel = Mapper.Map<RecipeDB, RecipesViewModel>(model);
return View(viewModel);
}
or you could even define a custom action attribute (like the one I used in my sample MVC project) allowing you to simply write:
[AutoMap(typeof(RecipeDB), typeof(RecipesViewModel))]
public ActionResult Foo()
{
RecipeDB model = _repository.GetRecipies();
return View(model);
}

Resources