Mvc3 - Best practice to deal with data which are required for (almost) all requests? - asp.net-mvc-3

I am creating an application in mvc3 and wondering how to deal with database data which is required for all application requests, some of them depends on a session, some of them depends on url pattern basically all data is in database.
Like to know best practice

What I do in my applications and consider to be the best practice is to load your common data to the ViewBag on the Controller constructor.
For every project, I have a DefaultController abstract class that extends Controller. So, every controller in the project must inherit from DefaultController, instead of Controller. In that class' constructor, I load all data common to the whole project, like so:
// DefaultController.cs
public abstract class DefaultController : Controller
{
protected IRepository Repo { get; private set; }
protected DefaultController(IRepository repo)
{
Repo = repo;
ViewBag.CurrentUser = GetLoggedInUser();
}
protected User GetLoggedInUser()
{
// your logic for retrieving the data here
}
}
// HomeController.cs
public class HomeController : DefaultController
{
public HomeController(IRepository repo) : base(repo)
{
}
// ... your action methods
}
That way you will always have the logged in user available in your views.

I do the same as #rdumont but with one exception: I create a CommonViewModel which I use to define all common properties that I use.
public class CommonViewModel
{
public string UserName {get;set;}
public string Extension {get;set; }
}
Declare a property in the base controller:
public abstract class BaseController : Controller
{
protected CommonViewModel Commons { get; private set; }
protected virtual void OnResultExecuting(ResultExecutingContext filterContext)
{
ViewBag.Commons = Commons;
}
}
By doing so I get everything almost typed. The only cast that I need to do is to cast ViewBag.Commons to the CommonViewModel.

Best is to avoid ViewBag at all.
See this answer, which details how to use Html.RenderAction() for that purpose:
Best way to show account information in layout file in MVC3

I'd suggest using a base ViewModel class.
So a base class with properties/functions which should be available at any point.

Related

How can i use custom dbcontext (Audit Log) with sharprepository

I have a custom dbcontext which name is Tracker-enabled DbContext (https://github.com/bilal-fazlani/tracker-enabled-dbcontext).I want to use it for audit log
And how can I implement EFRepository?
I implemented tracker-enabled-context but i cant solve how override sharp repo commit method.
public class HayEntities : TrackerContext
{
static HayEntities()
{
Database.SetInitializer<HayEntities>(null);
}
public HayEntities() : base(HayEntities)
{
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ValidateOnSaveEnabled = false;
}
public DbSet<Dummy> Dummys{ get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DummyConfiguration());
} }
}
public class DummyRepository : ConfigurationBasedRepository<DE.Dummy, long>, IDummyRepository
{
private readonly IRepository<DE.Dummy, long> _servisHasarRepository;
public DummyRepository (HayEntities hayEntities, ICachingStrategy<DE.Dummy, long> cachingStrategy = null)
{this.CachingEnabled = false;
_dummyRepository = new EfRepository<DE.Dummy, long>(hayEntities, cachingStrategy);
}
public void UpdateOrCreate() {
//In this area how can override save/commit method
}
}
You will want to tell SharpRepository to use an IoC provider to inject the DbContext. This will take care of getting the proper DbContext for your EfRepository.
If you want to control things based on the configuration and have custom repositories so you can implement your own mehods like UpdateOrCreate() then you would inherit from ConfigurationBasedRepository as you have in the example.
There are more details on setting up IoC with SharpRepository here: http://fairwaytech.com/2013/02/sharprepository-configuration/ (look in the "Entity Framework and Sharing the DbContext" section)
First look on NuGet for SharpRepository.Ioc.* to find the specific IoC you are using. If you are using StructureMap then you would do something like this.
In your StructureMap configuration:
// Hybrid (once per thread or ASP.NET request if you’re in a web application)
For<DbContext>()
.HybridHttpOrThreadLocalScoped()
.Use<HayEntities>()
.Ctor<string>("connectionString").Is(entityConnectionString);
Then you need to tell SharpRepository to use StructureMap by calling this in your startup code:
RepositoryDependencyResolver.SetDependencyResolver(new StructureMapDependencyResolver(ObjectFactory.Container));
After doing these things, then if you use EfRepository then it will know to ask StructureMap for the DbContext.
Now in your example above where you are using ConfigurationBasedRepository, I would suggest setting the caching in the configuration file instead of in code since you are using the configuration to load the repository. Since IoC is handling the DbContext you don't need to do anyhing with that and you can focus on the custom method you want to write.
public class DummyRepository : ConfigurationBasedRepository<DE.Dummy, long>, IDummyRepository
{
public void UpdateOrCreate()
{
// You have access to the underlying IRepository<> which is going to be an EfRepository in your case assuming you did that in the config file
// here you can call Repository.Add(), or Reposiory.Find(), etc.
}
}

