How to Overcome Entity Framework Caching/Tracking Using MassTransit? - dependency-injection

Forgive my ignorance, but it appears that MassTransit treats singleton and scoped dependencies effectively the same. At least in the case of an Entity Framework DbContext and, therefore, the UserManager registered via the extension method AddEntityFrameworkStores.
This means that any entities loaded with tracking or otherwise added to the context are cached for the duration of the application lifetime. That, in turn, means that changes to these entities via anything outside of the context (say ad-hoc scripts) won't be recognized until the service is recycled.
Is there a best practice for addressing this limitation? For context, consider the following snippet from startup:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddDbContextPool<SomeDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("SomeConnectionString")));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<SomeDbContext>()
.AddDefaultTokenProviders();
// ...
}
And the following consumer:
public class SomeConsumer : IConsumer<ISomeRequest>
{
private readonly SomeDbContext _dbContext;
private readonly UserManager<IdentityUser> _userManager;
public SomeConsumer(SomeDbContext dbContext, UserManager<IdentityUser> userManager)
{
_dbContext = dbContext;
_userManager = userManager;
}
public async Task Consume(ConsumeContext<ISomeRequest> context)
{
var identityUser1 = await _dbContext.Users.FindAsync(1);
var identityUser2 = await _userManager.FindByIdAsync(1);
var consumerHashCode = GetHashCode();
var dbContextHashCode = _dbContext.GetHashCode();
var userManagerHashCode = _userManager.GetHashCode();
var identityUser1HashCode = identityUser1.GetHashCode();
var identityUser2HashCode = identityUser2.GetHashCode();
context.Respond(new SomeResponse());
}
}
In the example above, the consumer (registered transient) serves a different hash code on each request. All of the others serve the same ones (suggesting the same instances), despite the DB context and user manager being registered as scoped.
In case it matters, MassTransit is running from a console application as opposed to a web application.

I was directed to the answer in comments on the original question, but thought I'd post the details for anyone else. Bottom line is that I needed to correctly configure the dependency injection using MassTransit.Extensions.DependencyInjection (see http://masstransit-project.com/MassTransit/usage/containers/msdi.html).
I had started down that path, but missed a key piece when configuring endpoints: e.LoadFrom(provider). Missing that piece effectively turned my consumers into singletons, regardless of how they were actually configured.
Lastly, I needed to configure my DbContext using AddDbContext instead of AddDbContextPool.
Here's a snippet of the bare minimum I needed to make this happen:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddDbContext<SomeDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("SomeConnectionString")));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<SomeDbContext>()
.AddDefaultTokenProviders();
// ...
services.AddScoped<SomeConsumer>();
services.AddMassTransit(x =>
{
x.AddConsumer<SomeConsumer>();
});
services.AddSingleton(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host("localhost", "/", h => { });// However you want to get your host
var queueName = "web-service-endpoint";
cfg.ReceiveEndpoint(host, queueName, e => e.LoadFrom(provider));
}));
}

Related

Connect to 2 different MartenDB datastores with ASP.Net Core

