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?

  • '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 May 30 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. – Shrodinger May 30 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 May 30 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 May 30 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. – Shrodinger May 30 at 13:59
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>(); 
}
| improve this answer | |
  • And to log an event in my controller I add a reference to log project and call Logger.Log? – Shrodinger May 30 at 17:39
  • Yes, that's correct – Fabio May 30 at 21:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.