How to log SQL generated by EF using log4net - c#

In my web project I'm using EF6 and I'd like to log generated SQL for debugging purpose.
I'm also using log4net to handle logs, so I'm looking for a way to integrate them together.
What's the correct way to achieve this?

At the moment I'm using this approach: in my BaseController I have something like this:
public class BaseController
{
protected MyDbContext DataContext { get; set; }
protected readonly ILog logger;
public BaseController()
{
DataContext = new MyDbContext();
logger = LogManager.GetLogger(GetType());
DataContext.Database.Log = (dbLog => logger.Debug(dbLog));
// ...
}
//...
}
I don't know if this is the best way, but it works...

if someone after 2 years still searches for a solution:
the solution of #davioooh led me to something similar (if you probably use Entity Framework not with a WebApi, or don't want to use a BaseController-class):
Just when inheriting from DbContext you could also do this in the constructor:
public class MyContext:DbContext
{
private readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public MyContext()
: base("<connectionstring>")
{
Database.Log = log => _logger.Debug(log);
}
}

Related

Access DBContext in ASP.NET Core 6 from data access layer class

I have an ASP.NET Core 6 MVC app that I created from a VS 2022 template. I'm writing a custom data access layer and business logic layer.
I know I can pass the _context from the controllers down through the BLL in the DAL, however I would prefer to have direct access from the DAL. I don't see any reason the BLL or the web code need to have anything to do with data access.
I've tried several examples of injection, but I can't seem to get any of them to work. Does anyone have a good solution?
Edit:
In Project.cs I have added DbContext:
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
I would like to get access to DbContext in my custom data access layer without having to pass DbContext all the way down from a Controller into my DAL contructor like so:
namespace MyProject.Code
public class CustomDataAccessLayer
{
public string[] GetCustomers()
{
DbConnection conn = DbContext.GetConnection();
//Whatever query logic I want to do here
Is there any way to access DbConext directly without having to pass it in from within a controller action?
Here you can read about how DI works exactly.
I'll provide some example of how you're supposed to do that.
first you need to have DataContext Class that extends DbContext Class that will be like this
using Microsoft.EntityFrameworkCore;
namespace MyProject.Context
{
public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<MyEntity> Entities { get; set; }
}
}
In the Program.cs you need to add theDataContextlike this
builder.Services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer(builder.Configuration
.GetConnectionString("DefaultConnection")); // for example if you're holding the connection string in the appsettings.json
});
and then in the Logic class that you want to use it in make a data member in the class of type DataContext
private readonly DataContext _dataContext;
and your constructor must take DataContext as parameter and Initialize your constructor will look like this
public ClassName(DataContext dataContext)
{
_dataContext = dataContext;
}
you don't need to add dbcontext to a controller, nobody does it if there is a data access layer, DI will automatically inject the context
public class CustomDataAccessLayer
{
private readonly ApplicationDbContext _context;
public CustomDataAccessLayer(ApplicationDbContext context)
{
_context = context;
}
public string[] GetCustomers()
{
//your code
}
}
with json :
var constr = builder.Configuration["ConnectionStrings:Default"];
builder.Services.AddDbContext<AppDbContext>(opt => {
opt.UseSqlServer(constr);
});
Json :
"ConnectionStrings": {
"Default": "Server=Victus;Database=TeamDb;Trusted_Connection=true;"
}
AppDbContext.cs :
public class AppDbContext:DbContext {
public AppDbContext(DbContextOptions <AppDbContext> options) : base(options) { }
public DbSet<team> teams { get; set; }
}
Home Controller :
private AppDbContext _appDbContext;
public HomeController(AppDbContext appDbContext) {
_appDbContext = appDbContext;
}
public IActionResult Index() {
return View(_appDbContext.teams);
}

Ninject nested constructor argument

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>();
}

IOption pattern - unit testing and passing through layers

