Defining controller accessible variables from filters in Grails - authentication

I'm writing a small webapp in Grails, and to make sure all users are authenticated I'm using the following filter:
class LoginFilters {
static filters = {
loginCheck(controller:'*', action:'*') {
before = {
if (session.user_id) {
request.user = User.get(session.user_id)
} else if (!actionName.equals("login")) {
redirect(controller: "login", action: "login")
return false
}
}
}
}
}
And all controller methods start with reading the user property of the request object:
def actionName = {
def user = request.user
...
}
The code above works, but I'd rather avoid the duplicate code in the all controller methods. Would it be possible for the filter to bind the user object to a variable named "user" instead of "request.user", that will be accessible from all controllers?
I understand that there might be scoping issues that makes this impossible, but the Grails framework seems to be able to create quite some magic under the hood, so I figured it might be worth asking.

Using the beforeInterceptor in a controller may help:
class LoginController {
def user
def beforeInterceptor = {
user = request.user
}
def index = {
render text:"index: ${user}"
}
def test = {
render text:"test: ${user}"
}
}

I think it generally not a good idea insert the user object into the request object every time:
The request lifetime is very short, so you might end up making round trips to caches or even worse to the database on each http-request to retrieve an object, that you might not even need and that get's deleted immideately afterwards. So if you must, better store the whole object in the session instead of just the id.
Generally, I'd suggest you write a AuthenticationService with a method isLoggedIn() that returns true when the user is authenticated and a method getLoggedInUser() that returns this object.
class AuthenticationService {
def transactional = false
boolean isLoggedIn() { return session.user_id }
def getLoggedInUser() { return User.get(session.user_id) }
}
Then you use the Filter for redirection if not authenticated, and maybe the Interceptor for storing the local reference user = authenticationService.loggedInUser. But also I don't think this the best way to go. I suggest you'd create an abstract AuthenticationAwareController as base class for all your controllers in src/groovy and there have the convenience method like user
class AuthenticationAwareController {
def authenticationService
def getUser() { return authenticationService.loggedInUser() }
}
This way, you can later change you mind about storing the user however you like and don't have to change your code. Also you benefit from Caches in Hibernate, that share already retrieved user object instances between different sessions, so db roundtrips are avoided.
You still should check the retrieved user object for validity or throw an AuthenticationException in case the retrieval does not succeed. (Maybe something like AuthenticationService.getLoggedInUser(failOnError = false).)
You can even make this Service/ControllerBase a small plugin an reuse that on every application or go directly with the spring security plugin... ;-)

I think you can do this but is it really worth the trouble? It seems to me your only advantage is typing "user" instead of "request.user". Not a big gain. Anyway, I think you could follow the instructions in "12.7 Adding Dynamic Methods at Runtime" of the User Guide. I think that if you created a dynamic method "getUser() {return request.user}" that the Groovy JavaBeans getter/setter access would allow you to simply reference "user" the way you want.
If you do add a dynamic method you might want to skip the filter and do it all in the dynamic method.

Related

Efficient way to bring parameters into controller action URL's

