Autofac parameter resolving from Model into Repository - autofac

How can I set parameter from Model: DatabaseName into MyRepository constructor during Autofac container registration?
Here is code sample:
public class ModelBase
{
public string DatabaseName;
}
public interface IRepository<TEntity>
where TEntity : ModelBase
{
}
public class MyRepository<TEntity> : IRepository<TEntity>
where TEntity : ModelBase
{
private readonly string _connectionString;
public MyRepository(string databaseName)
{
_connectionString = string.Format("DatabaseName='{0}';...", databaseName);
}
}
class Program
{
static void Main(string[] args)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterGeneric(typeof(MyRepository<>)).As(typeof(IRepository<>));
IContainer container = containerBuilder.Build();
var someRepository = container.Resolve<IRepository<ModelBase>>();
}
}

As far as I understand, you can try to extract value of DatabaseName from the generic argument of the repository using kinda code:
public class ModelBase
{
public virtual string DatabaseName { get; } = "MyDataBase";
}
public class SubModelBase : ModelBase
{
public override string DatabaseName { get; } = "AnotherDataBase";
}
public interface IRepository<TEntity>
where TEntity : ModelBase
{
}
public class MyRepository<TEntity> : IRepository<TEntity>
where TEntity : ModelBase
{
private readonly string _connectionString;
public MyRepository(string databaseName)
{
_connectionString = string.Format("DatabaseName='{0}';...", databaseName);
}
}
class Program
{
static void Main(string[] args)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterGeneric(typeof(MyRepository<>)).As(typeof(IRepository<>)).WithParameter((p, ctx) => true, (p, ctx) =>
{
var repType = p.Member.DeclaringType;
var genericParamType = repType.GenericTypeArguments[0];
var prop = genericParamType.GetProperty("DatabaseName");
var databaseName = prop.GetValue(Activator.CreateInstance(genericParamType));
// or for static DatabaseName
// var databaseName = prop.GetValue(null);
return databaseName;
});
IContainer container = containerBuilder.Build();
var someRepository = container.Resolve<IRepository<ModelBase>>();
var anotherRepository = container.Resolve<IRepository<SubModelBase>>();
}
}
Notice that DatabaseName must be determined for default ModelBase instance and type must have parameterless constructor. Or you can make DatabaseName static and make it assign it at compile time. In this case you can call var databaseName = prop.GetValue(null);.

Related

With LightInject, how can I pass arguments to child dependencies without registering a bunch of factories?

In the code below, I am trying to inject a ViewModel into a View, while the ViewModel requires a Model to wrap and another service that is in the container. The Model is not registered as it is not really a "service".
How do I:
a) not have to provide the IService instance as an argument (let the container resolve it),
b) not have to register a factory for my ViewModels (there will be many)
So what I'm really asking the container to do is treat my Model (that I pass as an argument) as if it were a registered "service" for the duration of this call to GetInstance.
If this is not possible with LightInject, are there any containers out there that have something like this?
public static class Program
{
public static void Main()
{
var container = new LightInject.ServiceContainer();
var service = new Service1();
container.RegisterInstance<IService>(service);
// Have to register the factory
container.Register<IService, PersonModel, PersonViewModel>(
(f, s, p) => new PersonViewModel(s, p));
container.Register<View>();
var person = new PersonModel(); // this is contextual -- not a service.
object view = CreateView(container, typeof(View), service, person);
// ultimate desired code:
//var view = container.GetInstance(typeof(View), new object[] { person });
}
private static object CreateView(ServiceContainer container, Type viewType, IService service, object model)
{
var ctor = viewType.GetConstructors()[0];
var parameters = new List<object>();
foreach (var param in ctor.GetParameters())
{
var attr = param.GetCustomAttributes(typeof(ModelAttribute), false).FirstOrDefault();
if (model != null && attr != null)
{
parameters.Add(model);
}
else
{
parameters.Add(container.GetInstance(param.ParameterType, new object[] { service, model }));
}
}
return Activator.CreateInstance(viewType, parameters.ToArray());
}
}
public interface IService
{
}
public class Service1 : IService
{
}
public class PersonModel
{
}
public class PersonViewModel
{
public PersonModel PersonModel { get; set; }
public PersonViewModel(IService service, [Model] PersonModel person)
{
PersonModel = person;
}
}
public class View
{
public PersonViewModel PersonViewModel { get; set; }
public View(PersonViewModel vm)
{
PersonViewModel = vm;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class ModelAttribute : Attribute
{
}
I have solved the issues with a combination of techniques...
a) use a Scope and register the ViewModel and View with PerScopeLifetime.
b) use a "ModelTracker" registered with a factory to allow an instance not created by the container to be injected (since models will be created by client code or a DbContext).
This combination also allows me to not register a factory for every ViewModel type -- but instead use the built-in mass registration functions (like RegisterAssembly).
public static class Program
{
public static void Main()
{
var container = new LightInject.ServiceContainer();
container.RegisterInstance<IService>(new Service1());
container.Register<View>(new PerScopeLifetime());
container.Register<PersonViewModel>(new PerScopeLifetime());
container.Register<ModelTracker>(new PerScopeLifetime());
container.Register<PersonModel>((f) => (PersonModel)f.GetInstance<ModelTracker>().Instance);
using (var scope = container.BeginScope())
{
var tracker = scope.GetInstance<ModelTracker>();
tracker.Instance = new PersonModel() { Name = "person1" };
var view = scope.GetInstance<View>();
}
}
}
public class ModelTracker
{
public object Instance { get; set; }
}
public class PersonModel
{
public string Name { get; set; }
}
public class PersonViewModel
{
private readonly IService service;
private readonly PersonModel person;
public PersonViewModel(IService service, PersonModel person)
{
this.service = service;
this.person = person;
}
}
public class View
{
public PersonViewModel PersonViewModel { get; set; }
public View(PersonViewModel vm)
{
PersonViewModel = vm;
}
}
public interface IService { }
public class Service1 : IService { }

