StructureMap Question - structuremap

This is the equivalent of what I'm trying to create with StructureMap:
new ChangePasswordWithNotificationAndLoggingService(
new ChangePasswordService(
new ActiveDirectoryRepository(new ActiveDirectoryCredentials()),
new TokenRepository("")),
new EmailNotificationService(new PasswordChangedNotification(new UserAccount())),
new LoggingService());
This is what I have right now:
ForRequestedType<IChangePasswordService>()
.TheDefault.Is.ConstructedBy(() =>
new ChangePasswordService(DependencyRegistrar.Resolve<IActiveDirectoryRepository>(),
DependencyRegistrar.Resolve<ITokenRepository>()))
.EnrichWith<IChangePasswordService>(x =>
new ChangePasswordWithNotificationAndLoggingService(x,
DependencyRegistrar.Resolve<INotificationService>(),
DependencyRegistrar.Resolve<ILoggingService>()));
I need to pass the UserAccount to the INotificationService...can't figure it out.
I've tried this:
DependencyRegistrar.With(new UserAccount { Username = "test" });
No luck...UserAccount always turns out null. I don't have to do it all with StructureMap, I'm open to any suggestions.
This is what I currently have working:
public static IChangePasswordService ChangePasswordService(UserAccount userAccount)
{
return new ChangePasswordWithNotificationService(
new ChangePasswordService(ActiveDirectoryRepository(), TokenRepository()),
new EmailNotificationService(new PasswordChangedNotification(userAccount)));
}

Have you tried just using AutoWiring? These are all concrete classes with simple construction so StructureMap can figure out what you need.
For<IChangePasswordService>().Use<ChangePasswordService>();
Looking at your construction I think that this simple configuration might just work.
Edit
Regarding the comments.
You should use the With(T instance) method to have the container construct your IChangePasswordService using the given userAccount.
var userAccount = new UserAccount("derans");
var changePasswordService = container.With(userAccount).GetInstance<IChangePasswordService>();

Why not encapsulate the creation of the change password service into a factory - the factory is then an implemented as StructureMap factory that use a UserAccount passed in and the 'ObjectFactory' to create instances of the IIChangePasswordService as required?
I have demo'ed it below:
namespace SMTest
{
class Program
{
static void Main(string[] args)
{
// bootstrapper...
ObjectFactory.Configure(x => x.AddRegistry(new TestRegistry()));
// create factory for use later (IoC manages this)...
var changePasswordServiceFactory = ObjectFactory.GetInstance<IChangePasswordServiceFactory>();
var daveAccount = new UserAccount("Dave Cox");
var steveAccount = new UserAccount("Steve Jones");
var passwordService1 = changePasswordServiceFactory.CreateForUserAccount(daveAccount);
var passwordService2 = changePasswordServiceFactory.CreateForUserAccount(steveAccount);
}
}
public class TestRegistry : Registry
{
public TestRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
x.AssemblyContainingType(typeof(IChangePasswordService));
x.AssemblyContainingType(typeof(IActiveDirectoryRepository));
x.AssemblyContainingType(typeof(IActiveDirectoryCredentials));
x.AssemblyContainingType(typeof(ITokenRepository));
x.AssemblyContainingType(typeof(INotification));
x.AssemblyContainingType(typeof(INotificationService));
x.AssemblyContainingType(typeof(ILoggingService));
ForRequestedType<ILoggingService>().TheDefault.Is.OfConcreteType<MyLogger>();
ForRequestedType<IActiveDirectoryRepository>().TheDefault.Is.OfConcreteType<MyAdRepository>();
ForRequestedType<IActiveDirectoryCredentials>().TheDefault.Is.OfConcreteType<MyAdCredentials>();
ForRequestedType<ITokenRepository>().TheDefault.Is.OfConcreteType<MyTokenRepository>();
ForRequestedType<IChangePasswordService>().TheDefault.Is.OfConcreteType<ChangePasswordService>();
ForRequestedType<IChangePasswordServiceFactory>().CacheBy(InstanceScope.Singleton).TheDefault.Is.OfConcreteType<StructureMapChangePasswordServiceFactory>();
ForRequestedType<INotification>().TheDefault.Is.OfConcreteType<MyPasswordChangedNotification>();
ForRequestedType<INotificationService>().TheDefault.Is.OfConcreteType<MyEmailNotificationService>();
});
}
}
public interface ILoggingService
{
}
public class MyLogger : ILoggingService
{
}
public class UserAccount
{
public string Name { get; private set; }
public UserAccount(string name)
{
Name = name;
}
}
public interface INotification
{
}
public class MyPasswordChangedNotification : INotification
{
private readonly UserAccount _account;
private readonly ILoggingService _logger;
public MyPasswordChangedNotification(UserAccount account, ILoggingService logger)
{
_account = account;
_logger = logger;
}
}
public interface INotificationService
{
}
public class MyEmailNotificationService : INotificationService
{
private readonly INotification _notification;
private readonly ILoggingService _logger;
public MyEmailNotificationService(INotification notification, ILoggingService logger)
{
_notification = notification;
_logger = logger;
}
}
public interface ITokenRepository
{
}
public class MyTokenRepository : ITokenRepository
{
}
public interface IActiveDirectoryRepository
{
}
public interface IActiveDirectoryCredentials
{
}
public class MyAdCredentials : IActiveDirectoryCredentials
{
}
public class MyAdRepository : IActiveDirectoryRepository
{
private readonly IActiveDirectoryCredentials _credentials;
public MyAdRepository(IActiveDirectoryCredentials credentials)
{
_credentials = credentials;
}
}
public interface IChangePasswordService
{
}
public class ChangePasswordService : IChangePasswordService
{
private readonly IActiveDirectoryRepository _adRepository;
private readonly ITokenRepository _tokenRepository;
private readonly INotificationService _notificationService;
public ChangePasswordService(IActiveDirectoryRepository adRepository, ITokenRepository tokenRepository, INotificationService notificationService)
{
_adRepository = adRepository;
_tokenRepository = tokenRepository;
_notificationService = notificationService;
}
}
public interface IChangePasswordServiceFactory
{
IChangePasswordService CreateForUserAccount(UserAccount account);
}
public class StructureMapChangePasswordServiceFactory : IChangePasswordServiceFactory
{
public IChangePasswordService CreateForUserAccount(UserAccount account)
{
return ObjectFactory.With(account).GetInstance < IChangePasswordService>();
}
}
}