In ASP.Net Core you have multiple ways to generate an URL for controller action, the newest being tag helpers.
Using tag-helpers for GET-requests asp-route is used to specify route parameters. It is from what I understand not supported to use complex objects in route request. And sometimes a page could have many different links pointing to itself, possible with minor addition to the URL for each link.
To me it seems wrong that any modification to controller action signature requires changing all tag-helpers using that action. I.e. if one adds string query to controller, one must add query to model and add asp-route-query="#Model.Query" 20 different places spread across cshtml-files. Using this approach is setting the code up for future bugs.
Is there a more elegant way of handling this? For example some way of having a Request object? (I.e. request object from controller can be put into Model and fed back into action URL.)
In my other answer I found a way to provide request object through Model.
From the SO article #tseng provided I found a smaller solution. This one does not use a request object in Model, but retains all route parameters unless explicitly overridden. It won't allow you to specify route through an request object, which is most often not what you want anyway. But it solved problem in OP.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Context.GetQueryParameters()" asp-route-somestring="optional override">Link</a>
This requires an extension method to convert query parameters into a dictionary.
public static Dictionary GetQueryParameters(this HttpContext context)
{
return context.Request.Query.ToDictionary(d => d.Key, d => d.Value.ToString());
}
There's a rationale here that I don't think you're getting. GET requests are intentionally simplistic. They are supposed to describe a specific resource. They do no have bodies, because you're not supposed to be passing complex data objects in the first place. That's not how the HTTP protocol is designed.
Additionally, query string params should generally be optional. If some bit of data is required in order to identify the resource, it should be part of the main URI (i.e. the path). As such, neglecting to add something like a query param, should simply result in the full data set being returned instead of some subset defined by the query. Or in the case of something like a search page, it generally will result in a form being presented to the user to collect the query. In other words, you action should account for that param being missing and handle that situation accordingly.
Long and short, no, there is no way "elegant" way to handle this, I suppose, but the reason for that is that there doesn't need to be. If you're designing your routes and actions correctly, it's generally not an issue.
To solve this I'd like to have a request object used as route parameters for anchor TagHelper. This means that all route links are defined in only one location, not throughout solution. Changes made to request object model automatically propagates to URL for <a asp-action>-tags.
The benefit of this is reducing number of places in the code we need to change when changing method signature for a controller action. We localize change to model and action only.
I thought writing a tag-helper for a custom asp-object-route could help. I looked into chaining Taghelpers so mine could run before AnchorTagHelper, but that does not work. Creating instance and nesting them requires me to hardcode all properties of ASP.Net Cores AnchorTagHelper, which may require maintenance in the future. Also considered using a custom method with UrlHelper to build URL, but then TagHelper would not work.
The solution I landed on is to use asp-all-route-data as suggested by #kirk-larkin along with an extension method for serializing to Dictionary. Any asp-all-route-* will override values in asp-all-route-data.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Model.RouteParameters.ToDictionary()" asp-route-somestring="optional override">Link</a>
ASP.Net Core can deserialize complex objects (including lists and child objects).
public IActionResult HelloWorld(HelloWorldRequest request) { }
In the request object (when used) would typically have only a few simple properties. But I thought it would be nice if it supported child objects as well. Serializing object into a Dictionary is usually done using reflection, which can be slow. I figured Newtonsoft.Json would be more optimized than writing simple reflection code myself, and found this implementation ready to go:
public static class ExtensionMethods
{
public static IDictionary ToDictionary(this object metaToken)
{
// From https://geeklearning.io/serialize-an-object-to-an-url-encoded-string-in-csharp/
if (metaToken == null)
{
return null;
}
JToken token = metaToken as JToken;
if (token == null)
{
return ToDictionary(JObject.FromObject(metaToken));
}
if (token.HasValues)
{
var contentData = new Dictionary();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToDictionary();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary { { token.Path, value } };
}
}

How to keep your MVC controllers DRY for Edit->Save->ValidateFail

I've got a Manage User event that takes an an optional userID and displays a user edit screen. There is a manageUserViewModel to go with this screen.
My Manage page has some dependencies - eg, PageTitle, what method to submit to, etc.
If I validate-fail, I need to show the manage screen again, but this time, using the view-model that was passed into the same method.
Supplying these dependencies in the fail scenario isn't very DRY.
How do I step repeating the dependencies? I tried putting them into a separate method, but that does not feel right.
public ActionResult Manage(Guid? UserID)
{
User user = this._UserLogic.GetUser(UserID);
ViewBag.Title = "User List";
ViewBag.OnSubmit = "Save";
ManageUserViewModel uvm = Mapper.Map<User, ManageUserViewModel>(user);
return View("Manage", uvm);
}
[AcceptVerbs("POST")]
public ActionResult Save(ManageUserViewModel uvm)
{
User user = this._UserLogic.GetUser(uvm.UserID);
if (!ModelState.IsValid)
// This is not very DRY!!!
ViewBag.Title = "Manage User";
ViewBag.OnSubmit = "Save";
return View("Manage", uvm);
}
Mapper.Map<ManageUserViewModel, User>(uvm, user );
this._UserLogic.SaveUser(user);
return RedirectToAction("Manage", new { UserID = user.ID });
}
I think you misunderstand DRY. DRY does not mean "NEVER repeat yourself", it means that you should not repeat yourself when it makes sense not to.
Different views have different requirements, and creating a complex structure just to avoid repeating yourself violates other best practices, like KISS, and SRP.
SOLID is interesting because Single Responsibility Principle is often at odds with Don't Repeat Yourself, and you have to come up with a balance. In most cases, DRY loses because SRP is far more important.
It looks to me like you have code here that is handling multiple responsibilities just so you can avoid writing similar code more than once. I disagree with doing that, because each view has different responsibilities and different requirements.
I would suggest just creating separate controller actions, views, and models for each action, particularly if the validation requirements are different for them. There may be a few things you can do (like using Partial Views or Editor Templates) to reduce repetition, but in general don't add lots of complexity just to avoid repetition.
You could add the 'Manager User' Title and 'Save' OnSubmit strings as properties of on the ManageUserViewModel. This means that you would not have to add them to the ViewBag each time you called Save.
You could also make a ManageUserService which could be responsible for the AutoMapper mappings and saving the user.
You code would then look like this:
public ActionResult Manage(Guid? UserID)
{
var uvm = _userService.GetById(UserId);
return View("Manage", uvm);
}
[AcceptVerbs("POST")]
public ActionResult Save(ManageUserViewModel uvm)
{
if (!ModelState.IsValid)
{
return View("Save", uvm);
}
_userService.Save(uvm);
return RedirectToAction("Manage", new { UserID = uvm.ID });
}
Just put the CRUD logic and AutoMapping functionality in the a class called UserService, and instance of which can be injected using Inversion of Control into your controller.
If you don't want to hard-code your string values into the view model itself, then you could add the values to an ApplicationResources file and reference those from the view model.
You will have to find some way to preserve this information between requests, which either means passing it back and forth between the client and server or saving it on the server. Saving it on the server means something like session but this feels a little heavy to me. You could add it to your ViewModel as #Ryan Spears suggested. To me that feels a little wrong, polluting the ViewModel with something that might be considered metadata. But that is just an opinion and I am not discrediting his answer because it is valid. Another possibility would be to just add the extra fields to the parameter list of the action method itself and use hidden fields.
[AcceptVerbs("POST")]
public ActionResult Save(ManageUserViewModel uvm, string title, string onSubmit)
{
...
}
In the form add:
<input type="hidden" name="title" value="#ViewBag.Title" />
<input type="hidden" name="onSubmit" value="#ViewBag.OnSubmit" />
This is essentially the same concept and solution as adding them to the ViewModel except in this situation they are not actually part of the ViewModel.
You can use RedirectToAction() and then export and import your tempdata (to maintain the ModelState) if you're worried about the 3 lines.
Personally I'd find it a lot more readable if you kept the logic in the POST version of the method, as you're performing something slightly different from the GET method, therefore not really repeating yourself. You could you probably keep the two ViewBag variables you have inside the View, and then there's no repetition at all.
As a side note: [HttpPost] now supersedes [AcceptVerbs]
We have come up with another solution that I thought I would share.
This based on the view-model containing info on what actions it can do, but we feel the controller should be specifying these (ie, controlling what actions different links route to) these because we have cases where the view-models are reused across actions. EG, the case where when you edit you can edit a template or an instance of something - the UI is the same, the only difference is the actions you post to/cancel from.
We abstracted away the part of the view-model that contains the data bound properties and the view model that contains other things we need for the view to render. We call the property-only object a DTO - it's not a true dto because it contains validation attributes.
We figure that we might be able to re-use these DTO's in the future for ajax or even XML requests - it, can keep validation DRY.
Anyway - here is an example of the code, we are happy with it (for now) and hope it helps others.
[HttpGet]
[ValidateInput(false)]
public virtual ActionResult ManageUser(ManageUserDTO dto, bool PopulateFromObject = true)
{
User user = this._UserLogic.GetUser(dto.UserID);
if (PopulateFromObject)
Mapper.Map<User, ManageUserDTO>(user, dto);
ManageUserViewModel vm = new ManageUserViewModel()
{
DTO = dto,
PageTitle = Captions.GetCaption("pageTitle_EditUser"),
OnSubmit = GetSubmitEventData(this.ControllerName, "SaveUser"),
OnCancel = GetCancelEventData(this.ControllerName, "ListUsers"),
};
return View("ManageUser", vm);
}
[HttpPost]
public virtual ActionResult SaveUser(ManageUserViewModel vm)
{
User user = this._UserLogic.GetUser(vm.DTO.UserID);
if (!ModelState.IsValid)
{
return ManageUser(vm.DTO, false);
}
Mapper.Map<ManageUserDTO, User>(vm.DTO, user);
this._UserLogic.SaveUser(user);
TempData.AddSuccess(Captions.GetCaption("message_UserSavedSuccessfuly"));
return RedirectToAction("ManageUser", new { UserID = user.ID });
}
The model-binder will set any URI variables into the dto in the get action. My logic layer will return a new User object if a call to getUserByID(null) is made.