Service Fabric Autofac how to?

I'm trying to configure IoC (concept I'm not very familiar with yet) in my SF in a stateful service as explained here : https://www.codeproject.com/Articles/1217885/Azure-Service-Fabric-demo and here : https://alexmg.com/posts/introducing-the-autofac-integration-for-service-fabric.
in program.cs - main:
var builder = new ContainerBuilder();
builder.RegisterModule(new GlobalAutofacModule());
builder.RegisterServiceFabricSupport();
builder.RegisterStatefulService<Payment>("PaymentType");
using (builder.Build())
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Payment).Name);
Thread.Sleep(Timeout.Infinite);
}
GlobalAutofacModule :
public class GlobalAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ChargeRepository>().As<IChargeRepository>().SingleInstance();
builder.RegisterType<CustomerRepository>().As<ICustomerRepository>().SingleInstance();
builder.RegisterType<InvoiceItemRepository>().As<IInvoiceItemRepository>().SingleInstance();
builder.RegisterType<PlanRepository>().As<IPlanRepository>().SingleInstance();
builder.RegisterType<ProductRepository>().As<IProductRepository>().SingleInstance();
builder.RegisterType<SourceRepository>().As<ISourceRepository>().SingleInstance();
builder.RegisterType<SubscriptionRepository>().As<ISubscriptionRepository>().SingleInstance();
builder.RegisterType<TokenRepository>().As<ITokenRepository>().SingleInstance();
}
}
the service is called without problems
public Payment(StatefulServiceContext context,
IChargeRepository chargeRepo,
ICustomerRepository customerRepo,
IInvoiceItemRepository invoiceItemRepo,
IPlanRepository planRepository,
IProductRepository productRepo,
ISourceRepository sourceRepo,
ISubscriptionRepository subscriptionRepo,
ITokenRepository tokenRepo)
: base(context)
{ ... }
in one of it's methodes it needs to call a custom mapper (error on missing params)
var test = new Mapper().GetProductsDto(false, false);
the class is defined like this :
private readonly IChargeRepository _chargeRepo;
private readonly ICustomerRepository _customerRepo;
private readonly IInvoiceItemRepository _invoiceItemRepo;
private readonly IPlanRepository _planRepo;
private readonly IProductRepository _productRepo;
private readonly ISourceRepository _sourceRepo;
private readonly ISubscriptionRepository _subscriptionRepo;
private readonly ITokenRepository _tokenRepo;
public Mapper(IChargeRepository chargeRepo,
ICustomerRepository customerRepo,
IInvoiceItemRepository invoiceItemRepo,
IPlanRepository planRepository,
IProductRepository productRepo,
ISourceRepository sourceRepo,
ISubscriptionRepository subscriptionRepo,
ITokenRepository tokenRepo)
{
_chargeRepo = chargeRepo;
_customerRepo = customerRepo;
_invoiceItemRepo = invoiceItemRepo;
_planRepo = planRepository;
_productRepo = productRepo;
_sourceRepo = sourceRepo;
_subscriptionRepo = subscriptionRepo;
_tokenRepo = tokenRepo;
}
public IEnumerable<ProductListDto> GetStripeProductsDto(bool isLogged, bool isSubscriber) {...}
So how do I instantiate the mapper and call the method without passing every repo as params ?
EDIT: tmp solution until approuved/disapprouved
private static void Main()
{
try
{
using (ContainerOperations.Container)
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Payment).Name);
Thread.Sleep(Timeout.Infinite);
}
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}
public class ContainerOperations
{
private static readonly Lazy<IContainer> _containerSingleton =
new Lazy<IContainer>(CreateContainer);
public static IContainer Container => _containerSingleton.Value;
private static IContainer CreateContainer()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new GlobalAutofacModule());
builder.RegisterServiceFabricSupport();
builder.RegisterStatefulService<Payment>("Inovatic.SF.Windows.PaymentType");
return builder.Build();
}
}
public class GlobalAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//builder.RegisterType<Mapper>();
builder.RegisterType<ChargeRepository>().As<IChargeRepository>().SingleInstance();
builder.RegisterType<CustomerRepository>().As<ICustomerRepository>().SingleInstance();
builder.RegisterType<InvoiceItemRepository>().As<IInvoiceItemRepository>().SingleInstance();
builder.RegisterType<PlanRepository>().As<IPlanRepository>().SingleInstance();
builder.RegisterType<ProductRepository>().As<IProductRepository>().SingleInstance();
builder.RegisterType<SourceRepository>().As<ISourceRepository>().SingleInstance();
builder.RegisterType<SubscriptionRepository>().As<ISubscriptionRepository>().SingleInstance();
builder.RegisterType<TokenRepository>().As<ITokenRepository>().SingleInstance();
}
}
call is now like this : var productListDto = Mapper.GetStripeProductsDto(isLogged, false);
mapper:
private static IProductRepository _productRepo => ContainerOperations.Container.Resolve<IProductRepository>();
public static IEnumerable<ProductListDto> GetStripeProductsDto(bool isLogged, bool isSubscriber)
{
var productList = _productRepo.GetAllStripeProducts().ToList();
I think you should also register Mapper class in IoC container and add it to Payment's constructor, then container will create Mapper with all required params for you. You can do it calling something like
builder.RegisterType<Mapper>().SingleInstance();

Autofac register multiple implementation from a single interface

How do I register this on Autofac given the code snippet below?
public interface IService
{
IEnumerable<string> GetNames();
}
public class GospelNames : IService
{
public IEnumerable<string> GetNames() { return new List<string>{ "John", "Mark" };}
}
public class CommonNames : IService
{
public IEnumerable<string> GetNames() { return new List<string>{ "Ben", "Matt" };}
}
public class WeirdNames : IService
{
public IEnumerable<string> GetNames() { return new List<string>{ "Weird Al" };}
}
public class BaseImplementation : IService
{
private readonly IEnumerable<IService> _services;
public BaseImplementation(params IService[] services)
{
_services = services;
}
public IEnumerable<string> GetNames()
{
var results = new List<string>();
foreach(var service in _services)
{
results.AddRange(service.GetNames());
}
return results;
}
}
I've been reading about decorator pattern and I'm not sure if this is a candidate to transform into that pattern? Or is this a bad practice?

EF migration control logging SQL

I have some code for creating a database and applying migrations:
public static (Server Server, string ConnectionString) InitializeServerAndDatabase(string databaseName, string defaultConnectionConnectionString, DbMigrationsConfiguration migrationsConfiguration)
{
var sqlConnection = new SqlConnection(defaultConnectionConnectionString);
var serverConnection = new ServerConnection(sqlConnection);
var server = new Server(serverConnection);
var database = new Database(server, databaseName);
database.Create();
// Build database with migrations and seed data
var sqlConnectionStringBuilder = new SqlConnectionStringBuilder(defaultConnectionConnectionString);
sqlConnectionStringBuilder.InitialCatalog = databaseName;
var connectionString = sqlConnectionStringBuilder.ToString();
migrationsConfiguration.TargetDatabase = new DbConnectionInfo(connectionString, "System.Data.SqlClient");
var migrator = new DbMigrator(migrationsConfiguration);
var logger = new MigratorLoggingDecorator(migrator, new MinimalMigrationLogger());
logger.Update();
// Set environment variable so the DbContext will establish a connection to the right database
Environment.SetEnvironmentVariable("DefaultConnection", connectionString);
return (server, connectionString);
}
Since running migrations logged a lot more SQL than I wanted, I attempted minimize the logging by writing MinimalMigrationsLogger, which is used in the method above:
public class MinimalMigrationLogger : MigrationsLogger
{
public override void Info(string message)
{
// Ignore it; there's too much of it clogging up CI
}
public override void Verbose(string message)
{
// The SQL text and other info comes here
// Ignore it; there's too much of it clogging up CI
}
public override void Warning(string message)
{
Console.WriteLine(message);
}
}
However, I'm still getting SQL in my logs for creating the table and the seed data. Why does my setup not avoid this? How can I change it so that it will not log table creation and seed data SQL?
Try the following full example and see what's the difference with yours.
All you have to do ,is create the migrations on the same project
using Microsoft.SqlServer.Management.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
namespace EntityFramework.ConsoleExample
{
using Microsoft.SqlServer.Management.Smo;
class Program
{
public static string ServerName = "localhost";
public static string DbName = "EntityFramework.ConsoleExample.MyContenxt";
public static string ConnectionString = #"Server=" + ServerName + "; Database=" + DbName + #";Integrated Security = True;MultipleActiveResultSets=true";
static void Main(string[] args)
{
var conf = new EntityFramework.Console.Migrations.Configuration
{
MigrationsAssembly = typeof(Customer).Assembly
};
InitializeServerAndDatabase(DbName, ServerName, Program.ConnectionString, conf);
System.Console.ReadLine();
}
public static void InitializeServerAndDatabase(string databaseName, string serverName, string defaultConnectionConnectionString, DbMigrationsConfiguration migrationsConfiguration)
{
ServerConnection sqlConnection = new ServerConnection(serverName);
Server sqlServer = new Server(sqlConnection);
Database newDB = new Database(sqlServer, databaseName);
newDB.Create();
migrationsConfiguration.TargetDatabase = new DbConnectionInfo(defaultConnectionConnectionString, "System.Data.SqlClient");
var migrator = new DbMigrator(migrationsConfiguration);
var logger = new MigratorLoggingDecorator(migrator, new MinimalMigrationLogger());
logger.Update();
}
}
public class Customer
{
public int CustomerId { get; set; }
public int Age { get; set; }
public string Name { get; set; }
public string Name2 { get; set; }
public string Name3 { get; set; }
}
public class MyContenxt : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
public class MinimalMigrationLogger : MigrationsLogger
{
public override void Info(string message)
{
System.Console.WriteLine("Info::::" + message);
}
public override void Verbose(string message)
{
//System.Console.WriteLine("Verbose::::" + message);
}
public override void Warning(string message)
{
System.Console.WriteLine("Warning::::" + message);
}
}
}

Calling Stored Procedure in Entity Framework using Aspnet boilerplate

I am using aspnetboilerplate template
i have a student service class. i am getting a student profile List from stored procedure. how can i call a stored procedure in aspnetboilerplate template
public class StudentRepository : TabonoRepositoryBase<User, long>
{
private readonly IActiveTransactionProvider _transactionProvider;
public StudentRepository(IDbContextProvider<TabonoDbContext> dbContextProvider, IActiveTransactionProvider transactionProvider)
: base(dbContextProvider)
{
_transactionProvider = transactionProvider;
}
//TODO: Make async!
public async Task<int> GetProfileCompletePercentage(int studentid)
{
EnsureConnectionOpen();
using (var command = CreateCommand("Sp_GetStudentprofilepercentage", CommandType.StoredProcedure, new SqlParameter("StudentId", studentid)))
{
using (var dataReader = await command.ExecuteReaderAsync())
{
while (dataReader.Read())
{
return Convert.ToInt16(dataReader["TotalPer"].ToString());
}
return 0;
}
}
}
private DbCommand CreateCommand(string commandText, CommandType commandType, params SqlParameter[] parameters)
{
var command = Context.Database.GetDbConnection().CreateCommand();
command.CommandText = commandText;
command.CommandType = commandType;
command.Transaction = GetActiveTransaction();
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
return command;
}
private void EnsureConnectionOpen()
{
var connection = Context.Database.GetDbConnection();
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
}
private DbTransaction GetActiveTransaction()
{
return (DbTransaction)_transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs
{
{"ContextType", typeof(TabonoDbContext) },
{"MultiTenancySide", MultiTenancySide }
});
}
}
And this is the service class
public class StudentService : AsyncCrudAppService<StudentCore, StudentDto, int, PagedResultRequestDto, StudentCreateDto, StudentUpdateDto>, IStudentService
{
public readonly IRepository<StudentCore> _studentRepository;
private readonly UserManager _userManager;
private readonly IStudentService _studentservice;
public StudentService(IRepository<StudentCore> repository, UserManager um, IStudentService studentservice) : base(repository)
{
_studentRepository = repository;
_userManager = um;
_studentservice = studentservice;
}
public Task GetProfileCompletePercentage(int studentid)
{
return _studentservice.GetProfileCompletePercentage(studentid);
}
}
Create an interface:
public interface IStudentRepository : IRepository<StudentCore>
{
Task<int> GetProfileCompletePercentage(int studentid);
}
Implement the interface:
public class StudentRepository : TabonoRepositoryBase<StudentCore>, IStudentRepository
{
// ...
}
Inject the interface and call the method:
public class StudentService : ...
{
private readonly IStudentRepository _studentRepository;
public StudentService(IStudentRepository repository) : base(repository)
{
_studentRepository = repository;
}
public Task GetProfileCompletePercentage(int studentid)
{
return _studentRepository.GetProfileCompletePercentage(studentid);
}
}
Note: StudentService must not inject IStudentService in constructor → infinite recursion!
For reference: https://www.codeproject.com/Articles/1199648/Using-Stored-Procedure-User-Defined-Function-and-V