need an example for Entity Framework when registering saga repositories with autofac - autofac

The masstransit documentation has an example for NHiberbate, does anyone have the same example, but using entity framework (SagaDbContextFactory,SagaDbContext) instead of NHibernate
// do not forget registering saga repositories (example for NHibernate)
var mappings = mappingsAssembly
.GetTypes()
.Where(t => t.BaseType != null && t.BaseType.IsGenericType &&
(t.BaseType.GetGenericTypeDefinition() == typeof(SagaClassMapping<>) ||
t.BaseType.GetGenericTypeDefinition() == typeof(ClassMapping<>)))
.ToArray();
builder.Register(c => new SqlServerSessionFactoryProvider(connString, mappings).GetSessionFactory())
.As<ISessionFactory>()
.SingleInstance();
builder.RegisterGeneric(typeof(NHibernateSagaRepository<>))
.As(typeof(ISagaRepository<>));

I've registered mine like this:
builder.RegisterAssemblyTypes(typeof(SomeType).Assembly).AsImplementedInterfaces().AsSelf();
builder.RegisterGeneric(typeof(SagaRepository<,>)).As(typeof(ISagaRepository<,>));
builder.RegisterGeneric(typeof(SagaDbContext<,>));
And then implemented the SagaRepository interface:
internal class SagaRepository<TSaga, TSagaClassMapping> : EntityFrameworkSagaRepository<TSaga>, ISagaRepository<TSaga, TSagaClassMapping>
where TSaga : class, ISaga where TSagaClassMapping : SagaClassMapping<TSaga>, new()
{
public SagaRepository(
IWorkerConfiguration workerConfiguration)
: base(() => new SagaDbContext<TSaga, TSagaClassMapping>(workerConfiguration.ConnectionStrings["SagaRepository"]))
{
}
}
For the mappings I use a base class:
internal abstract class BaseInstanceStateMap<TInstance> : SagaClassMapping<TInstance>, IInstanceStateMap<TInstance> where TInstance : BaseSagaInstance
{
protected BaseInstanceStateMap()
{
this.Property(x => x.CorrelationId);
this.Property(x => x.CurrentState).HasMaxLength(64);
this.Property(x => x.Initiated).HasColumnType("datetime2").IsRequired();
this.Property(x => x.Created).HasColumnType("datetime2").IsRequired();
this.Property(x => x.StateChanged).HasColumnType("datetime2").IsOptional();
}
}
Which I then inherit for each of my mappings:
internal class ProductIndexingStateMap : BaseInstanceStateMap<ProductIndexingSagaInstance>
{
public ProductIndexingStateMap()
{
this.Property(x => x.ProductId);
this.Property(x => x.ProductCorrelation);
this.Property(x => x.EncryptedConnectionString);
}
}