Yii Behaviors and scenario

i have a behavior for my models, the behavior has beforeFind, beforeSave, in methods i override user_id, something like:
...
public functio beforeSave() {
$this->owner->user_id = Yii::app()->user->id
}
I have model User, how can i disable behavior for registration new user?
Saving code:
$user = new User();
$user->id = 1332;
$user->field1 = 'data';
$user->save();
but on save i have null in $user->id (because work behavior).
i tried
$user->disableBehaviors();
$user->detachBehavior();
Without result.
Maybe its not right way? I create behaviors for identify users in system (find only user something, save only with user id...), but that if i have new user with full previegies, i should again detach behaviors?
If condition can be changed in future I just pass it as callback parameter into behavior from model.
This give you a bit more control over the condition. Hence, behavior becomes more reusable - if it is used by several models this condition can be unique for each.
Example below is a bit simplified, but you should get the idea.
Behavior:
class SomeBehavior extends CActiveRecordBehavior
{
public $trigger;
public function beforeSave($event)
{
if(!call_user_func($this->trigger))
return;
// do what you need
$this->owner->user_id = Yii::app()->user->id;
}
}
Model:
class SomeModel extends CActiveRecord
{
public function behaviors()
{
$me=$this;
return array(
'some'=>array(
'class'=>'SomeBehavior',
'trigger'=>function() use($me){
return $me->scenario=='some-scenario';
}
)
);
}
}
Also I use PHP 5.3. So, I use closure for trigger callback.
If your PHP version is less than 5.3 - anything callable can be used instead. Check here http://www.php.net/manual/en/function.is-callable.php
Because of behavior is a method, you can declare your own logic inside.
The model knows about its scenario, so there is no problem to return different arrays for different conditions:)
Hope it be helpful for somebody.
You can check Yii::app()-user->isGuest to determine if the user is logged in or not. or you can just try looking for the null. Like this:
if (!Yii::app()->user->isGuest)
$this->owner->user_id = Yii::app()->user->id;
or
if (null !== Yii::app()->user->id)
$this->owner->user_id = Yii::app()->user->id;