Attribute routing and inheritance

I am playing around with the idea of having a base controller that uses a generic repository to provide the basic CRUD methods for my API controllers so that I don't have to duplicate the same basic code in each new controller. But am running into problems with the routing attribute being recognized when it's in the base controller. To show exactly what the problem I'm having I've created a really simple WebAPI controller.
When I have a Get method in the main Controller and it inherits from the ApiController directly I don't have any problems and this works as expected.
[RoutePrefix("admin/test")]
public class TestController : ApiController
{
[Route("{id:int:min(1)}")]
public string Get(int id)
{
return "Success";
}
}
When I move the Get method into a base controller it is returning the contents of the 404 page.
[RoutePrefix("admin/test")]
public class TestController : TestBaseController
{
}
public class TestBaseController : ApiController
{
[Route("{id:int:min(1)}")]
public string Get(int id)
{
return "Success";
}
}
Some more interesting notes:
I can access the action at GET /Test/1. So it is finding it based on the default route still.
When I try to access POST /admin/test, it returns the following JSON
{
"Message":"No HTTP resource was found that matches the request URI 'http://test.com/admin/test'.",
"MessageDetail":"No type was found that matches the controller named 'admin'."
}
Does anyone know of a way to get the routing to work with attributes from a base controller?
Attribute routes cannot be inherited. This was a deliberate design decision. We didn't feel right and didn't see valid scenarios where it would make sense to inherit them.
Could you give a more realistic scenario as to where you would want to use this?
[Update(3/24/2014)]
In the upcoming 5.2 release of MVC Web API, there is going to be an extensibility point called System.Web.Http.Routing.IDirectRouteProvider through which you can enable the inheritance scenario that you are looking for here. You could try this yourself using the latest night builds(documentation on how to use night builds is here)
[Update(7/31/2014)]
Example of how this can be done in Web API 2.2 release:
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
//---------
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
// inherit route attributes decorated on base class controller's actions
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
Using Web API 2.2, you can:
public class BaseController : ApiController
{
[Route("{id:int}")]
public string Get(int id)
{
return "Success:" + id;
}
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
as outlined here: http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22
Got it.
[Route("api/baseuploader/{action}")]
public abstract class BaseUploaderController : ApiController
{
[HttpGet]
public string UploadFile()
{
return "UploadFile";
}
}
[Route("api/values/{action}")]
public class ValuesController : BaseUploaderController
{
[HttpGet]
public string Get(int id)
{
return "value";
}
}
One caveat here is that the route action paramter must be the same as the action name. I could not find a way to get around that. (You cannot rename the route with a RouteAttribute)

How do I use asp.net MVC Scaffolder with a context that is two subclasses down from dbcontext

I have a standard model set.
I have a base context class that inherits from dbcontext to add some features I needed.
public class MyContext : DbContext
{
public void MyFeature() {
}
}
I then have my actual Data Context:
public class DataContext : MyContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
I want to use the scaffolder built in when you create a controller, but I get an error "Unsupported Context Type"
If I change the datacontext to just inherit from dbcontext directly it works, but at this point I have alot of stuff that uses the added features, so changing the inheritance cant be done without commenting out all that stuff. And I have of course simplified down the features, it is actually quite alot of stuff, so adding it directly into the datacontext would be alot of work, plus the scaffolder should be smart enough to see that the datacontext is a dbcontext.
How can I use the scaffolder with my datacontext?
Why don't you use Composition?
If your feature really is just needed as lets say a few methods needed in those objects I would put those methods in a separate class called ContextDetails - something along those lines and have DataContext contain a ContextDetails like so:
//Changed MyContext to ContextDetails
public class ContextDetails
{
public void MyFeature()
{
//Do something
}
}
public class DataContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
public ContextDetails DbDetails { get; set; }
}
And if the ContextDetails object needs information about the DataContext/DbContext it's in pass the DataContext/DbContext into a method or even the constructor.
If you don't like Composition for this problem maybe you want to use an Interface. If that's the case check out http://www.codeproject.com/Tips/309753/Repository-Pattern-with-Entity-Framework-4-1-and-C
The context class must inherit from System.Data.EntityDbContext which provides facilities for querying and working with entity data as objects
The best reason I could find for why the inheritance is not working in your example.
EDIT:
I read my answer and realized DBDetails might not be the best name but you get the idea. Extract the implementation and use it as a separate entity. Good luck!
i think first you should install entity framework 4.0 then i think definitely it's working please try this.