You actually can register EntityFrameworkSagaRepository<T> as generic but it will require you to register one DbContext. If you use SagaDbContext - you cannot have more than one saga in your application. The only way to go there is to bind custom repository implementation and SagaDbContext via generic type parameters, this is what #slinzerthegod is doing in his example.
You can also create your own context that will register all types. For example:
public class AssemblyScanningSagaDbContext : DbContext
{
readonly Assembly _mappingAssembly;
public AssemblyScanningSagaDbContext(Assembly mappingAssembly, string nameOrConnectionString)
: base(nameOrConnectionString)
{
_mappingAssembly = mappingAssembly;
}
public AssemblyScanningSagaDbContext(Assembly mappingAssembly, ObjectContext objectContext, bool dbContextOwnsObjectContext)
: base(objectContext, dbContextOwnsObjectContext)
{
_mappingAssembly = mappingAssembly;
}
public AssemblyScanningSagaDbContext(Assembly mappingAssembly, DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
_mappingAssembly = mappingAssembly;
}
public AssemblyScanningSagaDbContext(Assembly mappingAssembly, string nameOrConnectionString, DbCompiledModel model)
: base(nameOrConnectionString, model)
{
_mappingAssembly = mappingAssembly;
}
public AssemblyScanningSagaDbContext(Assembly mappingAssembly, DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection)
: base(existingConnection, model, contextOwnsConnection)
{
_mappingAssembly = mappingAssembly;
}
protected AssemblyScanningSagaDbContext(Assembly mappingAssembly)
{
_mappingAssembly = mappingAssembly;
}
protected AssemblyScanningSagaDbContext(Assembly mappingAssembly, DbCompiledModel model)
: base(model)
{
_mappingAssembly = mappingAssembly;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) =>
modelBuilder.Configurations.AddFromAssembly(_mappingAssembly);
}
If you have such class, you can do the following:
builder.Register(c => new AssemblyScanningSagaDbContext(typeof(MySagaMapping).Assembly,
connectionString).As<DbContext>();
builder.RegisterGeneric(typeof(EntityFrameworkSagaRepository<>))
.As(typeof(ISagaRepository<>))
.SingleInstance();
builder.RegisterStateMachineSagas(typeof(MySaga).Assembly);
Autofac is able to compose a factory delegate automatically and will inject it to the repository constructor.
Here is the scanning context as gist.

Related

Blazor Server (EF Core) and database connection

Sorry for the rudimentary question.
I'm currently studying application development with Blazor Server and am having trouble connecting to a database.
I'm trying to use DI to connect to the database.
I created a code that uses the factory pattern as shown below, but an error occurs in the part that gets the connection string.
public void ConfigureServices(IServiceCollection services)
{
//error:CS0121 Inappropriate call between the following methods or properties: 'Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)' と 'BlazorSv.Models.FactoryExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)'
services.AddDbContextFactory<BlazorSv.Models.SQLbeginnerContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DBConnection")));
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
I thought that the definition of the <Models.SQLbeginnerContext> part was ambiguous, so I wrote the hierarchy, but it didn't work.
What should I do about this error?
I want some advice
Below is SQLbegginerContext.cs that describes StartUp.cs and the factory pattern.
StartUp.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
//error:CS0121 Inappropriate call between the following methods or properties: 'Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)' と 'BlazorSv.Models.FactoryExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)'
services.AddDbContextFactory<Models.SQLbeginnerContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DBConnection")));
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
SQLbegginerContext.cs
public interface IDbContextFactory<TContext> where TContext : DbContext
{
TContext CreateDbContext();
}
public class blazordbFactory<TContext> : IDbContextFactory<TContext> where TContext : DbContext
{
public blazordbFactory(IServiceProvider provider)
{
this.provider = provider;
}
private readonly IServiceProvider provider;
public TContext CreateDbContext()
{
return ActivatorUtilities.CreateInstance<TContext>(provider);
}
}
public static class FactoryExtensions
{
public static IServiceCollection AddDbContextFactory<TContext>(
this IServiceCollection collection,
Action<DbContextOptionsBuilder> optionsAction = null,
ServiceLifetime contextAndOptionsLifetime = ServiceLifetime.Singleton)
where TContext : DbContext
{
collection.Add(new ServiceDescriptor(
typeof(IDbContextFactory<TContext>),
sp => new blazordbFactory<TContext>(sp),
contextAndOptionsLifetime));
collection.Add(new ServiceDescriptor(
typeof(DbContextOptions<TContext>),
sp => GetOptions<TContext>(optionsAction, sp),
contextAndOptionsLifetime));
return collection;
}
private static DbContextOptions<TContext> GetOptions<TContext>(
Action<DbContextOptionsBuilder> action,
IServiceProvider sp = null) where TContext : DbContext
{
var optionsBuilder = new DbContextOptionsBuilder<TContext>();
if (sp != null)
{
optionsBuilder.UseApplicationServiceProvider(sp);
}
action?.Invoke(optionsBuilder);
return optionsBuilder.Options;
}
}
public partial class SQLbeginnerContext : DbContext
{
public SQLbeginnerContext()
{
}
public SQLbeginnerContext(DbContextOptions<SQLbeginnerContext> options)
: base(options)
{
}
追記:SQLbegginer.cs
//Comment out this part
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseNpgsql("Host=localhost; Database=SQLbeginner; Username=user; Password=****")
}
}

No extension Method ToList on a dbset - really though?

Background - I am using Entity framework code version 2.1.4-rtm-31024
check out the CODE LISTING 1 - the problem (according to Ms Build Engine 15.9) is that GetAllMakes calls .ToList, but no 'ToList' method exists for a DbSet of VehicleMake. (check out Code Listing 2) to see the implementation of _vehicleContext.VehicleMakes
Why do I get a compile error? this makes no sense to me since I can call VehicleMakes.ToList() elsewhere in the code (no compiler error) no problem at all - see listing 3 for an example.
CODE LISTING 1
using System.Collections.Generic;
namespace CarPriceComparison.Models
{
public class VehicleRepository : IVehicleRepository
{
private VehicleContext _vehicleContext;
public VehicleRepository(VehicleContext dbContext_)
{
_vehicleContext = dbContext_;
}
public IEnumerable<VehicleMake> GetAllMakes()
{
return _vehicleContext.VehicleMakes.ToList();
}
}
}
CODE LISTING 2
namespace CarPriceComparison.Models
{
public class VehicleContext : DbContext
{
private IConfigurationRoot _config;
public VehicleContext(IConfigurationRoot config_, DbContextOptions
options_) : base(options_)
{
_config = config_;
}
public DbSet<VehicleMake> VehicleMakes {get; set;}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["ConnectionStrings:VehicleContextConnection"]);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<VehicleModel>()
.HasOne(p => p.Make)
.WithMany(b => b.Models)
.HasForeignKey(p => p.VehicleMakeForeignKey)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
}
}
}
CODE LISTING 3
private VehicleContext _vehicleContext;
private IMailService _mailService;
private IConfigurationRoot _config;
public HomeController(IMailService mailService_, IConfigurationRoot
config_, VehicleContext vehicleContext_)
{
_vehicleContext = vehicleContext_;
_mailService = mailService_;
_config = config_;
}
public IActionResult Index()
{
var vehicleData = _vehicleContext.VehicleMakes.ToList();
return View();
}
I think you missing an using statement.
using System.Linq;

