Trying to convert WIF 4.0 custom STS (from project template) to use .NET 4.5 - wif

I have successfully created a working custom STS using the project templates for the passive and active cases that came with the most recent WIF SDK (for .NET 4.0). Everything works as desired.
I am now trying to upgrade my web applications and services to .NET 4.5, including my custom STS. I have been able to map all of the namespaces/classes from Microsoft.IdentityModel.xxx to the new namespaces/classes built into the framework with 1 exception - WSTrustServiceHostFactory.
That class no longer seems to exist, and I cannot figure out how to replace the functionality it provided. Namely, from this link:
http://msdn.microsoft.com/en-us/library/microsoft.identitymodel.protocols.wstrust.wstrustservicehostfactory
<%#ServiceHostLanguage="C#"Debug="true"Service="XXX.XXX.MyActiveSTSConfiguration"Factory="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceHostFactory"%>
My passive STS is currently working after upgrading to 4.5. I need to find an appropriate/recommended method of instantiating an active SecurityTokenService using the 4.5 framework (ideally, using web.config for most configuration as in the SDK project templates). Any suggestions are appreciated.

This turned out to be pretty simple once I figured a few things out.
Service host markup:
<%# ServiceHost Language="C#" Debug="true" Service="XXX.XXX.MyActiveSTSConfiguration" Factory="XXX.XXX.CustomWSTrustServiceHostFactory" %>
Here's my custom factory class implementation. The key is that since you can no longer use WSTrustServiceFactory to create a WSTrustServiceHost for you, you have to explicitly create one yourself in the CreateServiceHost method overrides.
public class CustomWSTrustServiceHostFactory
: ServiceHostFactory {
/// <summary>
/// Initializes a new instance of the <see cref="CustomWSTrustServiceHostFactory"/> class.
/// </summary>
public CustomWSTrustServiceHostFactory()
: base() { }
/// <summary>
/// Creates and configures a <see cref="WSTrustServiceHost"/> with a specific base address.
/// </summary>
/// <param name="serviceType">Specifies the type of service to host (ignored).</param>
/// <param name="baseAddresses">The <see cref="T:Uri"/> array that contains the base addresses for the service.</param>
/// <returns>A <see cref="WSTrustServiceHost"/> with a specific base address.</returns>
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) {
var config = new PortalActiveSTSConfiguration();
var host = new WSTrustServiceHost(config, baseAddresses);
//var host = base.CreateServiceHost(serviceType, baseAddresses);
var serviceBehavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
serviceBehavior.AddressFilterMode = AddressFilterMode.Any;
return host;
}
/// <summary>
/// Creates and configures a <see cref="WSTrustServiceHost"/> with a specific base address.
/// </summary>
/// <param name="constructorString">The constructor string (ignored).</param>
/// <param name="baseAddresses">The <see cref="T:Uri"/> array that contains the base addresses for the service.</param>
/// <returns></returns>
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses) {
var config = new PortalActiveSTSConfiguration();
var host = new WSTrustServiceHost(config, baseAddresses);
//var host = base.CreateServiceHost(constructorString, baseAddresses);
var serviceBehavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
serviceBehavior.AddressFilterMode = AddressFilterMode.Any;
return host;
}
}
Custom service configuration class implementation (basically what came with the WIF 4.0 STS template):
public class MyActiveSTSConfiguration
: SecurityTokenServiceConfiguration {
public MyActiveSTSConfiguration()
: base(
WebConfigurationManager.AppSettings[ISSUER_NAME],
new X509SigningCredentials(
CertificateUtil.GetCertificate(
StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint,
WebConfigurationManager.AppSettings[SIGNING_CERTIFICATE_THUMBPRINT],
true)
)
) {
this.SecurityTokenService = typeof(MyActiveSTS);
}
}

Related

Validating Configuration in MVC Core

