Pregunta

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?

¿Fue útil?

Solución

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>(); 
}
Licenciado bajo: CC-BY-SA con atribución
scroll top