How to Resolve multiple DBContext call using generic UnitOfWork<TContext> in Autofac

Hi I have created my UnitOfWork as generic and at runtime it should create new instance of DB context with DBContextOption Builder on the basis of TContext passing I have registered Mention DB Context in autofac but how to resolve this at DB Context Constructor Level
DB Context 1 Implemetation
public class DBContext1 : DbContext
{
public DBContext1(DbContextOptions<DBContext1> options1) : base(options1)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
DB Context 2 Implemetation
public class DBContext2 : DbContext
{
public DBContext2(DbContextOptions<DBContext2> options2) : base(options2)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
IUnitOfWork interface Implemetation
public interface IUnitOfWork<TContext> where TContext : DbContext, IDisposable
{
}
UnitOfWork class Implemetation
public class UnitOfWork<TContext> : IDisposable, IUnitOfWork<TContext> where TContext : DbContext, new()
{
private DbContext _context;
public UnitOfWork()
{
_context = new TContext();
}
}
StartUp Class Implemetation
public class Startup
{
protected IConfiguration _configuration { get; set; }
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddEntityFrameworkSqlServer()
.AddDbContext<DBContext1>(options =>
options.UseSqlServer(_configuration.GetConnectionString("DBContext1")))
.AddDbContext<DBContext2>(options =>
options.UseSqlServer(_configuration.GetConnectionString("DBContext2")));
/* Autofac DI Configuration with registering DBContext/DataModule/ServiceModule to it */
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterInstance(_configuration).AsImplementedInterfaces().ExternallyOwned();
var autoFacOptions1 = new DbContextOptionsBuilder<DBContext1>().UseSqlServer(_configuration.GetConnectionString("DBContext1")).Options;
var autoFacOptions2 = new DbContextOptionsBuilder<DBContext2>().UseSqlServer(_configuration.GetConnectionString("DBContext2")).Options;
containerBuilder.Register(c => new DBContext1(autoFacOptions1)).As<DbContext>();
containerBuilder.Register(c => new DBContext2(autoFacOptions2)).As<DbContext>();
containerBuilder.RegisterModule<DataModule>();
containerBuilder.RegisterModule<ServiceModule>();
containerBuilder.Register<String>(c => Guid.NewGuid().ToString())
.Named<String>("correlationId")
.InstancePerLifetimeScope();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Account}/{action=Login}/{id?}");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
I am able to achieve multiple DBContext Call as required but I have to create Default constructor & connection string in DB context like mention below
DB Context 1 Implemetation
public class DBContext1 : DbContext
{
public DBContext1()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Data Source=Server;Database=DB;User Id=UserID;Password=Password;Integrated Security=False;MultipleActiveResultSets=true;");
}
public DBContext1(DbContextOptions<DBContext1> options1) : base(options1)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
DB Context 2 Implemetation
public class DBContext2 : DbContext
{
public DBContext2()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Data Source=Server;Database=DB;User Id=UserID;Password=Password;Integrated Security=False;MultipleActiveResultSets=true;");
}
public DBContext2(DbContextOptions<DBContext2> options2) : base(options2)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Please help me to call parameterised constructor of DBContext1 & DBContext2 using autofac dependency resolver
Well, if you're using autofac to resolve dependencies then why are you trying to do its job for it? :) That's the main problem with your code.
First of all, you don't need to register IConfiguration explicitly. It is already registered in the IServiceCollection that's passed to ConfigureServices() method and will be automatically picked up by autofac during containerBuilder.Populate(services) call. You can just remove this registration and nothing will change.
Further, you're registering both your DbContexts twice - in the service collection and in the autofac container builder. This is not necessary as the latter will effectively replace the former. Also, it creates confusion about what is registered where and how this whole this is going to work. It's better to pick one method of registration and stick with it.
Next problem: how are you going to unit test your unit of work? It has hard dependency on DbContext whose lifecycle you cannot control in tests. This is exactly what you need autofac for: manage component's dependencies for you allowing you to concentrate on the component's purpose and not on the secondary stuff.
Next confusion point is here:
containerBuilder.Register(c => new DBContext1(autoFacOptions1)).As<DbContext>();
containerBuilder.Register(c => new DBContext2(autoFacOptions2)).As<DbContext>();
By doing this you are effectively replacing first db context registration with the second. From this point there is no way to inject DBContext1 anywhere in your application. EDITED: You still can inject collection of DbContext derivative implementations and find DBContext1 among them... but that would look very weird.
All in all, this can be done in much more clean and straightforward way.
Startup
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var builder = new ContainerBuilder();
builder.Register(c => c.CreateDbContextOptionsFor<DBContext1>("DBContext1")).As<DbContextOptions<DBContext1>>().SingleInstance();
builder.Register(c => c.CreateDbContextOptionsFor<DBContext2>("DBContext2")).As<DbContextOptions<DBContext2>>().SingleInstance();
builder.RegisterType<DBContext1>().AsSelf().InstancePerLifetimeScope();
builder.RegisterType<DBContext2>().AsSelf().InstancePerLifetimeScope();
builder.RegisterType<SomeComponent>().As<ISomeComponent>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(UnitOfWork<>)).As(typeof(IUnitOfWork<>)).InstancePerLifetimeScope();
builder.Populate(services);
var container = builder.Build();
return new AutofacServiceProvider(container);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
....
}
}
CreateDbContextOptionsFor helper implementation. It is introduced in order to make Startup code concise and more readable. It can probably be improved even further by making use of autofac's parameterized factory instead of new DbContextOptionsBuilder<TContext>(), but I'm not sure if there's a point in it in this case.
public static class DBExtentions
{
public static DbContextOptions<TContext> CreateDbContextOptionsFor<TContext>(this IComponentContext ctx,
string connectionName) where TContext : DbContext
{
var connectionString = ctx.Resolve<IConfiguration>().GetConnectionString(connectionName);
return new DbContextOptionsBuilder<TContext>().UseSqlServer(connectionString).Options;
}
}
UnitOfWork
public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : DbContext
{
private TContext _context;
public UnitOfWork(TContext context)
{
_context = context;
}
}
Injecting and using unit of work
public class SomeComponent : ISomeComponent
{
private readonly IUnitOfWork<DBContext1> _uow;
public SomeComponent(IUnitOfWork<DBContext1> uow)
{
_uow = uow;
}
public void DoSomething()
{
_uow.DoWhatever();
}
....

Unable to inject DBContext into my Web API 2 Controller with Unity

I've been at it for days, but I can't get Unity to inject anything with RegisterType<> into my Controller. I'm using Web Api 2, in Visual Studio 2015, with Unity 4. Whenever I try to inject IUnitOfWork or IRFContext, I get "message": "An error occurred when trying to create a controller of type 'ClPlayersController'. Make sure that the controller has a parameterless public constructor.".
I'm using the Unity.AspNet.WebApi to bootstrapp into WebApi. Below is my UnityWebApiActivator
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(mycompany.project.api.UnityWebApiActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(mycompany.project.api.UnityWebApiActivator), "Shutdown")]
namespace mycompany.project.api
{
public static class UnityWebApiActivator
{
public static void Start()
{
var resolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());
GlobalConfiguration.Configuration.DependencyResolver = resolver;
}
public static void Shutdown()
{
var container = UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
}
I'm using a Start.cs due to Owin.
[assembly: OwinStartup(typeof(mycompany.project.api.Startup))]
namespace mycompany.project.api
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
config.DependencyResolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
My WebApiConfig.cs is below:
namespace mycompany.project.api
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
log4net.Config.XmlConfigurator.Configure();
config.MapHttpAttributeRoutes();
config.EnableSystemDiagnosticsTracing();
config.Services.Add(typeof(IExceptionLogger),
new SimpleExceptionLogger(new LogManagerAdapter()));
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
}
}
}
My UnityConfig.cs is below
namespace mycompany.project.api
{
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
public static void RegisterTypes(IUnityContainer container)
{
var config = new MapperConfiguration(cfg =>
{
//AutoMapper bindings
});
container.RegisterInstance<IMapper>(config.CreateMapper());
container.RegisterType<IRFContext, RFContext>(new PerThreadLifetimeManager());
container.RegisterType<IUnitOfWork, UnitOfWork>();
XmlConfigurator.Configure();
var logManager = new LogManagerAdapter();
container.RegisterInstance<ILogManager>(logManager);
}
}
}
All that I have in my Global.asax is below:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Error()
{
var exception = Server.GetLastError();
if (exception != null)
{
var log = new LogManagerAdapter().GetLog(typeof(WebApiApplication));
log.Error("Unhandled exception.", exception);
}
}
}
If my Controller is like this, it works fine:
public class ClPlayersController : ApiController
{
private readonly IMapper mapper;
public ClPlayersController(IMapper _mapper, IUnityContainer container)
{
mapper = _mapper;
}
But placing IUnitOfWork, like below, or the IRFContext, I get the error:
private readonly IMapper mapper;
private readonly IUnitOfWork unitOfWork;
public ClPlayersController(IMapper _mapper, IUnityContainer container, IUnitOfWork _unitOfWork)
{
mapper = _mapper;
unitOfWork = _unitOfWork;
}
I can't find, for the life of me, what I'm doing wrong. If I loop through the container.Registrations on the constructor, I find the mappings, but they refuse to get injected. Any hints?
EDIT
Below is the code for UnitOfWork and RFContext
namespace mycompany.project.data.configuracao
{
public class UnitOfWork : IUnitOfWork
{
private readonly IRFContext _rfContext;
private bool _disposed = false;
public UnitOfWork(IRFContext rfContext)
{
_rfContext = rfContext;
}
public void Commit()
{
if (_disposed)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
_rfContext.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing && _rfContext != null)
{
_rfContext.Dispose();
}
_disposed = true;
}
}
}
and
namespace mycompany.project.data.configuracao
{
public interface IUnitOfWork : IDisposable
{
void Commit();
}
}
and RFContext is a basic POCO generated DBContext
namespace mycompany.project.data.configuracao
{
using System.Linq;
public class RFContext : System.Data.Entity.DbContext, IRFContext
{
public System.Data.Entity.DbSet<ClGrupoEconomico> ClGrupoEconomicoes { get; set; }
//all my DbSets
public System.Data.Entity.DbSet<SpTipoLog> SpTipoLogs { get; set; }
static RFContext()
{
System.Data.Entity.Database.SetInitializer<RFContext>(null);
}
public RFContext()
: base("Name=RFContext")
{
}
public RFContext(string connectionString)
: base(connectionString)
{
}
public RFContext(string connectionString, System.Data.Entity.Infrastructure.DbCompiledModel model)
: base(connectionString, model)
{
}
public RFContext(System.Data.Common.DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
public RFContext(System.Data.Common.DbConnection existingConnection, System.Data.Entity.Infrastructure.DbCompiledModel model, bool contextOwnsConnection)
: base(existingConnection, model, contextOwnsConnection)
{
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ClGrupoEconomicoConfiguration());
//all my Configuration classes
modelBuilder.Configurations.Add(new SpTipoLogConfiguration());
}
public static System.Data.Entity.DbModelBuilder CreateModel(System.Data.Entity.DbModelBuilder modelBuilder, string schema)
{
modelBuilder.Configurations.Add(new ClGrupoEconomicoConfiguration(schema));
//all my configuration classes
modelBuilder.Configurations.Add(new SpTipoLogConfiguration(schema));
return modelBuilder;
}
}
}
Unfortunately the exception you are seeing can occur for several reasons. One of them is when Unity cannot resolve one or more of your injections.
An error occurred when trying to create a controller of type
'FooController'. Make sure that the controller has a parameterless
public constructor.
So, based on the information in your question your setup is apparently correct, since IMapper can be injected. Therefore I guess that UnitOfWork and RFContext have dependencies that Unity cannot resolve. Maybe a repository?
UPDATE:
The problem here is that your RFContext has several constructors.
https://msdn.microsoft.com/en-us/library/cc440940.aspx#cnstrctinj_multiple
When a target class contains more than one constructor with the same
number of parameters, you must apply the InjectionConstructor
attribute to the constructor that the Unity container will use to
indicate which constructor the container should use. As with automatic
constructor injection, you can specify the constructor parameters as a
concrete type, or you can specify an interface or base class for which
the Unity container contains a registered mapping.
In this case Unity doesn't know how to resolve your RFContext, and will try to use the constructor with the most parameters. You can solve it by using
container.RegisterType<IRFContext, RFContext>(new InjectionConstructor());