TDD Best Practice In Using Restful Api in Yii application

I'm constantly looking for the best way to use TDD in Yii app development. Nowday most web app are composed by a fronted, an API layer (usually JSON) to provide async calls to the server and a backend. By my side, most used test in this of app are unit tests and functional ones. The latter the most widely showed in guides and books leverage PHPUnit + Selenium, but Behat + Mink seems very cool too (but I'm not really confident with it yet)
If you ever used functional tests that use a browser (like Selenium) you know that the less you have to run them the better you feel. This cause they're slower, harder to maintain and sometimes (like the popup FB Login using JS SDK) are painful.
When working with a single web page application I care about testing JSON output of my apis. I'd like to test these functionalities with a unit test like approach in order to have faster tests that are easier to maintain. Considering that most of my Controller's action are availaible to Logged only user using accessControl filter I wondered on the best ways to have my tests up and running.
At this moment I think to have two ways to accomplish this
use cUrl toward the desired enpoint to get the JSON directly invoke
the controller's function
In the first scenario I can use fixtures but I got no way to mock CWebUser class (to emulate a logged user), using Apache when the cUrl comes it gets executed by an instance of my CWebApplication that is not the one executed by PHPUnit. I can get rid of this problem by making all my API calls stateless and, as a consequence, removing accessControl filter.
In the second one the only way I found to mock CWebUser class is to override it in the test class that I'm executing. This approach pays until I dont need to test use cases requiring different type of user, and I got no way to change at runtime (or at setup time) my webuser mock. The only way I found to mock my webuser is the one you can find below, this cause $this->getMock('WebUser') doesnt affect anyway CWebApplication's WebUser (read-only) singleton defined in the configuration file.
Here comes a concrete example:
class UserControllerTest extends CDbTestCase
{
public $fixtures=array(
/* NEEDED FIXTURES*/
);
public function testUserCanGetFavouriteStore() {
$controller = new UserController(1);
$result = json_decode($controller->actionAjaxGetFavStore());
$this->assertInternalType('array', $result->data);
$model = $result->data[0];
$this->assertEquals($model->name, "Nome dello Store");
}
}
class WebUser extends CWebUser {
public function getId() {
return 1;
}
public function getIsGuest() {
return false;
}
};
I was wondering if being able to authenticate with the api interface, either by an API key or a user/password combo could be useful.
This is ok if I move toward a almost stateless API integration, but most of the time I just have controller's actions (permitted to logged user only) that returns Json data to populate the frontend.
Anyone can suggest me a better method? Maybe it's just useless to test this kind of JSON output?
Best Regards
Maybe I'm oversimplifying your problem. It sounds like you want to emulate user logins before running the test? If that's the case, why not just create a User object in your fixture and actually log them in before running a test, and log them out after?
Something like:
/**
* Sets up before each test method runs.
* This mainly sets the base URL for the test application.
*/
protected function setUp()
{
parent::setUp();
// login as registered user
$loginForm = new UserLoginForm();
$loginForm->email = USER_EMAIL; // use your fixture
$loginForm->password = USER_PASSWORD; // use your fixture
if(!$loginForm->login()) {
throw new Exception("Could not login in setup");
}
}
protected function tearDown()
{
parent::tearDown();
Yii::app()->user->logout(true);
}
Ok actually the only solution that me and my team found is creating a stub WebUser class.
Rewriting WebUser class in this way you can authenticate a user without having Yii relying on the session.
class WebUserMock extends WebUser {
public function login($identity,$duration=0)
{
$id=$identity->getId();
$states=$identity->getPersistentStates();
if($this->beforeLogin($id,$states,false))
{
$this->changeIdentity($id,$identity->getName(),$states);
$duration = 0;
if($duration>0)
{
if($this->allowAutoLogin)
$this->saveToCookie($duration);
else
throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.',
array('{class}'=>get_class($this))));
}
$this->afterLogin(false);
}
return !$this->getIsGuest();
}
public function changeIdentity($id,$name,$states)
{
$this->setId($id);
$this->setName($name);
$this->loadIdentityStates($states);
}
// Load user model.
protected function loadUser() {
$id = Yii::app()->user->id;
if ($id!==null)
$this->_model=User::model()->findByPk($id);
return $this->_model;
}
};
In the setUp method of your test class you can login any user (in this case leveraging my fixtures)
//a piece of your setUp() method....
$identity = new UserIdentity($this->users('User_2')->email, md5('demo'));
$identity->authenticate();
if($identity->errorCode===UserIdentity::ERROR_NONE)
Yii::app()->user->login($identity);
As a final thing to do just override the user component in the test configuration file and tell him to use this one:
protected/config/test.php
'user'=>array(
'class' => 'application.tests.mock.WebUserMock',
'allowAutoLogin'=>false,
),
Still not sure that this is the best way to handle it but seems to work fine

