Web API, Light Inject and Passing a Static Dictionary to the data layer - asp.net-mvc

We have a multi-database solution and are passing the connection string to a factory function like so:
container.Register<IDbContextFactory>(
f => new DynamicDbContextFactory(ClientConfig.GetConnectionString()),
new PerScopeLifetime());
ClientConfig contains a static dictionary that gets populated on app start that maps a sub domain to a connection string. It seems that this approach is causing a memory leak (not 100% sure about this causing the leak but there is a leak).
public class ClientConfig
{
private static ConcurrentDictionary<string, string> ConnectionStringManager
{
get;
set;
}
// etc.
}
My question is in MVC what is the best way to hold a list of connection strings that can be easily looked up on each request in order to pass that down the chain.

Edit : The question was initially tagged with Autofac
With Autofac you don't have to use a dictionary and something like that to do what you want. You can use a custom parameter :
public class ConnectionStringParameter : Parameter
{
public override Boolean CanSupplyValue(ParameterInfo pi,
IComponentContext context,
out Func<Object> valueProvider)
{
valueProvider = null;
if (pi.ParameterType == typeof(String)
&& String.Equals(pi.Name, "connectionString",
StringComparison.OrdinalIgnoreCase))
{
valueProvider = () =>
{
// get connectionstring based on HttpContext.Current.Request.Url.Host
return String.Empty;
};
}
return valueProvider != null;
}
}
Then register your Parameter using a Module
public class ConnectionStringModule : Autofac.Module
{
protected override void AttachToComponentRegistration(
IComponentRegistry componentRegistry, IComponentRegistration registration)
{
registration.Preparing += registration_Preparing;
}
private void registration_Preparing(Object sender, PreparingEventArgs e)
{
Parameter[] parameters = new Parameter[] { new ConnectionStringParameter() };
e.Parameters = e.Parameters.Concat(parameters);
}
}
Module you have to register inside your container using
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConnectionStringModule());
Each time Autofac have to resolve a parameter of type String named connectionString it will used the custom parameter and get your connectionstring based on what you want.
By the way this code sample use HttpContext.Current. In case of a multithreaded process it may return null. I don't recommend using HttpContext.Current for such things. You can use an intermediate class instead of accessing it, for example a IConnectionstringProvider interface.
public interface IConnectionstringProvider
{
String ConnectionString { get; }
}
public class ConnectionStringProvider : IConnectionstringProvider
{
public ConnectionStringProvider(Strong host)
{
// get connectionstring based on host
this._connectionString = String.Empty;
}
private readonly String _connectionString;
public String ConnectionString
{
get { return this._connectionString; }
}
}
Inside your Parameter you will have to change the valueProvider by
valueProvider = () =>
{
return context.Resolve<IConnectionstringProvider>().ConnectionString;
};
And finally you will have to register your IConnectionstringProvider at the beginning of the request lifetimescope :
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConnectionStringModule());
IContainer container = builder.Build();
container.ChildLifetimeScopeBeginning += container_ChildLifetimeScopeBeginning;
}
private static void container_ChildLifetimeScopeBeginning(
Object sender, LifetimeScopeBeginningEventArgs e)
{
String host = HttpContext.Current.Request.Url.Host;
ContainerBuilder childLifetimeScopeBuilder = new ContainerBuilder();
childLifetimeScopeBuilder.RegisterInstance(new ConnectionStringProvider(host))
.As<IConnectionstringProvider>()
.SingleInstance();
childLifetimeScopeBuilder.Update(e.LifetimeScope.ComponentRegistry);
}
}
Of course there is many way to do it but you have the idea

Related

Castle Windsor with Azure Functions (v3)