When setting up a MartenDB datastore in ASP.Net Core, you normally put code like this in your Startup.cs:
services.AddMarten(o =>
{
o.Connection(configuration.GetConnectionString("MyDatabase"));
o.AutoCreateSchemaObjects = AutoCreate.All;
o.Serializer(new JsonNetSerializer { EnumStorage = EnumStorage.AsString });
});
This allows you to then inject IDocumentSession and IDocumentStore into your various classes for working with that database.
Now what do you do if you have to connect to a second database? I looked at the ISessionFactory but it is not apparent that you can change the connection string from here. Do you need to manually create and register a new DocumentStore?
To answer my own question, I wound up creating a custom DocumentStore and ISessionFactory for each database I wanted to connect to, and then injecting the custom SessionFactory.
Here's the code (only showing one instance of each class for the sake of brevity. Just replace Db1 with Db2 for the second version of each class):
The custom DocumentStore:
public class Db1Store : DocumentStore
{
public Db1Store(StoreOptions options) : base(options)
{
}
}
The custom SessionFactory:
public class Db1SessionFactory : ISessionFactory
{
private readonly Db1Store store;
public Db1SessionFactory(Db1Store store)
{
this.store = store;
}
public IQuerySession QuerySession()
{
return store.QuerySession();
}
public IDocumentSession OpenSession()
{
return store.OpenSession();
}
}
The service registration (this replaces the services.AddMarten call):
services.AddSingleton(p =>
{
var options = new StoreOptions();
options.Connection(configuration.GetConnectionString("DB1"));
options.AutoCreateSchemaObjects = AutoCreate.All;
options.Serializer(new JsonNetSerializer { EnumStorage = EnumStorage.AsString });
return new Db1Store(options);
});
services.AddSingleton<Db1SessionFactory>();
Then you inject the Db1SessionFactory instance into your class, and run a query like this:
var result = await db1SessionFactory.QuerySession().Query<MyAwesomeTable>().ToListAsync();
Downsides:
I would prefer to inject the QuerySession or DocumentSession, but I can't see a way to do that without moving to Autofac or a similar DI Container that supports named instances.
I am not sure what downsides there will be creating these QuerySession/DocumentSessions in this manner. It may be a bad tradeoff.

Add EvenLogging to an IHost container

I’m creating a console app and have recently started adding custom services to the IHost container so I can simply pass the IHost to any number of factory classes and have everything thing I need to configure them. But I’ve gotten stuck when it comes to adding Windows Event Logging as a service, could use some help getting past this.
My Main static method in Program calls CreateHostBuilder and returns an IHostBuilder as shown below.
public static IHostBuilder CreateHostBuilder(string[] args)
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("appsettings.json");
IConfiguration configuration = configurationBuilder.Build();
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostContext, logging) =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Information);
logging.AddEventLog(eventViewerSettings =>
{
eventViewerSettings.SourceName = "MeCore2";
eventViewerSettings.LogName = "Application";
eventViewerSettings.MachineName = ".";
});
})
.ConfigureServices(services => services.AddDbContext<MeCore2Context>())
// Add custom service for performing DNS queries
.ConfigureServices(services => services.AddTransient<IDnsQueryService>(DnsQueryFactory.Create))
// Add custom service for Managing Runtime Environment Settings
.ConfigureServices(services => services.AddTransient<IEnvironmentSettings>(EnvironmentSettingsFactory.Create))
// Add custom service for Managing String Extractions
.ConfigureServices(services => services.AddTransient<IExtractStringsService>(ExtraxtStringsFactory.Create))
// Add custom service for IP GeoLocation
.ConfigureServices(services => services.AddTransient<IIpGeolocationService>(IpGeolocationFactory.Create));
return hostBuilder;
}
My factory classes are implemented like this.
public static class DnsQueryFactory
{
public static DnsQueryService Create(IServiceProvider serviceProvider)
{
bool exceptionDisplayOnly = serviceProvider.GetRequiredService<IEnvironmentSettings>().WriteErrorsToEventLogs;
IHost host = serviceProvider.GetRequiredService<IHost>();
return new DnsQueryService(exceptionDisplayOnly, host);
}
}
And my concrete service constructors are implemented like this.
public DnsQueryService(bool exceptionDisplayOnly, IHost host)
{
this.exceptionDisplayOnly = exceptionDisplayOnly;
this.logger = host.Services.GetRequiredService<ILogger>();
this.environmentSettings = host.Services.GetRequiredService<IEnvironmentSettings>();
}
When I ran the app after setting up in this manner, I was unable to pull an ILogger from the host container, I could though, pull an ILoggerFactory then I needed to take some additional steps before I had a fully functional ILogger.
I would like to be able to pull the ILogger from the Host container with it fully configured and ready to use for exception handling, warnings, and basic information logging. But I'm stumped here as I can't seem to get the right syntax for using the ILoggingBuilder or ILoggerFactory into the Host container.
I started down the path of creating a static class EventLoggingServices that would accept an IServiceProvider finish out the configuration steps and return an ILogger, but this too has got me stumped. I'm close but not where I need to be and can't find a blog that covers this approach, either that or I'm going at this the wrong way, to begin with. Appreciate the help and thanks in advance.
I believe I've answered my own question with the following code, it is writing to the event logs. I implemented a factory method to encapsulate the ILogger as follows.
public static class EventLoggingFactory
{
public static ILogger<IEventLogging> Create(IServiceProvider serviceProvider)
{
return new EventLogging().EventLogger;
}
}
public class EventLogging : IEventLogging
{
#region *-- Private Members --*
private ILogger<IEventLogging> _logger = null;
#endregion
public ILogger<IEventLogging> EventLogger { get { return this._logger; } }
public EventLogging()
{
EventLogSettings settings = new EventLogSettings();
settings.LogName = "Application";
settings.SourceName = "MeCore2";
settings.MachineName = ".";
ILoggerFactory loggerFactory = new LoggerFactory();
loggerFactory.AddProvider(new EventLogLoggerProvider(settings));
this._logger = loggerFactory.CreateLogger<IEventLogging>();
}
}
public interface IEventLogging
{
ILogger<IEventLogging> EventLogger { get; }
}
And in my HostBuilder the following:
.ConfigureServices(services => services.AddTransient(EventLoggingFactory.Create))
What I haven't considered and I'm still wrapping my head around are service LifeTimes. Using this approach the Ilogger is Transient, but is that the best way to implement it?
The final code block on this post has been a sufficient solution for my needs. With a little more effort I've been able to expand the features used to capture log data for viewing in Windows Event Viewer.