I have a section in appsettings.json which contains a list of libraries and their dependencies, and how to configure them in different execution environments.I'd like to be able to validate that the library collection includes all the dependencies.
That's easy enough to do with a little recursion. But I can't figure out how to override the configuration binding process so that I can do the validation.
The only way I've come up with is to create a raw collection of the libraries, based on appconfig.json, and then create a service which validates the collection and makes it available. Something like:
public class RawLibraries : List<Library>
{
}
public class LibraryResolver
{
public LibraryResolver( IOptions<RawLibraries> rawLibs, ILogger logger )
{
// validate rawLibs and log errors
}
// ...implementation
}
services.Configure<RawLibraries>(Configuration.GetSection("Libraries"));
services.AddSingleton<LibraryResolver, LibraryResolver>();
But this seems convoluted. Thoughts on a better approach?
Why not to follow the authors and write your own extension method with additional validation?
Take a look here. This is the source code of services.Configure<> method:
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Extension methods for adding options services to the DI container.
/// </summary>
public static class OptionsServiceCollectionExtensions
{
...
/// <summary>
/// Registers an action used to configure a particular type of options.
/// </summary>
/// <typeparam name="TOptions">The options type to be configured.</typeparam>
/// <param name="services">The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection" /> to add the services to.</param>
/// <param name="configureOptions">The action used to configure the options.</param>
/// <returns>The <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection" /> so that additional calls can be chained.</returns>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
{
if (services == null)
throw new ArgumentNullException("services");
if (configureOptions == null)
throw new ArgumentNullException("configureOptions");
services.AddSingleton<IConfigureOptions<TOptions>>((IConfigureOptions<TOptions>) new ConfigureOptions<TOptions>(configureOptions));
return services;
}
}
}
As you can see Configure<TOptions> method is an extension method. Simply write your own let say ConfigureAndValidate<TOptions>() extension method which will do proper validation before services.AddSingleton... line.

Unity: Implicit ResolvedParameter for unnamed registrations

The UserService constructor has two parameters, a IUnitOfWork and a IUserRepository:
public UserService(IUnitOfWork unitofWork, IUserRepository userRepository)
{ ... }
I am using named registrations to differentiate between multiple instances of IUnitOfWork, so when registering the UserService with the Unity container, I need to explicitly specify the parameters using an InjectionConstructor:
container.RegisterType<IUserService, UserService>(
new InjectionConstructor(
new ResolvedParameter<IUnitOfWork>("someContext"),
new ResolvedParameter<IUserRepository>()
)
);
Is it possible for new ResolvedParameter<IUserRepository>() to be omitted? I would like Unity to implicitly deduce this parameter since there is no need for a named registration. The code would look like this:
container.RegisterType<IUserService, UserService>(
new InjectionConstructor(
new ResolvedParameter<IUnitOfWork>("someContext")
)
);
This would be done is any case when I don't need to use the InjectionConstructor.
Based on InjectionConstructor, I came up with this RequiredInjectionConstructor. It allows you to specify any set of arguments and it will attempt to find a constructor which is required to have (at a minimum) the passed set of injection parameters. If there are multiple constructors that meet this criteria, it chooses the constructor with the least number of parameters. The remaining constructor parameters are assumed to be unnamed resolved parameters.
I haven't performed a full suite of unit tests on it yet, so let me know if you encounter any issues.
/// <summary>
/// A class that holds the collection of minimum required
/// parameters for a constructor, so that the container can
/// be configured to call this constructor.
/// </summary>
public class RequiredInjectionConstructor : InjectionMember
{
private readonly List<InjectionParameterValue> _requiredParameterValues;
/// <summary>
/// Create a new instance of <see cref="RequiredInjectionConstructor"/> that looks
/// for a constructor with a minimum of the given required set of parameters.
/// </summary>
/// <param name="requiredParameterValues">The values for the parameters, that will
/// be converted to <see cref="InjectionParameterValue"/> objects.</param>
public RequiredInjectionConstructor(params object[] requiredParameterValues)
{
_requiredParameterValues = InjectionParameterValue.ToParameters(requiredParameterValues).ToList();
}
/// <summary>
/// Add policies to the <paramref name="policies"/> to configure the
/// container to call this constructor with the required parameter values.
/// </summary>
/// <param name="serviceType">Interface registered, ignored in this implementation.</param>
/// <param name="implementationType">Type to register.</param>
/// <param name="name">Name used to resolve the type object.</param>
/// <param name="policies">Policy list to add policies to.</param>
public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies)
{
ConstructorInfo ctor = FindConstructor(implementationType, _requiredParameterValues);
IEnumerable<InjectionParameterValue> selectedConstructorParameterValues = GetSelectedConstructorParameterValues(ctor, _requiredParameterValues);
policies.Set<IConstructorSelectorPolicy>(
new SpecifiedConstructorSelectorPolicy(ctor, selectedConstructorParameterValues.ToArray()),
new NamedTypeBuildKey(implementationType, name));
}
private static ConstructorInfo FindConstructor(Type typeToCreate, IEnumerable<InjectionParameterValue> requiredInjectionParameters)
{
var typeToCreateReflector = new ReflectionHelper(typeToCreate);
var matchedConstructors = typeToCreateReflector.InstanceConstructors.
Where(ctor =>
{
var constructorParameterTypes = ctor.GetParameters().Select(info => info.ParameterType);
return requiredInjectionParameters.All(required => constructorParameterTypes.Any(required.MatchesType));
});
if (matchedConstructors.Any())
{
// Prefer the constructor that has the least number of arguments.
// Other preference models could be implemented here.
return matchedConstructors.OrderBy(ctor =>
ctor.GetParameters().Count()).
FirstOrDefault();
}
string signature = string.Join(", ", requiredInjectionParameters.Select(required => required.ParameterTypeName).ToArray());
throw new InvalidOperationException(
string.Format("Unable to find a constructor with the minimum required parameters. Type: {0}, RequiredParameters: {1}",
typeToCreate.FullName,
signature));
}
private static IEnumerable<InjectionParameterValue> GetSelectedConstructorParameterValues(ConstructorInfo ctor, IEnumerable<InjectionParameterValue> requiredInjectionParameters)
{
var injectionParameterValues = new List<InjectionParameterValue>();
foreach (var parameter in ctor.GetParameters())
{
var existingInjectionParameter = requiredInjectionParameters.FirstOrDefault(required => required.MatchesType(parameter.ParameterType));
injectionParameterValues.Add(existingInjectionParameter ?? new ResolvedParameter(parameter.ParameterType));
}
return injectionParameterValues;
}
}
Would you be willing to decorate your constructor with the DependencyAttribute from Unity? This solution is straight forward, built-in, and lets you pick and chose named dependency. But it does 'dirty' your constructor with Unity goo.
public UserService(
[Dependency("someContext")]IUnitOfWork unitofWork,
IUserRepository userRepository)
{ ... }
Another solution would be to write a custom BuilderStrategy and UnityContainerExtension. This could be done with a bit more work.

