I have built a service class that communicates with both an Azure Table Storage Table, and an EntityFrameworkCore InMemory Database.
public sealed class NotificationService : INotificationService
{
private readonly NotificationDatabaseContext _inMemoryCache;
private readonly INotificationRepository _notificationRepository;
public NotificationService(
NotificationDatabaseContext inMemoryCache,
INotificationRepository notificationRepository)
{
_inMemoryCache = inMemoryCache;
_notificationRepository = notificationRepository;
}
public string Test()
{
return DateTime.UtcNow.ToShortTimeString();
}
}
public sealed class NotificationRepository : INotificationRepository
{
private readonly CloudTable _cloudTable;
public NotificationRepository(CloudTable cloudTable)
{
_cloudTable = cloudTable;
}
}
public class NotificationDatabaseContext : DbContext
{
public NotificationDatabaseContext(DbContextOptions<NotificationDatabaseContext> dbContextOptions) : base(dbContextOptions) { }
public DbSet<Notification> Notifications { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Notification>()
.HasKey(x => x.RowKey);
}
}
public sealed class Notification : TableEntity
{
public bool HasBeenRead { get; set; }
}
I am using Autofac for dependency injection.
public sealed class AutofacAzureTableStorageModule : AutofacAzureModuleBase
{
private static string _azureStorageConnectionString;
private const string NOTIFICATIONS_AZURE_TABLE_STORAGE_CLOUD_TABLE_NAME = "Notifications";
public AutofacAzureTableStorageModule(string azureStorageConnectionString)
{
_azureStorageConnectionString = azureStorageConnectionString;
}
protected override void Load(ContainerBuilder containerBuilder)
{
ConfigureAzureStorageAccount(containerBuilder, _azureStorageConnectionString);
ConfigureServicesWithRepositories(containerBuilder);
ConfigureAzureCloudTables(containerBuilder);
ConfigureCloudTablesForRepositories(containerBuilder);
}
private static void ConfigureServicesWithRepositories(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<NotificationService>()
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
private static void ConfigureAzureCloudTables(ContainerBuilder containerBuilder)
{
containerBuilder.Register(x => x.Resolve<CloudStorageAccount>().CreateCloudTableClient());
containerBuilder.Register(x =>
GetCloudTable(x, NOTIFICATIONS_AZURE_TABLE_STORAGE_CLOUD_TABLE_NAME))
.Named<CloudTable>(NOTIFICATIONS_AZURE_TABLE_STORAGE_CLOUD_TABLE_NAME);
}
private static void ConfigureCloudTablesForRepositories(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<NotificationRepository>()
.WithParameter(
(x, y) => x.ParameterType == (typeof(CloudTable)),
(x, y) => y.ResolveNamed<CloudTable>(
NOTIFICATIONS_AZURE_TABLE_STORAGE_CLOUD_TABLE_NAME))
.AsImplementedInterfaces();
}
}
public abstract class AutofacAzureModuleBase : Module
{
protected static void ConfigureAzureStorageAccount(ContainerBuilder containerBuilder, string azureStorageConnectionString)
{
containerBuilder.Register(c => CreateAzureCloudStorageAccount(azureStorageConnectionString));
}
private static CloudStorageAccount CreateAzureCloudStorageAccount(string azureStorageConnectionString)
{
if (String.IsNullOrEmpty(azureStorageConnectionString))
{
throw new Exception("Azure Storage connection string is null!");
}
return CloudStorageAccount.Parse(azureStorageConnectionString);
}
protected static CloudTable GetCloudTable(IComponentContext componentContext, string tableName)
{
var cloudTable = componentContext.Resolve<CloudTableClient>().GetTableReference(tableName);
cloudTable.CreateIfNotExistsAsync().GetAwaiter();
return cloudTable;
}
}
Everything is configured in Program.cs file as follows:
// Add services to the container.
builder.Services.AddControllersWithViews();
// Configure EntityFrameworkCore InMemoryDatabase Cache
var notificationsInMemoryDatabaseName = builder.Configuration["EntityFrameworkCoreInMemoryDatabase:NotificationsDatabaseName"];
builder.Services.AddDbContext<NotificationDatabaseContext>(options =>
options.UseInMemoryDatabase(notificationsInMemoryDatabaseName));
// Configure EntityFrameworkCore InMemoryDatabase Cache
// Configure Autofac Modules
var azureStorageConnectionString = builder.Configuration["AzureStorage"];
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Register services directly with Autofac here.
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new AutofacAzureTableStorageModule(azureStorageConnectionString));
});
// Configure Autofac Modules
var app = builder.Build();
The Autofac dependency injection isn't correct (it worked previously when I was simply using DI for an Azure Storage Table (without the EntityFrameworkCore InMemory Database)).
Please could someone advise/help me refactor the Autofac dependency injection, to work with the InMemory Database?
UPDATE:
The following exception is thrown:
I've managed to fix it by adding the following method to the Autofac module.
private static void ConfigureEntityFrameworkCore(ContainerBuilder containerBuilder)
{
containerBuilder.Register(c => new DbContextOptionsBuilder<NotificationDatabaseContext>()
.UseInMemoryDatabase(_entityFrameworkCoreInMemoryDatabaseNameForNotifications)
.Options)
.SingleInstance();
containerBuilder.RegisterType<NotificationDatabaseContext>()
.WithParameter(
(x, y) => x.ParameterType == typeof(DbContextOptions<NotificationDatabaseContext>),
(x, y) => y.Resolve<DbContextOptions<NotificationDatabaseContext>>())
.InstancePerLifetimeScope();
}
Hopefully this helps someone in future.