UWP Template 10 and Service Dendency Injection (MVVM) not WPF

I have spent over two weeks searching google, bing, stack overflow, and msdn docs trying to figure out how to do a proper dependency injection for a mobile app that I am developing. To be clear, I do DI every day in web apps. I do not need a crash course on what, who, and why DI is important. I know it is, and am always embracing it.
What I need to understand is how this works in a mobile app world, and in particular a UWP Template 10 Mobile app.
From my past, in a .net/Asp app I can "RegisterType(new XYZ).Singleton() blah" {please forgive syntax; just an example} in App_Start.ConfigureServices. This works almost identical in .netcore, granted some syntactic changes.
My problem is now I am trying to provide my api is going to an UWP app that needs to digest my IXYZ service. By no means do I think that they should "new" up an instance every time. There has to be a way to inject this into a container on the UWP side; and I feel I am missing something very simple in the process.
Here is the code I have:
App.xaml.cs
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// TODO: add your long-running task here
//if (args.Kind == ActivationKind.LockScreen)
//{
//}
RegisterServices();
await NavigationService.NavigateAsync(typeof(Views.SearchCompanyPage));
}
public static IServiceProvider Container { get; private set; }
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IXYZ, XYZ>();
Container = services.BuildServiceProvider();
}
MainPage.xaml.cs:
public MainPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
private readonly IXYZ _xyz;
public MainPageViewModel(IXYZ xyz)
{
//Stuff
_xyz= xyz;
}
}
I now get the error:
XAML MainPage...ViewModel type cannot be constructed. In order to be constructed in XAML, a type cannot be abstract, interface nested generic or a struct, and must have a public default constructor.
I am willing to use any brand of IoC Container, but what I need is an example of how to properly use DI for services in a UWP app. 99.9% of questions about DI is about Views (i.e. Prism?) not just a simple DI for a service (i.e. DataRepo; aka API/DataService).
Again, I feel I am missing something obvious and need a nudge in the right direction. Can somebody show me an example project, basic code, or a base flogging on how I should not be a programmer...please don't do that (I don't know if my ego could take it).
You can try to Microsoft.Hosting.Extensions just like ASP.NET, there's an implementation on Xamarin.Forms by James Montemagno, as well it can be used in UWP I have tried and it works perfectly. You have to change some parts in order to get it working.
In OnLaunched Method add Startup.Init();
public static class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
StorageFolder LocalFolder = ApplicationData.Current.LocalFolder;
var configFile = ExtractResource("Sales.Client.appsettings.json", LocalFolder.Path);
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
// Tell the host configuration where to file the file (this is required for Xamarin apps)
c.AddCommandLine(new string[] { $"ContentRoot={LocalFolder.Path}" });
//read in the configuration file!
c.AddJsonFile(configFile);
})
.ConfigureServices((c, x) =>
{
// Configure our local services and access the host configuration
ConfigureServices(c, x);
}).
ConfigureLogging(l => l.AddConsole(o =>
{
//setup a console logger and disable colors since they don't have any colors in VS
o.DisableColors = true;
}))
.Build();
//Save our service provider so we can use it later.
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
//ViewModels
services.AddTransient<HomeViewModel>();
services.AddTransient<MainPageViewModel>();
}
static string ExtractResource(string filename, string location)
{
var a = Assembly.GetExecutingAssembly();
using (var resFilestream = a.GetManifestResourceStream(filename))
{
if (resFilestream != null)
{
var full = Path.Combine(location, filename);
using (var stream = File.Create(full))
{
resFilestream.CopyTo(stream);
}
}
}
return Path.Combine(location, filename);
}
}
Injecting a ViewModel is possible as well which is pretty nice.
With help from #mvermef and the SO question Dependency Injection using Template 10 I found a solutions. This turned out to be a rabbit hole where at every turn I ran into an issue.
The first problem was just getting Dependency Injection to work. Once I was able to get that figured out from the sources above I was able to start injecting my services into ViewModels and setting them to the DataContext in the code behind.
Then I ran into an injection issue problem with injecting my IXYZ services into the ViewModels of UserControls.
Pages and their ViewModels worked great but I had issues with the DataContext of the UserControl not being injected with UserControl's ViewModel. They were instead getting injected by the Page's ViewModel that held it.
The final solution turned out to be making sure that the UserControl had the DataContext being set in XAML not the code behind, as we did with the Pages, and then creating a DependencyProperty in the code behind.
To show the basic solution read below.
To make it work I started with:
APP.XAML.CS
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// long-running startup tasks go here
RegisterServices();
await Task.CompletedTask;
}
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IRepository, Repository>();
services.AddSingleton<IBinderService, BinderServices>();
**//ViewModels**
**////User Controls**
services.AddSingleton<AddressesControlViewModel, AddressesControlViewModel>();
services.AddSingleton<CompanyControlViewModel, CompanyControlViewModel>();
**//ViewModels**
**////Pages**
services.AddSingleton<CallListPageViewModel, CallListPageViewModel>();
services.AddSingleton<CallListResultPageViewModel, CallListResultPageViewModel>();
etc....
Container = services.BuildServiceProvider();
}
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
**//INJECT THE VIEWMODEL FOR EACH PAGE**
**//ONLY THE PAGE NOT USERCONTROL**
if (page is CallListPage)
{
return Container.GetService<CallListPageViewModel>();
}
if (page is CallListResultPage)
{
return Container.GetService<CallListResultPageViewModel>();
}
etc...
return base.ResolveForPage(page, navigationService);
}
In the code behind for the Page
CALLLISTPAGE.XAML.CS
public CallListPage()
{
InitializeComponent();
}
CallListPageViewModel _viewModel;
public CallListPageViewModel ViewModel
{
get { return _viewModel ?? (_viewModel = (CallListPageViewModel)DataContext); }
}
In your XAML add your UserControl
CALLLISTPAGE.XAML
<binder:CompanyControl Company="{x:Bind ViewModel.SelectedCompany, Mode=TwoWay}"/>
In your UserControl make sure to add the DataContext to the XAML NOT the code behind like we did with the pages.
COMPANYCONTROL.XAML
<UserControl.DataContext>
<viewModels:CompanyControlViewModel x:Name="ViewModel" />
</UserControl.DataContext>
In the UserControl Code Behind add a Dependency Property
COMPANYCONTROL.XAML.CS
public static readonly DependencyProperty CompanyProperty = DependencyProperty.Register(
"Company", typeof(Company), typeof(CompanyControl), new PropertyMetadata(default(Company), SetCompany));
public CompanyControl()
{
InitializeComponent();
}
public Company Company
{
get => (Company) GetValue(CompanyProperty);
set => SetValue(CompanyProperty, value);
}
private static void SetCompany(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as CompanyControl;
var viewModel = control?.ViewModel;
if (viewModel != null)
viewModel.Company = (Company) e.NewValue;
}
In the end I am not sure if this is an elegant solution but it works.