How do I format the dependency names for RequireJS in MVC when using IIS?

I followed a now deleted blog (see all my code below for what I did) to create a basic RequireJS structure in MVC. While in IIS Express everything works as expected. When in IIS the core module is not found. The only difference between the two (IIS and IIS Express) is that the Map Paths shows wwwroot for IIS and the actual file path where I am developing for IIS Express. Here is the code I am referring to that builds the require script.
if (File.Exists(helper.ViewContext.HttpContext.Server.MapPath(Path.Combine(jsLocation, module + ".js"))))
{
require.AppendLine("require( [ \"" + jsLocation + core + "\" ], function() {");
require.AppendLine(" require( [ \"" + module + "\"] );");
require.AppendLine("});");
}
SOLUTION
Replace the above AppendLines that have dependency names with the following structure. The VirtualPathUtility is what was needed.
require.AppendLine("require( [ \"" + VirtualPathUtility.ToAbsolute("~/" + jsLocation + core) + "\" ], function() {");
END SOLUTION
Here is my file structure (root project MVCTesting)
Scripts
require.js
-AMD
core.js
-Views
-JQuery
AMDRequire.js
If my layout.cshtml contains the require in the following format (Note: for some reason it is allowing me to put the .js in the module name, not sure on that one).
<script type="text/javascript">
require( [ "/Scripts/AMD/core.js" ], function() {});
</script>
What should the format of the dependency name ("/Scripts/AMD/core.js") be to correctly load while in MVC and using IIS? My project name is MVCTesting, so I have tried "/MVCTesting/Scripts/AMD/core.js" and various combinations with the forward slash manually without any luck.
UPDATE
Code in AMDRequire.js
alert("HELLO AMD WORLD!");
IIS Express
Full URL: http://localhost:55916/jquery/amdrequire
Project Url (Project Properties): http://localhost:55916/
Results: Displays alert message HELLO AMD WORLD!
IIS
Full URL: http://localhost/MVCTesting/jquery/amdrequire
Project URL (Project Properties): http://localhost/MVCTesting
Results: No display message, I am assuming because the js file was not file
Final Code (Note, I recommend using AngularJS over this)
#region DOJO AMD Loader
/// <summary>
/// Create the javascript required to use DOJO while following AMD specifications
///
/// Just place #Html.DOJOAMDLoader() in your Layout or other cshtml that you want to use
/// this and it will populate the dojoConfig, the script include for dojo, and the current
/// page's AMD JavaScript file.
/// </summary>
/// <param name="helper"></param>
/// <param name="bsndm"></param>
/// <param name="btsRoot"></param>
/// <returns></returns>
public static MvcHtmlString DOJOAMDLoader(this HtmlHelper helper)
{
var action = helper.ViewContext.RouteData.Values["action"].ToString().ToLower();
var controller = helper.ViewContext.RouteData.Values["controller"].ToString().ToLower();
return helper.CreateDOJORequire(controller, action,
BuildInitJsLogging(ConfigurationManager.AppSettings["JavascriptLoggingLevel"],
ConfigurationManager.AppSettings["JavascriptLoggingAllowDisplayOverride"]));
}
/// <summary>
/// Kick off the custom js logging, display is handled inside the logging JS
/// </summary>
/// <param name="logLevel">The state of the js log (expects Off, On, File)</param>
/// <param name="displayOverride">For failure to log to files, display to screen override</param>
/// <returns></returns>
private static string BuildInitJsLogging(string logLevel, string displayOverride)
{
//This is not used for non system pages or test pages
if (logLevel == null)
{
return null;
}
var updateVariable = new StringBuilder();
updateVariable.AppendLine("require(['logging', 'dojo/domReady!'], function (logging) {");
updateVariable.AppendFormat("logging.initJsLogging('{0}','{1}');", logLevel, displayOverride);
updateVariable.AppendLine("});");
return updateVariable.ToString();
}
/// <summary>
/// This builds the script that will be placed on the page for DOJOAMDLoader
///
/// Included scripts will be the dojoConfig, the script include for dojo, and the currnet
/// page's AMD JavaScript file.
/// </summary>
/// <param name="helper"></param>
/// <param name="controller"></param>
/// <param name="action"></param>
/// <returns></returns>
private static MvcHtmlString CreateDOJORequire(this HtmlHelper helper, string controller
, string action, string javascriptLogging)
{
//Don't build require string if there is not an amd script
if (!File.Exists(helper.ViewContext.HttpContext.Server.MapPath(
GetAbsolutePath(Path.Combine("Scripts", "AMD", "Views", controller, action + ".js")))))
{
return null;
}
//TODO:HP:At the end of the project before going live, merge these
//into as few AppendLine statements as possible.
var require = new StringBuilder();
require.AppendLine("<script>");
require.AppendLine("dojoConfig = {");
require.AppendLine(" async: true,");
require.AppendLine(" packages: [");
require.AppendLine(BuildPackageScriptText("jquery", "Scripts/ThirdParty/jQuery", "jquery-1.9.1", true));
require.AppendLine(BuildPackageScriptText("jquery-ui", "Scripts/ThirdParty/jQuery", "jquery-ui-1.10.2"));
require.Append(BuildPackageScriptText("jquery-loadmask", "Scripts/ThirdParty/jQuery", "jquery.loadmask"));
require.AppendLine(BuildPackageScriptText("jsfunctions", "Scripts/Common", "jsfunctions"));
require.AppendLine(BuildPackageScriptText("logging", "Scripts/TJPackages", "JsLogging"));
require.AppendLine(BuildPackageScriptText(action, string.Format("Scripts/AMD/Views/{0}", controller), action));
require.AppendLine(" ]");
require.AppendLine("};");
require.AppendLine("</script>");
require.AppendLine("<script src='" + GetAbsolutePath("Scripts/ThirdParty/ESRI/init.js") + "'></script>");
require.AppendLine("<script>");
require.AppendLine(javascriptLogging);
require.AppendFormat("require(['{0}', 'dojo/domReady!'],function({0})", action);
require.AppendLine("{");
//require.AppendLine("debugger;");
require.AppendFormat(" {0}.init();", action);
require.AppendLine("});");
require.AppendLine("</script>");
return new MvcHtmlString(require.ToString());
}
/// <summary>
/// Common format for packages in the dojoConfig
/// </summary>
/// <param name="name"></param>
/// <param name="location"></param>
/// <param name="main"></param>
/// <param name="isFirst">Set to true for the first package and the comma will NOT be appended</param>
/// <param name="isCDN">Set to true if using a CDN location for your javascript</param>
/// <returns></returns>
private static string BuildPackageScriptText(string name, string location, string main, bool isFirst = false, bool isCDN = false)
{
var sb = new StringBuilder();
if (!isFirst)
{
sb.Append(",");
}
sb.Append("{");
sb.AppendFormat("name: '{0}', location: '{1}', main: '{2}'", name, isCDN ? location : GetAbsolutePath(location), main);
sb.Append("}");
return sb.ToString();
}
#endregion
/// <summary>
/// Convert the path to work in IIS for MVC
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private static string GetAbsolutePath(string path)
{
return VirtualPathUtility.ToAbsolute("~/" + path);
}
I think you need to rewrite the C# code to use the Url.Content() function:
Converts a virtual (relative) path to an application absolute path.
But to be sure you should test if it does the job for applications in a subfolder (i.e. MVCTesting). In your code (e.g. the _layout file) just write:
#Url.Content("/Scripts/AMD/core.js")
On localhost the output should be something like /Scripts/AMD/core.js, but in IIS it should be /MVCTesting/Scripts/AMD/core.js. Let me know if this is the case.