I have some code (C# .Net Core WebAPI) I wish to unit test but need some help as the dependencies looks a bit odd to me.
The code came from some sample code (I found on the web) for accessing MongoDb using .Net Core WebAPI, which initially looked ok, until now..
Both the DbContext and the Repository have the same dependency - and the Repository just passes it through to the DbContext anyway - as the Repository instantiates the DbContext:
public class LogItemRepository : ILogItemRepository
{
private readonly DbContext _context = null;
public LogItemRepository(IOptions<DbSettings> settings)
{
_context = new DbContext(settings);
}
...
public class DbContext
{
private readonly IMongoDatabase _database = null;
public DbContext(IOptions<DbSettings> settings)
{
var client = new MongoClient(settings.Value.ConnectionString);
if (client != null)
_database = client.GetDatabase(settings.Value.Database);
}
public IMongoCollection<LogItem> LogItemsCollection
{
get
{
return _database.GetCollection<LogItem>("LogItem");
}
}
}
}
I'm not familiar with the Options pattern, but from a quick read it looks good. But I'm not convinced it's good practice to make child dependencies (the options), dependencies of the parent (as in the example above).
Instead should I be making an interface, IDbContext, and using that as the dependency for the repository? That's what I would have done in the past - but not sure if this breaks the options pattern.
I suspect this is subjective, but I'd like some others input.
Thanks
Tim
While primarily opinion based, common practice is to not instantiate the db context within the constructor of the repository. That tightly couples the repository to the context. Inject an abstraction as you stated in your OP.
I may be splitting hairs here but there is still too much tight coupling in the example provided.
First abstract the context
public interface IDbContext {
IMongoCollection<LogItem> LogItemsCollection { get; }
}
and also have IMongoDatabase be an explicit dependency
public class DbContext : IDbContext {
private readonly IMongoDatabase database = null;
public DbContext(IMongoDatabase database)
this.database = database;
}
public IMongoCollection<LogItem> LogItemsCollection {
get {
return database.GetCollection<LogItem>("LogItem");
}
}
}
configure service with what ever options are needed at the composition root (Startup). You would even consider encapsulating it in an extension method.
services.AddScoped<IMongoDatabase>(provider => {
var settings = provider.GetService<IOptions<DbSettings>>();
var client = new MongoClient(settings.Value.ConnectionString);
return client.GetDatabase(settings.Value.Database);
});
services.AddScoped<IDbContext, DbContext>();
services.AddScoped<ILogItemRepository, LogItemRepository>();
//...NOTE: Use the desired service lifetime. This is just an example
That now leaves the repository to be explicitly dependent on the context abstraction
public class LogItemRepository : ILogItemRepository {
private readonly IDbContext context = null;
public LogItemRepository(IDbContext context) {
this.context = context;
}
//...other code
}
All layers are now decoupled and explicitly state what their dependencies are, allowing for more isolated unit tests to be done as needed.

DbContext with Ninject ADO.NET