creating NHibernate repositories in view models with IoC

I would like to how to correct build and handle with NHibernate repository and session in view models classes.
For example I use Caliburn Micro framework with Castle Windsor boostraper.
First I created NHibernate repository:
public interface IRepository{//I omitted not needed code for simplification}
public class NHibRepository: IRepository
{
private ISession _session;
public NHibRepository(ISession session)
{
_session=session;
}
// I omitted not needed code for simplification
}
Second I definied some POCO class and mapping class with Fluent NHibernate.
public class User{}
public class Profile{}
public class Album{}
public class UserMap : ClassMap<User>{}
public class ProfileMap : ClassMap<Profile>{}
public class AlbumMap : ClassMap<Album>{}
Now I need use NHibernate repositories in my view models.
public interface IViewModelA{}
public class ViewModelA : ScreenViewModel, IViewModelA
{
public NHibRepository<User> UserRepo{get;set;}
public NHibRepository<Profile> ProfileRepo{get;set;}
}
public interface IViewModelB{}
public class ViewModelB : Screen, IViewModelB
{
public NHibRepository<Profile> ProfileRepo{get;set;}
public NHibRepoistory<Album> AlbumRepo{get;set;}
}
When I am creating repository class for some entity class I need pass session to NHibRepository construtor.
UserRepo= new NHibRepository<User>(NHIBERNATE SESSION);
Until now I used some helper class for creating Nhibernate session and init repository.
Helper class is here:
public class FluentNHibHelper
{
private ISessionFactory _sessionFactory;
public FluentNHibHelper(IPersistenceConfigurer db, Assembly asm)
{
InitializeSessionFactory(db, asm);
}
private void InitializeSessionFactory(IPersistenceConfigurer db, Assembly asm)
{
_sessionFactory = Fluently.Configure()
.Database(db)
.Mappings(m => m.FluentMappings.AddFromAssembly(asm))
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
.BuildSessionFactory();
}
public ISession OpenSession()
{
return _sessionFactory.OpenSession();
}
}
with this class I created repository class:
private const string ConnString =
#"Server=TestMachine\SQLEXPRESS;Database=TEST;Trusted_Connection=True;";
UserRepo = new NHibRepository<User>(
new FluentNHibHelper(MsSqlConfiguration.MsSql2008.ConnectionString(ConnString),
Assembly.GetExecutingAssembly())
.OpenSession());
Now I am confuse I don’t what is the best way for creating repository object in view models.
For example in bootstraper class can I somehow register repository class ?
public class CastleBootstrapper : Bootstrapper<IShellViewModel>
{
private IWindsorContainer _windsorContainer;
protected override void Configure()
{
_windsorContainer = new WindsorContainer();
//register repository class here ???
}
}
Can anybody help me? Thank you very much
Sorry for my english.
I use the following in an ASP.net MVC application. Castle.Windsor takes care of creating the ISession dependency for each repository.
//Located in your application startup
protected IWindsorContainer CreateContainer()
{
container = new WindsorContainer();
container.Install(
new PersistenceInstaller(),
new RepositoryInstaller()
//, other installers here
);
return container;
}
public class PersistenceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<PersistenceFacility>();
}
}
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
NHibernate.Cfg.Configuration config = BuildDatabaseConfiguration();
Kernel.Register(
Component.For<ISessionFactory>()
.UsingFactoryMethod(config.BuildSessionFactory),
Component.For<ISession>()
.UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
.LifeStyle.Trasient
);
}
private NHibernate.Cfg.Configuration BuildDatabaseConfiguration()
{
return Fluently.Configure()
.Database(SetupDatabase)
.Mappings(m => m.HbmMappings.AddFromAssemblyOf<Entity>())
.ExposeConfiguration(ConfigurePersistence)
.BuildConfiguration();
}
protected virtual IPersistenceConfigurer SetupDatabase()
{
return MsSqlConfiguration.MsSql2008
.UseOuterJoin()
.ConnectionString(x => x.FromConnectionStringWithKey("ApplicationServices"))
.ShowSql();
}
protected virtual void ConfigurePersistence(NHibernate.Cfg.Configuration config)
{
SchemaMetadataUpdater.QuoteTableAndColumns(config);
}
protected virtual bool IsDomainEntity(Type t)
{
return typeof(DomainBase).IsAssignableFrom(t);
}
}
public class RepositoryInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Find().Configure(ConfigureLifeStyle()));
}
private ConfigureDelegate ConfigureLifeStyle()
{
return c => c.LifeStyle.Transient;
}
private BasedOnDescriptor Find()
{
return AllTypes.FromAssemblyContaining<NHRepository>()
.Where(type => type.Name.EndsWith("Repository"))
.WithService
.AllInterfaces();
}
}