the way im binding my services with nested constructors is working but it seems like there's a better way to inject these nested constructors.
Please tell me what is the proper way to do it for this kind of scenario, also if there is something wrong with the way I layer my application kindly point it out also thank you!
Please check the codes below:
AuthorController.cs
public class AuthorsController : Controller
{
IAuthorService authorService;
public AuthorsController(IAuthorService _authorService)
{
authorService = _authorService;
}
}
AuthorService.cs
public class AuthorService :IAuthorService
{
IAuthorRepository repo = null;
public AuthorService(IAuthorRepository _authorRepository)
{
repo = _authorRepository;
}
}
AuthorRepository
public class AuthorRepository : IAuthorRepository
{
MyContext Context = null;
public AuthorRepository(MyContext _context)
{
Context = _context;
}
}
NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel)
{
MyContext db = new MyContext();
//this is where i have my doubts
kernel.Bind<IAuthorService>().ToConstructor(x =>
new AuthorService(new AuthorRepository(db))
);
}
EDIT: after several tries I found another way to achieve my goal which i think is much cleaner(note: I dont know how it automatically find the MyContext parameter of AuthorRepository but it does)
2nd Way:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IAuthorService>().To<AuthorService>();
kernel.Bind<IAuthorRepository>().To<AuthorRepository>();
}
Related
I'm making an ASP.NET Core Razor Pages web application. In my application I use the following code:
public class MyClass
{
private readonly ApplicationDbContext _dbContext;
private readonly ICalendarService _calendarService;
public MyClass(ApplicationDbContext dbContext, ICalendarService calendarService)
{
_dbContext = dbContext;
_calendarService = calendarService;
}
public void MyFunction()
{
// here I need to use _dbContext and _calendarService
}
But when I use this class I need to do the following:
public class MySecondClass
{
private ImportIntoCalendar ImportHintSchedule;
public MySecondClass()
{
MyClass= new MyClass(_dbContext, _calendarService);
}
// Do something with variable ImportHintSchedule
ImportHintschedule.Function()
}
Everytime I need to add the dbcontext and the calendarservice into the parameters. So both need to be available in the other class. This feels like I'm doing something stupid, like I'm duplicating the same step. Does anybody know a better way to do this. Or is this just fine?
Edit:
I have this line in my startup.cs
services.AddScoped<ICalendarService, CalendarService>();
In your ConfigureServices you can add the IOC scopes.
For example, something like this. I don't know all of your code so the is just an example.
services.AddScoped<ICalendarService, CalendarService>();
services.AddScoped<IApplicationDbContext, ApplicationDbContext>();
You can also add singletons if that meets your needs as well.
Here is an example singleton call I use in my application
services.AddSingleton<IRepository<BaseItem>>(x => new Repository<BaseItem>(Configuration["MongoConnection:DefaultConnection"]));
I would suggest to create an Interface of your class, something like:
public interface IMyClass {
void MyFunction();
}
Then, implement that in your class:
public class MyClass : IMyClass {
private readonly ApplicationDbContext _dbContext;
private readonly ICalendarService _calendarService;
public MyClass(ApplicationDbContext dbContext, ICalendarService calendarService)
{
_dbContext = dbContext;
_calendarService = calendarService;
}
public void MyFunction()
{
// here I need to use _dbContext and _calendarService
}
}
And the add that to injector:
public void ConfigureServices(IServiceCollection services)
{
// existing code
services.AddTransient<IMyClass, MyClass>();
}
and finally use IMyClass in Controller constructor.
public class MyController:Controller
{
private IMyInterface _myClass;
public MyController(IMyInterface myclass) {
_myClass = myClass;
}
public IActionResult MyAction() {
_myClass.MyFunction();
return View();
}
}
I've been trying to implement a .net MVC Unit of Work API (rather than creating a separate repository), but it doesn't feel right. Am I going about this the correct way?
BaseController
public class BaseController : ApiController
{
protected DBEntities _dbEntities;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
_dbEntities = new DBEntities();
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
_dbEntities.SaveChanges();
}
}
MyController
public class MyController : BaseController
{
public HttpResponseMessage PutMyObject(int id, int id2)
{
if (id != 0)
{
var myObject = _dbEntities.MyObjects.Where(x => x.id == id);
if (myObject.Count() > 0)
{
MyObject temp = myObject.SingleOrDefault();
temp.Processed = true;
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound);
}
}
else
{
/* do some other stuff */
}
}
}
My thinking around this is that the controller action is a single Unit of Work. The database connection is opened when the controller action starts, and SaveChanges is called once the response is returned.
Am I going about this in the correct manner? do i need to dispose of _dbentities after savechanges is called within the BaseController? (or is this a better question for code review exchange)
I think, It would be a bad design for OOP architecture and flexibility. Because, you have to provide database actions in your main MVC project and you couldn't provide them in other layers. So, you should provide repository pattern and you should access the repositories from main MVC project and other required projects. So, I suggest you to handle _dbEntities object in a singleton class for per thread.
Sample singleton class would be like this;
public class UnitOfWorkSampleContextBase : IDisposable
{
[ThreadStatic]
private static UnitOfWorkSampleContextBase _instance;
public static UnitOfWorkSampleContextBase Instance
{
get
{
if (_instance == null)
{
_instance = new UnitOfWorkSampleContextBase();
}
return _instance;
}
}
public SampleDbContext Context { get; private set; }
private UnitOfWorkSampleContextBase()
{
}
public void Commit()
{
Context.SaveChanges();
}
public void ResolveContext()
{
Context = new SampleDbContext(ConfigurationManager.ConnectionStrings["MainDatabase"].ConnectionString);
}
public void Dispose()
{
Context.Dispose();
Context = null;
}
}
And you can create the DbContext like this;
UnitOfWorkSampleContextBase.Instance.ResolveContext();
Then, you can perform actions in context like this;
var context = UnitOfWorkSampleContextBase.Instance.Context;
var records = context.sampleEntities.ToList();
Finally, you can commit and dispose the context like this;
UnitOfWorkSampleContextBase.Instance.Commit();
UnitOfWorkSampleContextBase.Instance.Dispose();
The singleton class should be in repository or base layer to be accessed desired repository classes.
Note : If you are using CastleWindsor or something like that, creating
and commiting the context would be better in an interceptor. Also, it is very important that the singleton context class should be initialized for per thread.
I have a problem with EF5. I am using MVC 4.5
I am trying to make the 1 context per request "pattern".
I am not using "unit of work" pattern, nor testing or DI.
I am using the Generic Repository pattern to interact with DB. Each repository uses the same context mantained by a singleton "DataContextManager".
In each request in the global asax I refresh the context, but something wrong is happening: ie: I have a paged list and moving by the pages if i change data in DB manualy it doesnt refresh correctly. It's not an HTML cache issue, i tested it.
I know is a EF Context problem because i have "something like this":
private static Context C; //for the singleton. And in global.asax
public Application_BeginRequest()
{
DataContextManager.RefreshNew();
}
protected void Application_EndRequest(object sender, EventArgs e)
{
Domain.DataContextManager.Dispose();
}
And the first time the list works and in the second page i get an error saying that the
Context is disposed.
I read something of using the context in Static variables, but i don't know whats happening. I would like to use something simple like this, because to implement the UnitOfWork pattern i will need to change a lot of code.
Here is a little snnipet of my classes:
public class DataContextManager
{
private static Entities _Context;
private const string ConnectionString = "connString";
public static Entities Context
{
get
{
if (DataContextManager._Context == null)
DataContextManager._Context = new Entities(ConfigurationManager.ConnectionStrings[ConnectionString].ConnectionString);
return DataContextManager._Context;
}
}
//This method is not necessary but made it for testing
public static void RefreshNew()
{
DataContextManager._Context = new Entities(ConfigurationManager.ConnectionStrings[ConnectionString].ConnectionString);
}
public static void Dispose()
{
if (DataContextManager._Context != null)
{
DataContextManager._Context.Dispose();
DataContextManager._Context = null;
}
}
}
And repositories use DataContextManager like this:
public class BaseRepository<TEntity> where TEntity : class
{
internal Entities context;
internal DbSet<TEntity> dbSet;
public BaseRepository()
: this(DataContextManager.Context)
{
}
public BaseRepository(Entities context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
Thanks in advance!
Pablo.
I don't know that you really need the manager class, since everything seems to just access the DbContext property.
That being said, you should normally avoid using DbContext in a static class; I don't have any canonical source to back that up, but my personal experience has been that it at some point causes more problems than any benefit having a static class provides. So, I'd update the manager like so:
public class DataContextManager
{
private readonly string connectionToUse = string.Empty;
private Entities _context;
public Entities Context
{
get
{
if (_context == null)
{
_context = new Entities(WebConfigurationManager.ConnectionStrings[connectionToUse].ConnectionString);
}
return _context;
}
}
public DataContextManager()
{
connectionToUse = "connString";
}
public DataContextManager(string key)
{
connectionToUse = key;
}
#region IDisposable Members
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposeAll)
{
if (disposeAll)
{
_context.Dispose();
}
_context = null;
}
#endregion
}
Then you simply add a protected field to each of your controllers and instantiate the manager in the controller's constructor:
protected DataContextManager Manager = null;
public HomeController()
{
Manager = new DataContextManager();
// or
//
//__manager = new DataContextManager("connection-To-Use");
}
If all of your controllers use the same DbContext class, you could create a BaseController class that inherits System.Web.Mvc.Controller and move the manager into it, which saves some duplication.
I have this in my Application_Start:
var crumbsCache = new MemoryCache("breadCrumbsNames");
var crumbsList = new List<CacheItem>
{
//list of new CacheItem();
};
foreach (var cacheItem in crumbsList)
{
crumbsCache.Add(cacheItem, new CacheItemPolicy());
}
Now, in my controllers i am doing this:
var cache = new MemoryCache("breadCrumbsNames");
var cacheItem = cache.GetCacheItem("nameOfCacheItem");
But then cacheItem is always null, what am I doing wrong?
I think a better option for you would be to use Ninject or some other dependency injection framework to inject your MemoryCache into the controllers as needed.
You will begin by adding Ninject and Ninject.Mvc3 (and any other related bits) to your ASP.NET MVC project. If you are working in Visual Studio, you can use NuGet to do that. It is quite painless and well-automated.
The next step will be to wrap your MemoryCache into some kind of a interface, such as:
public interface IMemoryCacheService
{
MemoryCache MemoryCache
{
get;
set;
}
}
And:
public class MemoryCacheService : IMemoryCacheService
{
public MemoryCacheService()
{
MemoryCache = new MemoryCache();
}
public MemoryCache MemoryCache
{
get;
set;
}
}
Then you define a binding within Ninject so that Ninject knows that when you need something of type IMemoryCacheService, it should give you the instance of MemoryCacheService.
I will paste my own Ninject config class here. The one that will be created in your project will be very similar and will be in a folder called App_Start (which will be created automatically if you use NuGet). The class that Ninject creates by default is called NinjectWebCommon.
public static class NinjectConfig
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
public static void Stop()
{
bootstrapper.ShutDown();
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>()
.ToMethod(context => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>()
.To<HttpApplicationInitializationHttpModule>();
kernel.RegisterServices();
return kernel;
}
private static void RegisterServices(this IKernel kernel)
{
kernel.Bind<IMemoryCacheService>()
.To<MemoryCacheService>()
.InSingletonScope();
// InSingletonScope() is important so Ninject knows
// to create only one copy and then reuse it every time
// it is asked for
// ignore the stuff below... I have left it in here for illustration
kernel.Bind<IDbTransactionFactory>()
.To<DbTransactionFactory>()
.InRequestScope();
kernel.Bind<IDbModelContext>()
.To<DbModelContext>()
.InRequestScope();
kernel.Bind<IDbModelChangeContext>()
.To<DbModelChangeContext>()
.InRequestScope();
kernel.Bind<IUserContext>()
.To<UserContext>()
.InRequestScope();
kernel.BindAttributeAndFilter<IgnoreNonAjaxRequestsFilter, IgnoreNonAjaxRequestsAttribute>();
kernel.BindAttributeAndFilter<ProvideApplicationInfoFilter, ProvideApplicationInfoAttribute>();
kernel.BindAttributeAndFilter<ProvideSessionInfoFilter, ProvideSessionInfoAttribute>();
kernel.BindAttributeAndFilter<UseDialogLayoutFilter, UseDialogLayoutAttribute>();
kernel.BindAttributeAndFilter<CheckResourceAccessFilter, CheckResourceAccessAttribute>();
kernel.BindAttributeAndFilter<CheckResourceStateFilter, CheckResourceStateAttribute>();
}
private static void BindAttributeAndFilter<TFilter, TAttribute>(this IKernel kernel)
{
kernel.BindFilter<TFilter>(FilterScope.Action, null)
.WhenControllerHas<TAttribute>();
kernel.BindFilter<TFilter>(FilterScope.Action, null)
.WhenActionMethodHas<TAttribute>();
}
}
Finally, your controllers will change from:
public class HomeController : Controller
{
public ActionResult Foo()
{
...
}
...
}
to:
public class HomeController : Controller
{
private IMemoryCacheService memoryCacheService;
public HomeController(IMemoryCacheService memoryCacheService)
{
this.memoryCacheService = memoryCacheService;
}
public ActionResult Foo()
{
// use this.memoryCacheService in your controller methods...
}
...
}
Say, you made another service as well called IEmailService following the above-mentioned strategy, and you wanted IEmailService to be available in HomeController as well, then:
public class HomeController : Controller
{
private IMemoryCacheService memoryCacheService;
private IEmailService emailService;
public HomeController(IMemoryCacheService memoryCacheService, IEmailService emailService)
{
this.memoryCacheService = memoryCacheService;
this.emailService = emailService;
}
public ActionResult Foo()
{
// use this.memoryCacheService in your controller methods...
// and also use this.emailService in your controller methods...
}
...
}
Ninject will change the ASP.NET MVC controller factory to automatically provide the injected arguments to the controller constructors.
I think this sort of approach is better in the long run that keeping global variables, etc.
You are creating a new instance of MemoryCache in each controller. Since it is new there is nothing in it which is why you values are always null. You need to access the same instance that you created in Application_Start. Look into using MemoryCache.Default.
I have been researching about the unit-of-work and the repository pattern in C#. AFAIK, DataContext implements the unit of work pattern, and can be used to implement a repository interface. One last piece that's lacking in DataContext is a way to share this resource globally within a predefined scope.
I briefly looked at NCommon as a solution to this missing piece. Would you share your experience in NCommon, or recommend other solutions? Please also correct me if I have misunderstood. Thanks.
I always worked with DataContext in the same way NHibernate does: Having a static way to get it and different storages for storing it. For instance, it could be stored in the HttpContext.Current.Items collection for a web based application, or in Call.Context for the unit tests. When do you create the instance and when do you close it will depend on the scenario. Again, for web it make sense to do it on the Request_begin and Request_end events of your application. For unit tests, maybe on the setup and teardown.
Hope it helps.
Edit: Here's some implementation
public abstract class DataContextProvider
{
public abstract DataContext GetCurrent();
public abstract void OpenNew();
public void CloseCurrent()
{
var current = GetCurrent();
current.Dispose();
}
}
In your data context you add this:
public static DataContextProvider Provider { private get; set; }
public static DataContext Current { get { return Provider.GetCurrent(); } }
For web:
In your web project you put this class:
public class WebDataContextProvider : DataContextProvider
{
private const string Key = "WebDataContextProvider.DataContext";
public override DataContext GetCurrent()
{
return (DataContext)HttpContext.Current.Items[Key];
}
public override void OpenNew()
{
HttpContext.Current.Items[Key] = new DataContext();
}
}
And in your global.asax:
You add a field of type WebDataContextProvider:
WebDataContextProvider dataContextProvider = new WebDataContextProvider();
You override the application start event for doing:
DataContext.Provider = dataContextProvider;
In your Request Begin event you put:
dataContextProvider.OpenNew();
And in your Request End event you put:
dataContextProvider.CloseCurrent();
For test
For your test projects you can follow the same logic, but creating a TestDataContextProvider, like:
public class WebDataContextProvider : DataContextProvider
{
[ThreadStatic]
private static DataContext Current;
public override DataContext GetCurrent()
{
return Current;
}
public override void OpenNew()
{
Current = new DataContext();
}
}
And in your open and close the data context in your SetUp and TearDown methods, and configuring the "current provider" in the constructor of the Test or the TestFixtureSetUp
Hope it helps.