Configure Unity container per-request in OWIN middleware

I'm wanting to configure registrations in a Unity container being used by ASP.NET Web API 2 based on properties of a HTTP request. For example, a request to /api/database1/values should result in a Unity container configuration with an IDbContext configured for database1, while a request to /api/database4/values will get an IDbContext configured for database4.
I've gotten so far as using UnityHierarchicalDependencyResolver as the dependency resolver, so types registered with HierarchicalLifetimeManager last only for the lifetime of the request. This works well for getting types resolved per request. But how to get them registered per request using OWIN middleware is beyond me.
In my middleware, a call to System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer)) gets an instance of IUnityContainer, but it's the same container for all requests, including any registrations from previous requests.
By encapsulating UnityHierarchicalDependencyResolver with my own implementation of IDependencyResolver I can see that IDependencyResolver.BeginScope isn't called until much later in the process. So the problem would seem to be that the child container isn't created until Web API wakes up, long after my middleware calls Next(..).
Is there a way I can get the scope of my dependency resolver to start sooner? Is there some other strategy that I'm missing. In case it makes any difference, I'm hosting in IIS, but favouring the OWIN middleware approach.
Update
This isn't an answer, and it's too big for a comment, but after struggling to solve this with Unity I decided to switch to Autofac and it all just fell into place.
The Autofac OWIN packages (Autofac.Mvc5.Owin, Autofac.Owin, Autofac.WebApi2.Owin) make it dead easy to use Autofac within the OWIN pipeline and ensure appropriate lifetime management in ASP.NET MVC and Web API. This was the missing link.
I couldn't find a way to reconfigure the container per-request, but it did at least make it possible to configure a factory per-request (so yes, #Haukinger and #alltej, you were right to push in that direction.
So I register a factory like:
builder.RegisterType<DataDependencyFactory>().InstancePerRequest();
And register the create method of that factory like:
builder
.Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency())
.As<IDataDependency>()
.InstancePerRequest();
Registering the factory this way is particularly useful, because downstream dependents don't need to be aware of the factory. I like this because my dependents don't need a factory, they need an instance. The container bends to the needs of my dependents, not the other way around :)
Then, in a piece of OWIN middleware, I resolve the factory, and set a property on it according to the properties of the request. Subsequent resolution of IDataDependency in an MVC or Web API controller, or anything else later in the OWIN pipeline, will get an instance configured according to the property on the factory.
Based on your api URL ("/api/database4/values"), I suggest that you create a filter attribute(e.g. DbIdFilter) so that you can reuse the filter attribute to other controller methods that follow similar url path/segment like this below:
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/values")]
public IHttpActionResult GetValues()
{
return Ok();
}
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/products")]
public IHttpActionResult GetProducts()
{
return Ok();
}
First, create the filter attribute:
public class DbIdFilterAttribute : ActionFilterAttribute
{
private readonly string _routeDataId;
private const string defaultRouteName = "databaseId";
public DbIdFilterAttribute():this(defaultRouteName)
{}
public DbIdFilterAttribute(string routeDataId)
{
_routeDataId = routeDataId;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var routeData = actionContext.Request.GetRouteData();
var dbId = routeData.Values[_routeDataId] as string;
//here we create the db instance at the filter level.
DbInstanceFactory.RegisterDbInstance(dbId);
}
}
Next, create an instance factory that will register/resolve the db instance during runtime:
public class DbInstanceFactory : IDbInstanceFactory
{
public static IDbInstance RegisterDbInstance(string databaseId)
{
var factory = UnityConfig.GetConfiguredContainer().Resolve<IDbInstanceFactory>();
return factory.CreateInstance(databaseId);
}
public IDbInstance CreateInstance(string databaseId)
{
var container = UnityConfig.GetConfiguredContainer();
//container.RegisterType<IDbInstance, DbInstance>();
container.RegisterType<IDbInstance, DbInstance>(new InjectionConstructor(databaseId));
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
public IDbInstance GetInstance()
{
var container = UnityConfig.GetConfiguredContainer();
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
}
public interface IDbInstanceFactory
{
IDbInstance CreateInstance(string databaseId);
IDbInstance GetInstance();
}
Register this factory class in UnityConfig.cs (or wherever you currently register the types):
container.RegisterType<IDbInstanceFactory, DbInstanceFactory>
(new ContainerControlledLifetimeManager());
It's registered ContainerControlledLifetimeManager since this factory does not have to be a per request.
So just a basic DbInstance class below(for clarity) that takes a parameter in the constructor (this parameter can be your connection string or a named connection):
public class DbInstance : IDbInstance
{
public string DbId { get; }
public DbInstance(string databaseId)
{
DbId = databaseId;
}
}
public interface IDbInstance
{
string DbId { get; }
}
In controller class, you can use it like this:
....
private IDbInstanceFactory _dbFactory;
public MyController(IDbInstanceFactory dbFactory)
{
_dbFactory = dbFactory;
}
// Alternate, if you want to use property injection instead of constructor injection
//[Dependency]
//public IDbInstanceFactory DbFactory { get; set; }
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/test")]
public IHttpActionResult Test()
{
var db = _dbFactory.GetInstance();
return Ok(db.DbId);
}
...

Cannot get a working Unity Session Lifetime Manager, ASP.NET MVC5

I've read and Googled everything on this, but can't seem to get it to work. I created a custom LifetimeManager for Unity in my MVC5 application based on these posts:
MVC3 Unity Framework and Per Session Lifetime Manager
This may be the issue I am experiencing
Here is my SessionLifetimeManager
public class SessionLifetimeManager : LifetimeManager
{
private string key = Guid.NewGuid().ToString();
public override object GetValue()
{
return HttpContext.Current.Session[key];
}
public override void RemoveValue()
{
HttpContext.Current.Session.Remove(key);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Session[key] = newValue;
}
}
I only have a few types I'm playing with, here is the relevant registrations in UnityConfig.cs:
container.RegisterType<IEpiSession, EpiSession>(new SessionLifetimeManager(),
new InjectionConstructor(config.AppServerURI, config.PathToSysConfig));
container.RegisterType<IReportRepository, EpicorReportRepository>(new TransientLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Note that the EpicorReportRepository has a dependency on IEpiSession via constructor injection.
public class EpicorReportRepository : IReportRepository
{
private IEpiSession session;
// DI constructor
public EpicorReportRepository(IEpiSession session) {
this.session = session;
}
// ...
}
My Problem: After the first user / session connects to the application, every new user / session after that seems to still be using the EpiSession object and credentials that the first user had create/injected for him. This seems to be a common pattern used on the interwebs, so I'm wondering what I am missing.
How did you test that IEpiSession is the same in different Sessions?
Try to open you application from different browsers. If you open several tabs in the same browser then the same session is used.
I checked your code and it works for me.
There is the only one difference in SetResolver():
DependencyResolver.SetResolver(
type => container.Resolve(type),
types => container.ResolveAll(types));
The full registration code is the following:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
var container = new UnityContainer();
container.RegisterType<IEpiSession, EpiSession>(
new SessionLifetimeManager(),
new InjectionConstructor("config.AppServerURI", "config.PathToSysConfig"));
container.RegisterType<IReportRepository, EpicorReportRepository>(new TransientLifetimeManager());
DependencyResolver.SetResolver(
type => container.Resolve(type),
types => container.ResolveAll(types));
}
}

Resources