1

I have an ASP.NET Core Entity Framework Core application and I want to implement my own custom logger. I don't want to use ready solutions like NLog or Serilog.

I added a new class library project ("Logger") to my application and added classes that do the logging. Mostly they call the service provider to get the db context and add a log object to it and then save changes.

The problem is that my db context is located in another project of type class library ("Data"), so "Logger" references "Data" in order to use the db context class. However to be able to save logs in the database I also need the db context to have a db set of type "Log", which would mean a circular dependency.

How do I remedy this situation?

5
  • 'I don't want to use ready solutions like NLog or Serilog.' But why? Making efficient logging into database (or service, which is superior) is not a simple task. Why not use existing solution?
    – Euphoric
    CommentedMay 30, 2020 at 8:35
  • 1
    @Euphoric, because it took me less time to create my own logger than to learn how to setup the configuration for one of those things, let alone implement them properly.CommentedMay 30, 2020 at 9:00
  • If I had a dollar every time someone said that to me, only to realize they have spent hundreds of hours fixing and tuning whatever homegrown solution they made, then I would be quite a rich man.
    – Euphoric
    CommentedMay 30, 2020 at 10:19
  • @Euphoric, or if someone after using some third-party library spends hundreds hours to switch to another third-party library because of missing feature or other issues ;)
    – Fabio
    CommentedMay 30, 2020 at 10:29
  • @Euphoric, fair point, however - counter argument, if every time I was getting involved with a library or technology I knew all the effort I put into it would, in the end, be wasted and I would have been better off writing my own (which in the end usually happens) I'd have so much time on my hands, I'd be able to relive my high school years again.CommentedMay 30, 2020 at 13:59

1 Answer 1

1

Change dependency direction so lower layer (Data) depends on higher layer (Log project)

In "Log" project create all required types and logic to execute logging, but without dependency on the DbContext, instead create an abstraction for saving data.

public class LogItem { public Guid Id { get; set; } public DateTime Time { get; set; } public string Content { get; set; } } public interface ILoggingDatabase { Task Save(LogItem item); } public class Logger { private ILoggingDatabase _database; public Logger(ILoggingDatabase database) { _database = database; } public Task Log(string content) { var item = new LogItem { Time = DateTime.UtcNow, Content = content }; return _database.Save(item); } } 

Then implement database abstraction in "Data" project

public class SqlLoggingDatabase : ILoggingDatabase { private MyDbContext _context; public SqlLoggingDatabase(MyDbContext context) { _context = context; } public async Task Save(LogItem item) { context.LogItems.Add(item); await context.SaveChangesAsync(); } } 

So "Logging" doesn't know about existence of "Data" project, but "Data" project implements/depends on "Logging" projects - no circular dependencies.

Now we need to "glue" them together in our main ASP.NET Core project - which can be called "Entry point" project, this "entry point" project knows or depends on all projects involved in the application.

In Startup class

public void ConfigureServices(IServiceCollection services) { services.AddDbContext(<register your DbContext>); services.AddTransient<Logger>(); services.AddTransient<ILoggingDatabase, SqlLoggingDatabase>(); } 
2
  • And to log an event in my controller I add a reference to log project and call Logger.Log?CommentedMay 30, 2020 at 17:39
  • Yes, that's correct
    – Fabio
    CommentedMay 30, 2020 at 21:37

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.