What is the best way to create EF DbContext instance for ASP.NET MVC

In order to support lazy loading feature in EF, what is the best way to instantiate DbContext?
I know HttpContext's current item is good place to create DbContext via Application_BeginRequest method and Application_EndRequest method, but in some sample codes of MSDN and official asp.net mvc site, they just create DbContext in Controller's constructor and dispose it in controller's Dispose() method.
I think the both ways are not too different because all of those all implement session per request pattern.
I just want to make sure that my understanding is correct or not.
The Dispose() method in the controller isn't always reliable. By the same token, Session is probably not a good idea either. "Best" is probably subjective, but we've had the best success by using dependency injection (Castle Windsor) and following a Unit of Work Repository pattern.
Setup the unit of work along the following lines:
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork()
{
this.Context = new MyEFEntities();
this.Context.ContextOptions.LazyLoadingEnabled = true;
}
public void Dispose()
{
this.Context.Dispose();
}
public ObjectContext Context { get; internal set; }
}
Setup your repository:
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
public Repository(IUnitOfWork unitOfWork)
{
Context = unitOfWork.Context;
ObjectSet = Context.CreateObjectSet<TEntity>();
}
public ObjectContext Context { get; set; }
public IObjectSet<TEntity> ObjectSet { get; set; }
}
Register with Castle in Global.asax:
void Application_Start()
{
this.Container.Register(
Component.For<IUnitOfWork>()
.UsingFactoryMethod(() => new UnitOfWork())
.LifeStyle
.Is(LifestyleType.PerWebRequest)
);
ControllerBuilder.Current.SetControllerFactory(
new WindsorControllerFactory(this.Container));
}
And use in your controller (or wherever you're using it, as long as it's injectable):
public class SomeController
{
public SomeController(IRepository<MyEntity> repository)
{
this.Repository = repository;
}
public IRepository<MyEntity> Repository { get; set; }
public ActionResult MyAction()
{
ViewData.Model = this.Repository.ObjectSet.Single(x => x.Condition); //or something...
}
}
Any lazy loading here could potentially be a trap for a future issue. Without DI, without a repository - its hard to see anything working without it being a hack for lazy loading. Also do you you plan on passing your entities to your view. If so this is going to create a bad overlap. The controller should package data for your view, not have things evaluated later in your view.
For MVC best practices, you should flatten out your domain model as much as possible into a viewmodel (if flattening makes sense) and use the view model. Since you would ideally then know what would be lazy loaded, it may make more sense to take the hit up front and use .Include() in your query to eager load, otherwise you can issue many many queries to the database.
I've used a session factory pattern and saved the DBContext in the session object. It will stay open per session. I haven't had problems with it so far.

Inject strongly typed instance with structuremap

I have this IPageModel interface which is the base for all my models in my project.
The current model is a part of the RouteData and I want to inject this instance to my controllers.
this is how I do it today
x.For<IPageModel>().UseSpecial(y => y.ConstructedBy( r => ((MvcHandler) HttpContext.Current.Handler).RequestContext.RouteData.GetCurrentModel<IPageModel>(‌​)));
Is it possible to tell structuremap to inject the correct type instead of the IPageModel?
An couple examples would be like this:
public HomeController(Home model) {
// Home implements IPageModel
}
and
public PageController(Page model) {
// Page implements IPageModel
}
The RouteData object has the correct instance of the model
You can make your controllers generic, so they work with a specific type of IPageModel.
so you would have
HomeController<Home> and
PageController<Page>
If you make them derive from a single base class which only has a constructor with 1 parameter (an instance of T) so:
public abstract class BaseController<T> where T : IPageModel
{
protected T Model { get; private set; }
public BaseController(T model)
{
Model = model;
}
This way you will get a controller with a newly constructed instance of (for example) Home. I don't think you want an empty model but you'll have to handle this in your registrations in structuremap by scoping them on the session or request for example.

Resources