Related

How to change database name in dbcontext connection string at runtime

My Asp.Net MVC application is setup as follows.
There are 4 projects in solution.
ge.Web
ge.BLL
ge.Core
ge.Entities
Controller in ge.Web initializes a repository object present in ge.Core
public class MapsController : Controller
{
private AssessmentRepository repAssessments = new AssessmentRepository("name=GEContext", schoolCode);
public ActionResult DisplaySearchResults()
{
.....
}
}
Assessments Repository
public class AssessmentRepository : Repository<Assessment>, IAssessmentRepository
{
public AssessmentRepository(string connString, string schoolCode)
:base(connString, schoolCode)
{ }
}
Repository
public class Repository<TEntity> : IRepository<TEntity> where TEntity:class
{
protected readonly GEContext context;
public Repository(string connString, string schoolCode) {
context = new GEContext(connString);
}
}
GEContext
public class GEContext : DbContext
{
public GEContext(string connString):base(connString)
{
this.Configuration.LazyLoadingEnabled = false;
Database.SetInitializer(new MySqlInitializer());
}
}
DbContext
public class DbContext : IDisposable, IObjectContextAdapter
{
public DbContext(string nameOrConnectionString);
}
Web.Config
<add name="GEContext" connectionString="server=localhost;port=4040;uid=root;pwd=xxx;database=ge" providerName="MySql.Data.MySqlClient" />
now i want to replace "database=ge" present in web.config with database=ge_[schoolCode]. at runtime How can i go about it?
UPDATE
My solution did not work. so i am stating the problem once again.
Web.Config
I have changed My config file to the following (previously GEContext was the only connection string)
<connectionStrings>
<add name="GEContext_sc001" connectionString="server=localhost;port=4040;uid=root;pwd=blabla;database=db_sc001" providerName="MySql.Data.MySqlClient" />
<add name="GEContext_sc002" connectionString="server=localhost;port=4040;uid=root;pwd=blabla;database=db" providerName="MySql.Data.MySqlClient" />
<appSettings>
<add key="SchoolCodes" value="sc001,sc002"/>
these are the allowed schoolCodes
Now when the user enters schoolcode at login screen, it is validated against the codes present in SchoolCodes key. and if yes, then it should try to connect to the connectionString for that particular connection. Now when my code comes to
UserManager.FindAsync
in Login function of AccountController, it crashes trying to find GEContext. Where is that set? and how can i change it?
I have changed the repository calling in controller as follows
private static string schoolCode = (string)System.Web.HttpContext.Current.Session["SchoolCode"];
private AssessmentRepository repAssessments = new AssessmentRepository("name=GEContext_" + schoolCode);
UPDATE-2
Following is present in ge.Web
IdentityConfig.cs
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser, Role, int, UserLogin, UserRole, UserClaim>(context.Get<ApplicationDbContext>()));
...........
}
The following is present in ge.Core
ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, Role, int, UserLogin, UserRole, UserClaim>
{
public ApplicationDbContext(string connString)
: base(connString)
{
Database.SetInitializer(new MySqlInitializer());
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext("name=GEContext_");
}
}
How can i pass schoolCode from ge.web to ge.Core (answer should be straight forward but currently i cant get my head around it)
UPDATE-3
As told by itikhomi and taking help from this post I have changed my code as follows
in ApplicationDbContext class added the following
public static ApplicationDbContext Create(string scCode){
return new ApplicationDbContext("name=GEContext_" + scCode);
}
in AccountController Login
var appDbContext = ApplicationDbContext.Create(model.SchoolCode);
Request.GetOwinContext().Set<ApplicationDbContext>(appDbContext);
it still does not hit the correct database
You have two ways
1)
using System.Data.SqlClient;
public class Repository<TEntity> : IRepository<TEntity> where TEntity:class
{
protected readonly GEContext context;
public Repository(string connString, string schoolCode) {
context = new GEContext(connString);
var connection = new SqlConnectionStringBuilder(context.Database.Connection.ConnectionString);
connection.InitialCatalog = "YOUR_PREFIX_FROMSOMEWHERE"+schoolCode;
context.Database.Connection.ConnectionString = connection.ConnectionString;
}
}
2) if you wants to switch connection when it opened before use ChangeDatabase:
//open connection if it close
context.Database.Connection.ChangeDatabase("DATABASE-NAME");
NOTE: if use ChangeDatabase connection should be already opened
FOR UPDATE3:
You need to do somethink like this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public ApplicationDbContext(string schoolCode)
: base(schoolCode)
{
var connection = new SqlConnectionStringBuilder(this.Database.Connection.ConnectionString);
connection.InitialCatalog = "YOUR_PREFIX_FROMSOMEWHERE" + schoolCode;
this.Database.Connection.ConnectionString = connection.ConnectionString;
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
in account controller:
public ApplicationSignInManager SignInManager
{
get
{
if (_signInManager == null)
{
var code = HttpContext.Request.Form.Get("SchoolCode");//Get from FORM\QueryString\Session whatever you wants
if (code != null)
{
HttpContext.GetOwinContext().Set<ApplicationSignInManager>(new ApplicationSignInManager(_userManager, HttpContext.GetOwinContext().Authentication));
}
_signInManager = HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
return _signInManager;
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
if (_userManager == null)
{
var code = HttpContext.Request.Form.Get("SchoolCode");//Get from FORM\QueryString\Session whatever you wants
if (code != null)
{
var appDbContext = new ApplicationDbContext(code);
HttpContext.GetOwinContext().Set<ApplicationDbContext>(appDbContext);
HttpContext.GetOwinContext().Set<ApplicationUserManager>(new ApplicationUserManager(new UserStore<ApplicationUser>(appDbContext))); //OR USE your specified create Method
}
_userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
return _userManager;
}
private set
{
_userManager = value;
}
}
Your problem is in Store of UserManager is created before you change your OWIN context, in this case better to use DI like here
You can change the database for an open connection
context.Database.GetDbConnection().ChangeDatabase("");
I resolved it with the help of itikhomi..Posting the final code..
ApplicationDbContext
public static ApplicationDbContext Create()
{
return new ApplicationDbContext("name=GEContext");
}
AccountController
public ApplicationUserManager UserManager {
get
{
if (System.Web.HttpContext.Current.Session["SchoolCode"] == null)
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
else
{
var appDbContext = ApplicationDbContext.Create(System.Web.HttpContext.Current.Session["SchoolCode"].ToString());//new ApplicationDbContext("name=GEContext", System.Web.HttpContext.Current.Session["SchoolCode"].ToString());
HttpContext.GetOwinContext().Set<ApplicationDbContext>(appDbContext);
HttpContext.GetOwinContext().Set<ApplicationUserManager>(new ApplicationUserManager(new UserStore<ApplicationUser, Role, int, UserLogin, UserRole, UserClaim>(appDbContext)));
return HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
private set
{
_userManager = value;
}
}

How do I use multiple databases with Autofac?

I am having a project and my project is connecting to two different databases are BookStoreEntities and BlogEntities.
If I remove line code builder.RegisterType<BlogEntities>().As<DbContext>(); in Autofac configuration my project works fine and else I'll get error "The entity type Book is not part of the model for the current context".
My autofac config:
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<BookStoreEntities>().As<DbContext>();
builder.RegisterType<BlogEntities>().As<DbContext>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerDependency();
builder.RegisterType<BookService>().As<IBookService>();
builder.RegisterFilterProvider();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
Repository class:
public class Repository<T> : IRepository<T> where T : class
{
private DbContext _dbContext;
private DbSet<T> _dbSet;
public Repository(DbContext dbContext)
{
_dbContext = dbContext;
_dbSet = dbContext.Set<T>();
}
public IEnumerable<T> GetAll()
{
return _dbSet;
}
}
Service layer:
public class BookService : IBookService
{
private IRepository<Book> _bookRepository;
public BookService(IRepository<Book> bookRepository)
{
_bookRepository = bookRepository;
}
public IEnumerable<Book> GetBooks()
{
return _bookRepository.GetAll();
}
}
Controller:
public class BookController : Controller
{
private IBookService _bookService;
public BookController(IBookService bookService)
{
_bookService = bookService;
}
// GET: Book
public ActionResult Index()
{
var books = _bookService.GetBooks();
return View(books);
}
}
My Project is using 2 different databases and Service layer will implement from this Generic Repository. I want to myservice1 works with MyDbContext1 and myservice2 works with MyDbContext2
Then don't new your DbContext inside your repository. That makes testing hard anyway.
Inject it:
public Repository(DbContext dbContext)
{
_dbContext = dbContext;
}
Now the repository doesn't care which DbContext-derived class is injected. This works because you only call DbContext.Set<T>().
Try to something like:
public class MyDbContext1 : DbContext
{
public MyDbContext1 ()
:base("ConnectionString")
{ }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//...
}
}
And add to repository new property
private IDbSet<T> Entities
{
get
{
if (_dbSet == null)
{
_dbSet = _dbContext1.Set<T>();
}
return _dbSet;
}
}

Entity Framework, Unity, and MVC

I have a multi tier application using Entity Framework, MVC and Unity.
The basic setup is like this:
EF Data Access Layer
public class MyDataProvider : DbContext, IMyDataProvider
{
public MyDataProvider(SqlConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection,contextOwnsConnection)
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 60;
Configuration.LazyLoadingEnabled = true;
Configuration.ValidateOnSaveEnabled = true;
Configuration.ProxyCreationEnabled = true;
Configuration.AutoDetectChangesEnabled = true;
}
public new IDbSet<TModel> Set<TModel>() where TModel : class
{
return base.Set<TModel>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new EmployeeMapping());
base.OnModelCreating(modelBuilder);
}
public abstract class ServiceBase<TModel> : IDisposable, IService<TModel> where TModel : class, IModel
{
[Dependency]
public IMyDataProvider MyDataProvider { get; set; }
...
}
All services inherit from this class
I then inject specific services into the Business Logic Layer like so:
public class GetEmployees
{
[Dependency("EmployeeService")]
public IEmployeeService EmployeeService { get; set;
public IQueryable<Employee> GetAllEmployees()
{
return EmployeeService.GetTable();
}
...
}
In MVC I use a controller factory
public class MyControllerFactory : DefaultControllerFactory
{
private IUnityContainer _container;
public MyControllerFactory(IUnityContainer container)
{
_container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
{
return _container.Resolve(controllerType) as IController;
}
else
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
Global.asax
private static IUnityContainer InitContainer()
{
IUnityContainer unityContainer = new UnityContainer();
Bootstrapper bootstrapper = new Bootstrapper(unityContainer);
return unityContainer;
}
I pass the instance of UnityContainer into the Bootstrapper class. The Bootstrapper class self registers all assemblies.
In the MVC Controllers, I inject the Business Logic like so:
public class EmployeeController
{
[Dependency("GetEmployees")]
public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
This all works great up to a point. Every so often I will get an exception thrown from MyDataProvider class: "EntityConnection can only be constructed with a closed DbConnection". This seems to happen during high use of the MVC site. The exception is simple enough to understand, but how should I go about fixing it?
I found that changing how I instantiate the business logic class from a field on the controller to inside the ActionResult method, I don't recieve the exception.
For example:
public class EmployeeController
{
//[Dependency("GetEmployees")]
//public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
IBusinessLogic GetEmployees_Operations = _ioc_Bootstrapper.Resolve(typeof(IBusinessLogic), "GetEmployees") as IBusinessLogic;
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
Have I completely missed the boat on this and implemented Unity incorrectly?
Bootstrapper code
private void RegisterDAL(String assembly)
{
var currentAssembly = Assembly.LoadFrom(assembly);
var assemblyTypes = currentAssembly.GetTypes();
foreach (var assemblyType in assemblyTypes)
{
...
if (assemblyType.FullName.EndsWith("Provider"))
{
foreach (var requiredInterface in assemblyType.GetInterfaces())
{
if (requiredInterface.FullName.EndsWith("DataProvider"))
{
var typeFrom = assemblyType.GetInterface(requiredInterface.Name);
var typeTo = assemblyType;
var injector = GetInjectorConstructor(assemblyType.Module.Name);
RegisterType(typeFrom, typeTo, false, injector);
}
}
continue;
}
...
}
private InjectionConstructor GetInjectorConstructor(String moduleName)
{
...
connString = String.Concat("Data Source=MySqlServer, ";Initial Catalog=", catalogName, ";Application Name=", applicationName, ";Integrated Security=True; );
var conn = new SqlConnection(connString);
return new InjectionConstructor(conn, true);
}

Autofac. How to get caller class Type?

Suppose we have two classes with same constructor Injectable dependency:
public class FirstClass
{
public FirstClass(ISomeDependency someDependency)
{ }
}
public class SecondClass
{
public SecondClass(ISomeDependency someDependency)
{ }
}
Now we have a registration for ISomeDependency:
builder.Register(x =>
{
string key = GetKeyFromCurrentHttpRequest();
// if "Caller" is "FirstClass" return new Dependency(key);
// else return new Dependency("defaultKey");
}).As<ISomeDependency>();
Note: This is a simplified use case. The real scenario is much more complicated.
1. How to get "Caller" type which tryies to resolve ISomeDependency?
2. Is there a better way design for such situations?
You can use delegate factories do achieve your goal. The only drawback is the FirstClass and SecondClass cannot use ISomeDependency as parameter.
You can try this code in a console application (just add Autofac dependency).
using System;
using Autofac;
namespace test
{
class MainClass
{
public static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder ();
builder.RegisterType<SomeDependency>().As<ISomeDependency>();
builder.RegisterType<FirstClass>();
builder.RegisterType<SecondClass>();
var container = builder.Build();
var dummy = container.Resolve<FirstClass>();
var dummy2 = container.Resolve<SecondClass>();
}
public interface ISomeDependency
{
}
public class SomeDependency : ISomeDependency
{
public delegate ISomeDependency Factory(string value);
private readonly string _value;
public SomeDependency(string value)
{
_value = value;
Console.WriteLine("Value = " + _value);
}
}
public class FirstClass
{
private ISomeDependency _dependency;
public FirstClass(SomeDependency.Factory factory)
{
_dependency = factory.Invoke("my value");
}
}
public class SecondClass
{
private ISomeDependency _dependency;
public SecondClass(SomeDependency.Factory factory)
{
_dependency = factory.Invoke("my value 2");
}
}
}
}

need to figure out T of class Repository is for

I have class Repository, and based on T i want to create objectset for the type T..in my constructor...this is what i have so far..
public static class Repository<T> : IDisposable
where T : class
{
private DeltaDBEntities context;
private ObjectSet<T> objectset;
public Repository()
{
this.context = new DeltaDBEntities();
switch
{
case T = typeof(ViewModels.Company):
this.objectset = context.Companies;
break;
}
this.objectset = context.Set
}
If your DeltaDBEntities inherits from ObjectContext, you can use its generic method CreateObjectSet<T>().
public class Repository<T> where T : class
{
private DeltaDBEntities context;
private ObjectSet<T> objectset;
public Repository()
{
this.context = new DeltaDBEntities();
this.objectset = context.CreateObjectSet<T>();
}
}
Not sure what you are trying to achieve and why are you using generics for something that is not known at compile time but you may try the following:
public class Repository<T> : IDisposable where T : class
{
private DeltaDBEntities context;
private ObjectSet<T> objectset;
public Repository()
{
this.context = new DeltaDBEntities();
if (typeof(T) == typeof(ViewModels.Company))
{
this.objectset = context.Companies;
}
else if (typeof(T) == typeof(ViewModels.SomeOtherClass))
{
this.objectset = context.SomethingElse;
}
else
{
this.objectset = context.Set;
}
}
public void Dispose()
{
// TODO: implement IDisposable
}
}
Also note that a static class cannot implement an interface so Repository<T> should not be static.

Resources