I am working on a big project that 80% completed (Some features need to be implemented though).But recently we discovered that the project doesn't allow concurrent requests (I mean multiple users request to same repository). Sometime we get null referece & sometimes "Executed can not open available connection , connection state is closed" etc.
Our source code is strongly restricted outside of the world. Here is some code.Let me know if there is any architectural problem, as architectural guys left company. It's using ninject 3.0. I already used InRequestScope() for all manager's repositories but no luck
Update: I am not using any ORM here, I am trying to connect SqlServer through data adapter in my DbContext class
public class DbContext
{
//execute query , nonquery etc using adapter & datatable
//Example
var dt=new DataTable();
_adapter=new _dbfactory.CreateAdapter();
_adapter.Fill(dt);
return dt;
}
//MyController
public class MyController
{
private readonly IMyManager_iMyManager;
public MyController(IMyManager iMyManager){_iMyManager=iMyManager}
public ActionResult Save()
{
_iMyManager.Save()
}
}
// My Manager
public class MyManager:IMyManager
{
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
public MyManager
(
IMyRepository iMyRepository, DbContext dbContext
)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
Public DataTable GetDataTable()
{
try
{
_dbContext.Open();
_iMyRepository.GetDataTable()
}
catch(Exception ex){}
finally{_dbContext.Close()}
}
}
// here is the repository
Public class MyRepository:IMyRepository
{
public _dbContext;
public MyRepository(DbContext dbContext)
{
_dbContext=dbContext;
}
public DataTable GetDataTable()
{ return _dbContext.ExecuteQuery()}
}
Finally Here is our ninject binding
public class NinjectDependencyResolver()
{
var context=new DbContext("someparameter","connectionStrin");
kernel.Bind<IMyManager>().To<MyManager>().WithConstructorArgument("_dbContext",context);
kernel.Bind<IMyRepository >().To<MyRepository >().WithConstructorArgument("_dbContext",context);
}
there can have some typo in my code as I wrote everything in so editor
I think you did this too complicated in Ninject Dependency Resolver.
You shouldn't create DbContext with a new keyword. Instead you should make Ninject to be resolving DbContext in request scope or in thread scope.
To register DbContext you can do it like this:
kernel.Bind<DbContext>().To<MyDbContext>().WithConstructorArgument("someArgument", "someValue").InRequestScope();
kernel.Bind<IMyManager>().To<MyManager>().InRequestScope();
kernel.Bind<IMyRepository>().To<MyRepository>().InRequestScope();
You don't need to precise the constructor argument to DbContext as DbContext is only once registered in the Ninject.
You can also register DbContext to a DbContextProvider class and there you can add some specific logic to resolve object.
Example:
kernel.Bind<DbContext>().ToProvider<MyDbContextProvider>().InRequestScope();
internal class MyDbContextProvider : Ninject.Activation.IProvider
{
public object Create(IContext context)
{
return new MyDbContext("connectionStringArgument";
}
public Type Type { get { return typeof (MyDbContext); } }
}
I hope this helps.
You need to remove this initialization in the MyManager since you pass the initialized DbContext via IoC.
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
You also need to remove the finally block in the GetDataTable in the MyManager class since as a rule of thumb, if the object is initialized via IoC, it should be destroyed by IoC as well.
finally{_dbContext.Close()}
If you are initializing something in the field level then why would you initialize it again from the constructor?
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new DbContext("someParameter","connectionstring");
public MyManager(IMyRepository iMyRepository, DbContext dbContext)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
This may also be a typo. Either remove the _dbContext initialization from the constructor or delegate the task of initialization to the caller of this class.
Multiple initialization can also be the problem. since you are doing dbcontext initialization both in NinjectDependencyResolver() and MyManager. For this you are getting two different exceptions. This is a platform design issue i guess
Two problems:
// My Manager
public class MyManager:IMyManager
{
private readonly IMyRepository _iMyRepository;
DbContext _dbContext=new
DbContext("someParameter","connectionstring");
public MyManager
(
IMyRepository iMyRepository, DbContext dbContext
)
{
_iMyRepository=iMyRepository;
_dbContext=dbContext;
}
The new that is created for the field will be overwritten when the constructor is called.
public class NinjectDependencyResolver()
{
var context=new DbContext("someparameter","connectionStrin");
kernel.Bind<IMyManager>().To<MyManager>().WithConstructorArgument("_dbContext",context);
kernel.Bind<IMyRepository >().To<MyRepository >().WithConstructorArgument("_dbContext",context);
}
You create the context here once and pass it to each object creation. So you are still reusing the context object instead of creating it for each request scope.

Unity IOC sometimes cant resolve dependency

I am having a weird problem using Unity as an IOC container and im out of ideas of what could cause it. I have a service dependency in my webapi controller but it randomly fails to resolve this dependency. Sometimes i have to start my application 3 or 4 times and then it suddenly works again.
The error I am getting is:
Resolution of the dependency failed, type = "Base.WebApi.Controllers.ApiUsersController", name = "(none)". Exception occurred while: while resolving. Exception is: InvalidOperationException - The type IApiUserService does not have an accessible constructor. ----------------------------------------------- At the time of the exception, the container was: Resolving Base.WebApi.Controllers.ApiUsersController,(none) Resolving parameter "apiUserService" of constructor Base.WebApi.Controllers.ApiUsersController(Base.BLL.Services.User.IApiUserService apiUserService) Resolving Base.BLL.Services.User.IApiUserService,(none)
For initializing and registering my types in unity i use the following:
public static void RegisterTypes(IUnityContainer container)
{
var myAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("Base") && !a.FullName.StartsWith("Base.WebApi")).ToArray();
container.RegisterType(typeof(Startup));
container.RegisterTypes(
UnityHelpers.GetTypesWithCustomAttribute<UnityIoCSingletonLifetimedAttribute>(myAssemblies),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled,
null
).RegisterTypes(
UnityHelpers.GetTypesWithCustomAttribute<UnityIoCTransientLifetimedAttribute>(myAssemblies),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.Transient);
}
As you can see i am using singletone and transient named attributes to define the way my dependencies should be resolved.
My controller looks like this:
public class ApiUsersController : ODataController
{
private readonly IApiUserService _apiUserService;
public ApiUsersController(IApiUserService apiUserService)
{
_apiUserService = apiUserService;
}
public IQueryable<ApiUserEntity> Get()
{
return this._apiUserService.GetUsers();
}
}
as you can see it has a dependency on user service which looks like this:
[UnityIoCTransientLifetimed]
public class ApiUserService : BaseService, IApiUserService
{
private readonly IUserRepository _userRepository;
public ApiUserService(IUserRepository userRepository, IUnitOfWork uow) : base(uow)
{
_userRepository = userRepository;
}
}
The api user repository looks like this:
[UnityIoCTransientLifetimed]
public class UserRepository : GenericRepository<ApiUserEntity>, IUserRepository
{
public UserRepository(IUnitOfWork unitOfWork, IDomainContext context) : base(unitOfWork, context)
{
}
Extending the following GenericRepository:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected readonly BaseContext Context;
public GenericRepository(IUnitOfWork unitOfWork, IBaseContext context)
{
// register this repository with the unit of work.
unitOfWork.Register(this);
Context = (BaseContext)context;
}
With my unit of work that looks like this:
[UnityIoCSingletonLifetimed]
public class UnitOfWork : IUnitOfWork
{
private readonly Dictionary<string, IRepository> _repositories;
// unit of work class is responsible for creating the repository and then dispossing it when no longer needed.
public UnitOfWork()
{
_repositories = new Dictionary<string, IRepository>();
}
}
However it sometimes works and sometimes it doesnt and i cant figure out why or where to look.
Finally solved it thanks to some suggestions. Looking at the documentation for
AppDomain.CurrentDomain.GetAssemblies()
it says the following:
Gets the assemblies that have been loaded into the execution context of this application domain.
Which basically means that it only loads the assemblies when they are actually needed.
The way i solved it was by using the more reliable GetReferencedAssemblies which loads all assemblies even if they are not being used.
var allAssemblies = new ReadOnlyCollection<Assembly>(
BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList());
Restarted tons of times and not one resolve crash :) Thanks everyone! For everyone looking for more information check out this SO answer: Difference between AppDomain.GetAssemblies and BuildManager.GetReferencedAssemblies

Categories