Hook Unity into Web API Filter Attributes

I have Unity running great for all the controllers in my ASP.NET Web API project - just using the default set up that comes out of the NuGet box. I have also managed to hook it up to MVC Filter Attributes - but can't seem to do the same for ASP.NET Web API filter attributes.
How to I extend this default implementation to inject a dependency into an ActionFilterAttribute, for example...
public class BasicAuthenticationAttribute : ActionFilterAttribute
{
[Dependency]
public IMyService myService { get; set; }
public BasicAuthenticationAttribute()
{
}
}
This filter is applied to controllers using attributes:
[BasicAuthentication]
I'm pretty sure I need to hook up the Unity container so it handles the creation of the attribute class, but need some clues about where to start as it does not use the same extensibility points as the MVC filters.
I just wanted to add, other things I have tried include service location rather than dependency injection, but the DependencyResolver you get back is not the same one you configure.
// null
var service = actionContext.Request.GetDependencyScope().GetService(typeof(IMyService));
Or
// null
var service = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IApiUserService));
The problem is that the Attribute class is created by .NET and not by the WebAPI framework.
Before reading farther, did you forget to configure your DependencyResolver with your IApiUserService?
(IUnityContainer)container;
container.RegisterType<IApiUserService, MyApiUserServiceImpl>();
...
var service = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IApiUserService));
I created an App_Start\UnityConfig class that holds my UnityContainer:
public class UnityConfig {
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() => {
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Gets the configured Unity container.
/// </summary>
public static IUnityContainer GetConfiguredContainer() {
return container.Value;
}
#endregion
public static void Configure(HttpConfiguration config) {
config.DependencyResolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());
}
/// <summary>Registers the type mappings with the Unity container.</summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to
/// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
private static void RegisterTypes(IUnityContainer container) {
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterType<MyClass>(new PerRequestLifetimeManager(), new InjectionConstructor("connectionStringName"));
}
}
The UnityDependencyResolver and PerRequestLifetimeManager came from this blog post and Unity.WebApi (Project/Nuget Package) which I internalized. (since it's a bootstrap)
When I need to make use of the UnityContainer in my other code, I passed it into the constructor:
config.Filters.Add(new MyFilterAttribute(UnityConfig.GetConfiguredContainer()));

Is there any advantage in using a "repository factory" with ASP.NET MVC4 and Entity Framework?

I am developing an application and I started to use as my base some code from an example by John Papa. Looking on the web I found this same code and it appears in an answer to a question on
Stackoverflow. Here is the question:
How to de-attach an entity from a Context in Entity Framework?
It's in the answer that was given by: SynerCoder
One part of the answer suggests the following class that is used to provide a repository from a dictionary of cached repositories. Can someone help me out and tell me is there really
an advantage in doing this. I understand the code but can't see the point of keeping repositories in a dictionary. Would it not be the case that every new web request would see an
empty dictionary and have to get / make a new repository anyway.
Data/Helpers/IRepositoryProvider.cs
using System;
using System.Collections.Generic;
using System.Data.Entity;
using Data.Contracts;
namespace Data.Helpers
{
/// <summary>
/// A maker of Repositories.
/// </summary>
/// <remarks>
/// An instance of this class contains repository factory functions for different types.
/// Each factory function takes an EF <see cref="DbContext"/> and returns
/// a repository bound to that DbContext.
/// <para>
/// Designed to be a "Singleton", configured at web application start with
/// all of the factory functions needed to create any type of repository.
/// Should be thread-safe to use because it is configured at app start,
/// before any request for a factory, and should be immutable thereafter.
/// </para>
/// </remarks>
public class RepositoryFactories
{
/// <summary>
/// Return the runtime repository factory functions,
/// each one is a factory for a repository of a particular type.
/// </summary>
/// <remarks>
/// MODIFY THIS METHOD TO ADD CUSTOM FACTORY FUNCTIONS
/// </remarks>
private IDictionary<Type, Func<DbContext, object>> GetFactories()
{
return new Dictionary<Type, Func<DbContext, object>>
{
//If you have an custom implementation of an IRepository<T>
//{typeof(IArticleRepository), dbContext => new ArticleRepository(dbContext)}
};
}
/// <summary>
/// Constructor that initializes with runtime repository factories
/// </summary>
public RepositoryFactories()
{
_repositoryFactories = GetFactories();
}
/// <summary>
/// Constructor that initializes with an arbitrary collection of factories
/// </summary>
/// <param name="factories">
/// The repository factory functions for this instance.
/// </param>
/// <remarks>
/// This ctor is primarily useful for testing this class
/// </remarks>
public RepositoryFactories(IDictionary<Type, Func<DbContext, object>> factories)
{
_repositoryFactories = factories;
}
/// <summary>
/// Get the repository factory function for the type.
/// </summary>
/// <typeparam name="T">Type serving as the repository factory lookup key.</typeparam>
/// <returns>The repository function if found, else null.</returns>
/// <remarks>
/// The type parameter, T, is typically the repository type
/// but could be any type (e.g., an entity type)
/// </remarks>
public Func<DbContext, object> GetRepositoryFactory<T>()
{
Func<DbContext, object> factory;
_repositoryFactories.TryGetValue(typeof(T), out factory);
return factory;
}
/// <summary>
/// Get the factory for <see cref="IRepository{T}"/> where T is an entity type.
/// </summary>
/// <typeparam name="T">The root type of the repository, typically an entity type.</typeparam>
/// <returns>
/// A factory that creates the <see cref="IRepository{T}"/>, given an EF <see cref="DbContext"/>.
/// </returns>
/// <remarks>
/// Looks first for a custom factory in <see cref="_repositoryFactories"/>.
/// If not, falls back to the <see cref="DefaultEntityRepositoryFactory{T}"/>.
/// You can substitute an alternative factory for the default one by adding
/// a repository factory for type "T" to <see cref="_repositoryFactories"/>.
/// </remarks>
public Func<DbContext, object> GetRepositoryFactoryForEntityType<T>() where T : class
{
return GetRepositoryFactory<T>() ?? DefaultEntityRepositoryFactory<T>();
}
/// <summary>
/// Default factory for a <see cref="IRepository{T}"/> where T is an entity.
/// </summary>
/// <typeparam name="T">Type of the repository's root entity</typeparam>
protected virtual Func<DbContext, object> DefaultEntityRepositoryFactory<T>() where T : class
{
return dbContext => new EFRepository<T>(dbContext);
}
/// <summary>
/// Get the dictionary of repository factory functions.
/// </summary>
/// <remarks>
/// A dictionary key is a System.Type, typically a repository type.
/// A value is a repository factory function
/// that takes a <see cref="DbContext"/> argument and returns
/// a repository object. Caller must know how to cast it.
/// </remarks>
private readonly IDictionary<Type, Func<DbContext, object>> _repositoryFactories;
}
}
Here's the code that calls the factory:
using System;
using Data.Contracts;
using Data.Helpers;
using Models;
namespace Data
{
/// <summary>
/// The "Unit of Work"
/// 1) decouples the repos from the controllers
/// 2) decouples the DbContext and EF from the controllers
/// 3) manages the UoW
/// </summary>
/// <remarks>
/// This class implements the "Unit of Work" pattern in which
/// the "UoW" serves as a facade for querying and saving to the database.
/// Querying is delegated to "repositories".
/// Each repository serves as a container dedicated to a particular
/// root entity type such as a <see cref="Url"/>.
/// A repository typically exposes "Get" methods for querying and
/// will offer add, update, and delete methods if those features are supported.
/// The repositories rely on their parent UoW to provide the interface to the
/// data layer (which is the EF DbContext in this example).
/// </remarks>
public class UnitOfWork : IUnitOfWork, IDisposable
{
public UnitOfWork(IRepositoryProvider repositoryProvider)
{
CreateDbContext();
repositoryProvider.DbContext = DbContext;
RepositoryProvider = repositoryProvider;
}
// Repositories
public IRepository<Event> Events { get { return GetStandardRepo<Event>(); } }
public IRepository<Candidate> Candidates { get { return GetStandardRepo<Candidate>(); } }
/// <summary>
/// Save pending changes to the database
/// </summary>
public void Commit()
{
//System.Diagnostics.Debug.WriteLine("Committed");
DbContext.SaveChanges();
}
protected void CreateDbContext()
{
DbContext = new UnicornsContext();
// Do NOT enable proxied entities, else serialization fails
DbContext.Configuration.ProxyCreationEnabled = false;
// Load navigation properties explicitly (avoid serialization trouble)
DbContext.Configuration.LazyLoadingEnabled = false;
// Because Web API will perform validation, I don't need/want EF to do so
DbContext.Configuration.ValidateOnSaveEnabled = false;
}
protected IRepositoryProvider RepositoryProvider { get; set; }
private IRepository<T> GetStandardRepo<T>() where T : class
{
return RepositoryProvider.GetRepositoryForEntityType<T>();
}
private T GetRepo<T>() where T : class
{
return RepositoryProvider.GetRepository<T>();
}
private UnicornsContext DbContext { get; set; }
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (DbContext != null)
{
DbContext.Dispose();
}
}
}
#endregion
}
}
It seems to me that the factory makes things more complicated that they need be. Am I correct and should I do this a simpler way such as with something like:
private IRepository<xx> = new GenericRepository<xx>(dbContext);
One more point. In my application I am using Unity. So would it be even easier to just specify the needed repositories in the constructor and have Unity create the repositories for me. If I did this then is there a way I could pass around the dbContext so it could be used by Unity when creating the repository? Has anyone used Unity to create repositories like this?
OK. Here's my best shot:
The point of keeping repositories in a cache is to ensure that the repository is only initiated once per request. The repository cache is in the RepositoryProvider class and is exposed to the UnitOfWork by the GetRepositoryForEntityType method. So the advantage is that the unit of work is not concerned with caching or creation of repositories.
The RepositoryProvider class is instantiated once per unit of work. (NB - it is desirable to create the repositories new for every request). The RepositoryProvider keeps the repositories in a dictionary using the type as a key. This is fine when using the generic repository base which has a Type parameter. But what if you have created a custom repository? In this example the creation of repositories by type is handed off to the RepositoryFactories class via the MakeRepository method. The advantage is that creating repositories is separated from caching.
The RepositoryFactories class knows when to make a custom repository because it contains a dictionary that uses Type as a key and a function as a value. The function is the constructor for a custom repository. If there's a value in the dictionary then use that constructor otherwise just use the generic base constructor.
All this means that as you add entities you do not have to modify any of these classes unless you create a custom repository. And when you do that all you have to do is add an entry to the dictionary in RepositoryFactories

Resources