MVC 3/EF repository pattern and proper data access

Being rather new to MVC 3 and EF, I'm trying to understand the best architectural approach to developing an application for my company. The application will be a large-scale application that potentially handles hundreds of users at the same time, so I want to make sure I understand and am following proper procedures. So far, I've determined that a simple repository pattern (such as Controller -> Repository -> EF) approach is the best and easiest to implement, but I'm not sure if that is definitely the best way to do things. The application will basically return data that is shown to a user in a devexpress grid and they can modify this data/add to it etc.
I found this article and it is rather confusing for me at this time, so I'm wondering if there is any reason to attempt to work with a disconnected EF and why you would even want to do so: http://www.codeproject.com/Articles/81543/Finally-Entity-Framework-working-in-fully-disconne?msg=3717432#xx3717432xx
So to summarize my question(s):
Is the code below acceptable?
Should it work fine for a large-scale MVC application?
Is there a better way?
Will unnecessary connections to SQL remain open from EF? (SQL Profiler makes it look like it stays open a while even after the using statement has exited)
Is the disconnected framework idea a better one and why would you even want to do that? I don't believe we'll need to track data across tiers ...
Note: The repository implements IDisposable and has the dispose method listed below. It creates a new instance of the entity context in the repository constructor.
Example Usage:
Controller (LogOn using Custom Membership Provider):
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
User newUser = new User();
using (AccountRepository repo = new AccountRepository())
{
newUser = repo.GetUser(model.UserName);
...
}
}
Membership Provider ValidateUser:
public override bool ValidateUser(string username, string password)
{
using (AccountRepository repo = new AccountRepository())
{
try
{
if (string.IsNullOrEmpty(password.Trim()) || string.IsNullOrEmpty(username.Trim()))
return false;
string hash = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Trim(), "md5");
bool exists = false;
exists = repo.UserExists(username, hash);
return exists;
}catch{
return false;
}
}
}
Account Repository Methods for GetUser & UserExists:
Get User:
public User GetUser(string userName)
{
try
{
return entities.Users.SingleOrDefault(user => user.UserName == userName);
}
catch (Exception Ex)
{
throw new Exception("An error occurred: " + Ex.Message);
}
}
User Exists:
public bool UserExists(string userName, string userPassword)
{
if (userName == "" || userPassword == "")
throw new ArgumentException(InvalidUsernamePassword);
try
{
bool exists = (entities.Users.SingleOrDefault(u => u.UserName == userName && u.Password == userPassword) != null);
return exists;
}
catch (Exception Ex)
{
throw new Exception("An error occurred: " + Ex.Message);
}
}
Repository Snippets (Constructor, Dispose etc):
public class AccountRepository : IDisposable
{
private DbContext entities;
public AccountRepository()
{
entities = new DbContext();
}
...
public void Dispose()
{
entities.Dispose();
}
}
What's acceptable is pretty subjective, but if you want to do proper data access I suggest you do NOT use the repository pattern, as it breaks down as your application gets more complex.
The biggest reason is minimizing database access. So for example look at your repository and notice the GetUser() method. Now take a step back from the code and think about how your application is going to be used. Now think about how often you are going to request data from the user table without any additional data. The answer is almost always going to be "rarely" unless you are creating a basic data entry application.
You say it your application will show a lot of grids. What data is in that Grid? I'm assuming (without knowing your application domain) that the grids will combine user data with other information that's relevant for that user. If that's the case, how do you do it with your repositories?
One way is to call on each repository's method individually, like so:
var user = userRepository.GetUser("KallDrexx");
var companies = companyRepository.GetCompaniesForUser(user.Id);
This now means you have 2 database calls for what really should be just one. As your screens get more and more complex, this will cause the number of database hits to increase and increase, and if your application gets significant traffic this will cause performance issues. The only real way to do this in the repository pattern is to add special methods to your repositories to do that specific query, like:
public class UserRepository
{
public User GetUser(string userName)
{
// GetUser code
}
public User GetUserWithCompanies(string userName)
{
// query code here
}
}
So now what happens if you need users and say their contact data in one query. Now you have to add another method to your user repository. Now say you need to do another query that also returns the number of clients each company has, so you need to add yet another method (or add an optional parameter). Now say you want to add a query that returns all companies and what users they contain. Now you need a new query method but then comes the question of do you put that in the User repository or the Company repository? How do you keep track of which one it's in and make it simple to choose between GetUserWithCompany and GetCompanyWithUsers when you need it later?
Everything gets very complex from that point on, and it's those situations that have made me drop the repository pattern. What I do now for data access is I create individual query and command classes, each class represents 1 (and only 1) query or data update command to the database. Each query class returns a view model that only contains the data I need for one specific user usage scenario. There are other data access patterns that will work too (specification pattern, some good devs even say you should just do your data access in your controllers since EF is your data access layer).
The key to doing data access successfully is good planning. Do you know what your screens are going to look like? Do you know how users are going to use your system? Do you know all the data that is actually going to be on each screen? If the answer to any of these is no, then you need to take a step back and forget about the data layer, because the data layer is (or should be for a good application) determined based on how the application is actually going to be used, the UI and the screens should not be dependent on how the data layer was designed. If you don't take your UI needs and user usage scenarios into account when developing the data access, your application will not scale well and will not be performant. Sometimes that's not an issue if you don't plan on your site being big, but it never hurts to keep those things in mind.
No matter what you do, you may consider moving instantiation and disposing of your context to your controller like this:
public class MyController : Controller
{
private Entities context = new Entities();
...
public override void Dispose()
{
context.Dispose();
}
}
You can then pass that context into any method that needs it without duplicating the overhead of creating it.
I disagree that the repository pattern is necessarily bad for the same reason. You create multiple classes to break up your code to make it manageable and still reuse the same context. That could look something like this:
repository.Users.GetUser(userName);
In this case "Users" is a lazy loaded instance of your user repository class which reuses the context from your repository. So the code for that Users property in your repository would look something like this:
private UserRepository users;
public UserRepository Users
{
get
{
If (users == null)
{
users = new UserRepository(this);
}
return users;
}
}
You can then expose your context to these other lazy loaded classes via a property.
I don't think this necessarily conflicts with KallDrexx's pattern. His method simply flips this so instead of
repository.Users.GetUser(userName);
You would have something like
UserQuery query = new UserQuery(repository.Users);
This then becomes an issue of syntax. Do you want this:
repository.Area.Query(value1, value2, ...);
Or this:
AreaQuery query = new AreaQuery { Property1 = value1, ... };
The latter actually works nicer with model binding but obviously is more verbose when you actually have to code it.
Best advice KallDrexx gave is to just put your code I your actions and then figure it out. If you are doing simple CRUD, then let MVC instantiate and populate your model, then all you have to do is attach and save. If you find you can reuse code, move it to where it can be reused. If your application starts getting too complicated, try some of these recommendations until you find what works for you.