I'm going around in circles with this. Is there any way to use the Castle Windsor dependency injection adapter (https://github.com/volosoft/castle-windsor-ms-adapter) with Azure Function apps?
The code example on the adaptor github page is for ASP.NET Core but I would have imagined that there'd be a way to specify the service provider used for a function app, too?
There have been no official documents on this but there is a feature request made by DavidJFowler
Please find the sample code from the above feature request.
The IJobActivatorEx implementation:
public class CastleWindsorJobActivator : IJobActivatorEx
{
private readonly WindsorContainer container;
public CastleWindsorJobActivator(WindsorContainer container) => this.container = container;
public T CreateInstance<T>(IFunctionInstanceEx functionInstance)
{
var disposer = functionInstance.InstanceServices.GetRequiredService<ScopeDisposable>();
disposer.Scope = container.BeginScope();
return container.Resolve<T>();
}
// Ensures a created Castle.Windsor scope is disposed at the end of the request
public sealed class ScopeDisposable : IDisposable
{
public IDisposable Scope { get; set; }
public void Dispose() => this.Scope?.Dispose();
}
public T CreateInstance<T>()
{
var disposer = container.Resolve<ScopeDisposable>();
disposer.Scope = container.BeginScope();
return container.Resolve<T>();
}
}
Startup class:
[assembly: FunctionsStartup(typeof(FunctionApp2.Startup))]
namespace FunctionApp2
{
public class Startup: FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var container = new WindsorContainer();
container.Register(Component.For<IScoped1>().ImplementedBy<Scoped1>().LifestyleScoped())
.Register(Component.For<IScoped2>().ImplementedBy<Scoped2>().LifestyleScoped())
.Register(Component.For<ISingleton1>().ImplementedBy<Singleton1>())
.Register(Component.For<ISingleton2>().ImplementedBy<Singleton2>());
// register function classes in container
var functions = Assembly.GetExecutingAssembly().GetTypes().Where(t =>
t.GetMethods().Any(m => m.GetCustomAttributes(typeof(FunctionNameAttribute), false).Any()));
foreach (var function in functions)
{
container.Register(Component.For(function).LifestyleScoped());
}
builder.Services.AddScoped<CastleWindsorJobActivator.ScopeDisposable>()
.AddSingleton<IJobActivator>(new CastleWindsorJobActivator(container));
container.AddServices(builder.Services);
}
}
}
Example function:
public class Function1
{
public Function1(IScoped1 scoped1, IScoped2 scoped2, ISingleton1 singleton1, ISingleton2 singleton2)
{
}
[FunctionName("Function1")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
}
For now, this feature isn't officially released but you can follow other dependency injection patterns. You can raise a feature request from Microsoft QnA or from Azure Portal.

Injecting HttpService into a Mule 4 Custom Configuration Properties Provider

I'm working on making a custom properties provider to load the contents of a Spring cloud config server at startup. I need to make a single call at the initialization of the provider to fetch these properties, and would like to use the Mule HttpService in order to make the http client for this call, instead of creating my own. Unfortunately, whenever I try this, it seems the HttpService hasn't been created yet and so throws an NPE once it's referenced.
CustomConfigurationPropertiesProviderFactory.java
public class CustomConfigurationPropertiesProviderFactory implements ConfigurationPropertiesProviderFactory {
public static final String EXTENSION_NAMESPACE = "custom-properties";
public static final String CONFIGURATION_PROPERTIES_ELEMENT = "config";
public static final ComponentIdentifier CUSTOM_CONFIGURATION_PROPERTIES =
builder().namespace(EXTENSION_NAMESPACE).name(CONFIGURATION_PROPERTIES_ELEMENT).build();
#Inject
HttpService httpService;
#Override
public ComponentIdentifier getSupportedComponentIdentifier() {
return CUSTOM_CONFIGURATION_PROPERTIES;
}
#Override
public ConfigurationPropertiesProvider createProvider(ConfigurationParameters parameters,
ResourceProvider externalResourceProvider) {
String url = parameters.getStringParameter("url");
return new CustomConfigurationPropertiesProvider(url, httpService);
}
}
CustomConfigurationPropertiesProvider.java
public class CustomConfigurationPropertiesProvider implements ConfigurationPropertiesProvider {
private final static String PREFIX = "custom::";
private Properties properties = null;
public CustomConfigurationPropertiesProvider(String url, HttpService httpService) {
HttpClientConfiguration.Builder builder = new HttpClientConfiguration.Builder();
builder.setName("customProperties");
HttpClient client = httpService.getClientFactory().create(builder.build()); //NPE here
client.start();
// proceed to create and execute request, then load into properties
}
#Override
public Optional<ConfigurationProperty> getConfigurationProperty(String configurationAttributeKey) {
if (configurationAttributeKey.startsWith(PREFIX)) {
String effectiveKey = configurationAttributeKey.substring(PREFIX.length());
if (properties != null && !properties.isEmpty()) {
return Optional.of(new ConfigurationProperty() {
#Override
public Object getSource() {...}
#Override
public Object getRawValue() { return properties.getProperty(effectiveKey); }
#Override
public String getKey() { return effectiveKey; }
});
}
}
return Optional.empty();
}
}
What do I need to change to properly inject this service?
I've been following the advice from these two bits of documentation, for reference:
https://docs.mulesoft.com/mule-runtime/4.2/custom-configuration-properties-provider
https://docs.mulesoft.com/mule-sdk/1.1/mule-service-injection

Dependency injection in asp.net core

I have following code:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IWsApiProvider, WsApiProvider>();
services.AddScoped<IApplicationUserRepository, ApplicationUserRepository>();
...
}
WsApiProvider has following:
public Guid SessionId { get; set; }
public IWSocketProvider WsApi { get; set; }
In Invoke method I'm updating these properties:
public Task Invoke(HttpContext httpContext, IOptions<AppSettings> appSettings)
{
...
this._wsApiProvider.SessionId = sessionGuid;
this._wsApiProvider.WsApi = connection;
...
}
And then I'm going to Controller where I injected Repository:
public AccountController(IApplicationUserRepository applicationUserRepository)
{
this._applicationUserRepository = applicationUserRepository;
}
public ApplicationUserRepository(IWsApiProvider wsApi) : base(wsApi)
{
}
And here I have wsApi object with empty properties. Two questions:
Why in repository constructor I have this object with empty properties?
Is there any way to create one instance of IWsApiProvider for all dependencies per request (non-singleton solution)?
Thank you in advance
UPDATED. The whole middleware class:
public class WsApiMiddleware
{
private readonly RequestDelegate _next;
private readonly IWsApiProvider _wsApiProvider;
private const string QisSessionId = "QisSessionId";
public WsApiMiddleware(RequestDelegate next, IWsApiProvider wsApiProvider)
{
_next = next;
this._wsApiProvider = wsApiProvider;
}
public Task Invoke(HttpContext httpContext, IOptions<AppSettings> appSettings)
{
var sessionId = httpContext.Request.Cookies[QisSessionId];
var sessionGuid = Guid.Empty;
if (!string.IsNullOrEmpty(sessionId))
{
Guid.TryParse(sessionId, out sessionGuid);
}
var connection = ConnectionsPool.GetSocket(sessionGuid);
if (connection == null)
{
connection = new WSocketProvider(null);
var connectTask = Task.Run(async () =>
await connection.Connect(appSettings.Value.WsApiServerEndPointUri, CancellationToken.None)
);
Task.WaitAll(connectTask);
var sessionService = new SessionService(connection);
var sessionOpenTask = Task.Run(async () =>
{
SessionDataState sessionData = null;
//TODO [W-8/6/2017] - think about better solution for situation when sessionId doesn't exist on the server
try
{
sessionData = await sessionService.OpenSession(sessionGuid != Guid.Empty ? (Guid?)sessionGuid : null);
}
catch (Exception ex)
{
sessionData = await sessionService.OpenSession();
}
sessionGuid = sessionData.SessionId;
if (!sessionData.ClientType.HasValue)
{
await sessionService.LoginClient();
}
ConnectionsPool.TryAddConnection(sessionGuid, connection);
httpContext.Response.Cookies.Append(QisSessionId, sessionGuid.ToString());
});
Task.WaitAll(sessionOpenTask);
}
this._wsApiProvider.SessionId = sessionGuid;
this._wsApiProvider.WsApi = connection;
return this._next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class WsApiMiddlewareExtensions
{
public static IApplicationBuilder UseWsApiMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<WsApiMiddleware>();
}
}
From the ASP.Net core middleware doc :
Middleware is constructed once per application lifetime. Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors are not shared with other dependency-injected types during each request.
And the most important part in you situation:
If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by dependency injection.
Since IWsApiProvider is a scoped service(i.e. per request), it should be passed as an argument to the Invoke method, as follow:
public class WsApiMiddleware
{
private readonly RequestDelegate _next;
// no longer passed in the constructor
public WsApiMiddleware(RequestDelegate next)
{
_next = next;
}
// passed as an argument to Invoke, via dependency injection
public Task Invoke(HttpContext httpContext, IWsApiProvider wsApiProvider, IOptions<AppSettings> appSettings)
{
wsApiProvider.SessionId = "SessionId";
wsApiProvider.WsApi = "WsApi";
return this._next(httpContext);
}
}

How to use Func<T> in built-in dependency injection

Using asp.net 5 I'd like my controller to be injected with a Func<T>instead of T
For example:
public HomeController(Func<Interfaces.IUnitOfWork> uow)
Instead of
public HomeController(Interfaces.IUnitOfWork uow)
Is it possible with the built-in DI or am I forced to move to an external DI?
Func<T> does not get registered or resolved by default but there is nothing stopping you from registering it yourself.
e.g.
services.AddSingleton(provider =>
new Func<IUnitOfWork>(() => provider.GetService<IUnitOfWork>()));
Note that you will also need to register IUnitOfWork itself in the usual way.
You can register a Func<T> or a delegate with a ServiceCollection. I recommend a delegate because it allows you to distinguish between different methods with identical signatures.
Here's an example.
public interface IThingINeed {}
public class ThingINeed : IThingINeed { }
public delegate IThingINeed ThingINeedFactory();
public class DelegateRegistrationTests
{
[Test]
public void RegisterDelegateFromDependency()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IThingINeed, ThingINeed>();
serviceCollection.AddTransient<ThingINeedFactory>(
provider => provider.GetService<IThingINeed>);
var serviceProvider = serviceCollection.BuildServiceProvider();
var factoryMethod = serviceProvider.GetService<ThingINeedFactory>();
var output = factoryMethod();
Assert.IsInstanceOf<ThingINeed>(output);
}
}
This almost looks like a service locator because the function we're resolving is actually IServiceCollection.GetService<ThingINeedFactory>(). But that's hidden in the composition root. A class that injects this delegate depends on the delegate, not on the implementation.
You can use the same approach if the method you want to return belongs to a class that the container must resolve.
public interface IThingINeed
{
string SayHello();
}
public class ThingINeed : IThingINeed
{
private readonly string _greeting;
public ThingINeed(string greeting)
{
_greeting = greeting;
}
public string SayHello() => _greeting;
}
public class ThingINeedFactory
{
public IThingINeed Create(string input) => new ThingINeed(input);
}
public delegate IThingINeed ThingINeedFactoryMethod(string input);
public class DelegateRegistrationTests
{
[Test]
public void RegisterDelegateFromDependency()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IThingINeed, ThingINeed>();
serviceCollection.AddSingleton<ThingINeedFactory>();
serviceCollection.AddSingleton<ThingINeedFactoryMethod>(provider =>
provider.GetService<ThingINeedFactory>().Create);
var serviceProvider = serviceCollection.BuildServiceProvider();
var factoryMethod = serviceProvider.GetService<ThingINeedFactoryMethod>();
var created = factoryMethod("abc");
var greeting = created.SayHello();
Assert.AreEqual("abc", greeting);
}
}
Here's an extension method to (maybe) make it a little bit easier:
public static class ServiceCollectionExtensions
{
public static IServiceCollection RegisterDelegate<TSource, TDelegate>(
this IServiceCollection serviceCollection,
Func<TSource, TDelegate> getDelegateFromSource)
where TDelegate : class
{
return serviceCollection.AddSingleton(provider =>
getDelegateFromSource(provider.GetService<TSource>()));
}
}
serviceCollection
.RegisterDelegate<ThingINeedFactory, ThingINeedFactoryMethod>(
factory => factory.Create);
As far as I'm aware deferring dependencies like this isn't possible using the current default IoC container within ASP.NET Core. I've not been able to get it working anyway!
To defer the initialisation of dependencies like this you'll need to implement an existing, more feature rich IoC container.
While there is no built in Func building support in the default dependency injection for .net core we can build an extension method to add in all the missing funcs. We just need to make sure we call it at the end of registration.
public static class ServiceCollectionExtensions
{
private static MethodInfo GetServiceMethod;
static ServiceCollectionExtensions()
{
Func<IServiceProvider, object> getServiceMethod = ServiceProviderServiceExtensions.GetService<object>;
GetServiceMethod = getServiceMethod.Method.GetGenericMethodDefinition();
}
/// <summary>
/// Registers all Funcs in constructors to the ServiceCollection - important to call after all registrations
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
public static IServiceCollection AddFactories(this IServiceCollection collection)
{
// Get a list of all Funcs used in constructors of regigstered types
var funcTypes = new HashSet<Type>(collection.Where(x => x.ImplementationType != null)
.Select(x => x.ImplementationType)
.SelectMany(x => x.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
.SelectMany(x => x.GetParameters())
.Select(x => x.ParameterType)
.Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>)));
// Get a list of already registered Func<> and remove them from the hashset
var registeredFuncs = collection.Select(x => x.ServiceType)
.Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>));
funcTypes.ExceptWith(registeredFuncs);
// Each func build the factory for it
foreach (var funcType in funcTypes)
{
var type = funcType.GetGenericArguments().First();
collection.AddTransient(funcType, FuncBuilder(type));
}
return collection;
}
/// <summary>
/// This build expression tree for a func that is equivalent to
/// Func<IServiceProvider, Func<TType>> factory = serviceProvider => new Func<TType>(serviceProvider.GetService<TType>);
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static Func<IServiceProvider, object> FuncBuilder(Type type)
{
var serviceProvider = Expression.Parameter(typeof(IServiceProvider), "serviceProvider");
var method = GetServiceMethod.MakeGenericMethod(type);
var call = Expression.Call(method, serviceProvider);
var returnType = typeof(Func<>).MakeGenericType(type);
var returnFunc = Expression.Lambda(returnType, call);
var func = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(IServiceProvider), returnType), returnFunc, serviceProvider);
var factory = func.Compile() as Func<IServiceProvider, object>;
return factory;
}
}
In AddFactories we get a list of all the concreate types that are registered then check their constructors for any Func<>. From that list remove any Func that has been registered before. Using some expressiontrees we build the needed Funcs.
The code is also over in codereview, minus the check for already registered funcs.
I wrote a little extension method that registres the service and the factory (Func<T>):
public static class IServiceCollectionExtension
{
public static IServiceCollection AddFactory<TService, TServiceImplementation>(this IServiceCollection serviceCollection)
where TService : class
where TServiceImplementation : class, TService
{
return serviceCollection
.AddTransient<TService, TServiceImplementation>();
.AddSingleton<Func<TService>>(sp => sp.GetRequiredService<TService>);
}
}
Usage:
serviceCollection
.AddFactory<IMyInterface, MyImplementation>()
There are a few options available to you, the first is you can switch over to use the incredible Lamar (with it's ASP.NET Core integration).
For the most part, switching to Lamar is a few lines of code, and you'll be able to resolve Func<> and Lazy<> all day long.
I've been using it at scale for a while on a large microservices based platform and we're completely happy with it *.
If you don't want to move over to Lamar, you can use this for resolving Lazy<> (sorry, I've tried and tried, and I can't get it to work with Func<>:
// Add to your Service Collection.
services.AddTransient(typeof(Lazy<>), typeof(LazyServiceFactory<>));
class LazyServiceFactory<T> : Lazy<T>
{
public LazyServiceFactory(IServiceProvider serviceProvider)
: base(() => serviceProvider.GetRequiredService<T>())
{
}
}
And just for completeness, here's a test too.
// And some tests...
[TestMethod]
[DataTestMethod]
[DataRow(ServiceLifetime.Transient)]
[DataRow(ServiceLifetime.Scoped)]
[DataRow(ServiceLifetime.Singleton)]
public void Resolve_GivenLazyilyRegisteredService_CanResolve(ServiceLifetime serviceLifetime)
{
// Arrange
IServiceProvider serviceProvider = CreateServiceProvider(serviceLifetime);
using IServiceScope scope = serviceProvider.CreateScope();
// Act
Func<Lazy<ClassHello>> result = () => scope.ServiceProvider.GetRequiredService<Lazy<ClassHello>>();
// Assert
result
.Should()
.NotThrow()
.And
.Subject()
.Value
.Should()
.NotBeNull();
}
static IServiceProvider CreateServiceProvider(ServiceLifetime serviceLifetime)
{
IServiceCollection services = new ServiceCollection();
services.Add(new ServiceDescriptor(typeof(Lazy<>), typeof(LazyServiceFactory<>), serviceLifetime));
services.Add(new ServiceDescriptor(typeof(ClassHello), typeof(ClassHello), serviceLifetime));
return services.BuildServiceProvider(true);
}
I've not put this through it's paces as I use Lamar pretty much exclusivly now, but this has come in handy for smaller/ disposable projects.
* My only minor issue is that it doesn't support IAsyncDisposable yet.
I have solution below
public static IServiceCollection WithFunc<TService>(this IServiceCollection serviceCollection) where TService : class {
var serviceType = typeof(TService);
var serviceDescriptor = serviceCollection.LastOrDefault(x => x.ServiceType == serviceType);
Debug.Assert(serviceDescriptor != null);
serviceCollection.Add(ServiceDescriptor.Describe(typeof(Func<TService>),
scope => new Func<TService>(scope.GetRequiredService<TService>),
serviceDescriptor.Lifetime));
return serviceCollection;
}
usage
[Fact]
void with_func() {
var services = new ServiceCollection()
.AddTransient<IFoo, Foo>().WithFunc<IFoo>()
.BuildServiceProvider();
var fooFunc = services.GetRequiredService<Func<IFoo>>();
Assert.NotNull(fooFunc);
}
more detail in gist https://gist.github.com/leoninew/d2f174fe1422e453c60fb78e69342310

MVC Get/Impersonate Windows User In Repository

I have an intranet application that uses the Windows username and passes that to a procedure to return data.
I'm using dependency injection, but I don't believe I have the method to get the username separated properly.
I'm trying to keep this secure by not passing in the username as a parameter, but I also want to be able to impersonate (or bypass my GetWindowsUser() method) and send in another username so I can test results for other users.
One idea I had for this was to set a session variable in another page with another (impersonated) username, then check if that session variable exists first before grabbing the actual user name, but I couldn't figure out how to access the session variable in the repository.
WEB API CONTROLLER
public class DropDownDataController : ApiController
{
private IDropDownDataRepository _dropDownDataRepository;
//Dependency Injection using Unity.WebAPI NuGet Package
public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
{
_dropDownDataRepository = dropDownDataRepository;
}
[HttpGet]
public HttpResponseMessage MyList()
{
try
{
return _dropDownDataRepository.MyList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
REPOSITORY
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private DatabaseEntities db = new DatabaseEntities();
public HttpResponseMessage MyList()
{
//(This should be separated somehow, right?)
//Create a new instance of the Utility class
Utility utility = new Utility();
//Grab the windowsUser from the method
var windowsUser = utility.GetWindowsUser();
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.myProcedure(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
INTERFACE
public interface IDropDownDataRepository : IDisposable
{
HttpResponseMessage MyList();
}
UTILITY CLASS
public class Utility
{
public string GetWindowsUser()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
return windowsUser;
}
}
UPDATE 1
In addition to what Nikolai and Brendt posted below, the following is also needed to allow Web Api controllers work with the session state.
Accessing Session Using ASP.NET Web API
Abstract the Utility class and inject it into the repository.
Then you can stub or mock for testing.
public interface IUtility
{
string GetWindowsUser();
}
public class TestUtility : IUtility
{
public string GetWindowsUser()
{
return "TestUser";
}
}
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private IUtility _utility;
public DropDownDataRepository(IUtility utility)
{
_utility = utility;
}
}
EDIT
Also the repository should not return an HTTPResponseMessage type it should just return a List<T> of the domain model you're accessing.
i.e.
public List<Model> MyList()
{
//Grab the windowsUser from the method
var windowsUser = _utility.GetWindowsUser();
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.myProcedure(windowsUser)
select p).ToList();
return sourceQuery
}
Then move the JSON portion to the controller.
One idea I had for this was to set a session variable in another page
with another (impersonated) username, then check if that session
variable exists first before grabbing the actual user name, but I
couldn't figure out how to access the session variable in the
repository.
Potentially, if you add in a dependency to session, you need to isolate it, e.g.
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
// ... other fields
private ISession session;
public DropDownDataRepository(ISession session)
{
this.session = session;
}
public HttpResponseMessage MyList()
{
var myUserName = this.session.UserName;
// ... etc
With ISession being something like:
public interface ISession
{
string UserName { get; }
}
Implemented as:
public class MySession : ISession
{
public string UserName
{
get
{
// potentially do some validation and return a sensible default if not present in session
return HttpContext.Current.Session["UserName"].ToString();
}
}
}
Of course there is the potential to decouple this MySession class from HttpContext if desired.
With regards to this:
//(This should be separated somehow, right?)
//Create a new instance of the Utility class
Utility utility = new Utility();
Yes, anytime you create a new object you are tightly coupling them together, which will give you issues, for example, if you try to unit test it in isolation.
In this instance you could extract an IUtility interface from Utility:
public class Utility : IUtility
{
string GetWindowsUser();
}
Then:
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
// ... other fields
private IUtility utility;
public DropDownDataRepository(IUtility utility)
{
this.utility = utility;
// .... etc
Then you have removed the depenedency between Utility and DropDownDataRepository, and can substitute in another type or mock with ease.
I got a lot of help from Nikolai and Brent and got most of the way there with their posted answers, but ended up figuring out the complete answer on my own. The problems I was having were related to not being able to access session variables in a WebAPI. So, I'm sure there are cleaner solutions to this, but I definitely improved what I had and came up with the following code, which works.
This answer was needed to allow access to the session variable in Web Api - Accessing Session Using ASP.NET Web API
GLOBAL.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
UnityConfig.RegisterComponents();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
//Added to allow use of session state in Web API
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
//Added to allow use of session state in Web API
private bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
}
protected void Session_Start(Object sender, EventArgs e)
{
//Default set the session variable to none
Session["_impersonatedUser"] = "none";
}
protected void Session_End(Object sender, EventArgs e)
{
//Reset the session variable to blank
Session["_impersonatedUser"] = "";
}
}
UNITY.config
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterType<IDropDownDataRepository, DropDownDataRepository>();
container.RegisterType<IUtilityRepository, UtilityRepository>();
container.RegisterType<ISessionRepository, SessionRepository>();
//MVC5
//Unity.MVC5 NuGet Package
DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));
//WEB API
//Unity.WebApi NuGet Package
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
}
}
WEB API CONTROLLER
public class DropDownDataController : ApiController
{
private IDropDownDataRepository _dropDownDataRepository;
//Dependency Injection using Unity.WebAPI NuGet Package
public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
{
_dropDownDataRepository = dropDownDataRepository;
}
[HttpGet]
public HttpResponseMessage MyList()
{
try
{
var sourceQuery = _dropDownDataRepository.MyList();
//JSON stuff moved to controller
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
protected override void Dispose(bool disposing)
{
_dropDownDataRepository.Dispose();
base.Dispose(disposing);
}
}
DROPDOWNDATA REPOSITORY
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private DatabaseEntities db = new DatabaseEntities();
private IUtilityRepository _utilityRepository;
private ISessionRepository _sessionRepository;
//Dependency Injection of Utility and Session
public DropDownDataRepository(IUtilityRepository utilityRepository, ISessionRepository sessionRepository)
{
_utilityRepository = utilityRepository;
_sessionRepository = sessionRepository;
}
//Changed to a list here
public List<MyProcedure> MyList()
{
string windowsUser;
//Check the session variable to see if a user is being impersonated
string impersonatedUser = _sessionRepository.ImpersonatedUser;
//Grab the windowsUser from the Utility Repository
windowsUser = _utilityRepository.GetWindowsUser();
if (impersonatedUser != "none")
{
windowsUser = impersonatedUser;
}
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.MyProcedure(windowsUser)
select p).ToList();
return sourceQuery;
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
DROPDOWNDATA INTERFACE
public interface IDropDownDataRepository : IDisposable
{
//Changed to list here
List<MyProcedure> MyList();
}
UTILITY REPOSITORY
public class UtilityRepository : IUtilityRepository
{
public string GetWindowsUser()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
return windowsUser;
}
}
UTILITY INTERFACE
public interface IUtilityRepository
{
string GetWindowsUser();
}
SESSION REPOSITORY
public class SessionRepository : ISessionRepository
{
public string ImpersonatedUser
{
get
{
return HttpContext.Current.Session["_impersonatedUser"].ToString();
}
}
}
SESSION INTERFACE
public interface ISessionRepository
{
string ImpersonatedUser { get; }
}

Resources