Adds Repository Resolver and backing config section implementation U4-988
Will need a bit of refactoring because of the way UnitOfWork is currently implemented.
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Repositories
|
||||
{
|
||||
internal class RepositoryConfigurationSection : ConfigurationSection
|
||||
{
|
||||
[ConfigurationProperty("repositories", IsDefaultCollection = false)]
|
||||
[ConfigurationCollection(typeof(RepositoryCollection),
|
||||
AddItemName = "add",
|
||||
ClearItemsName = "clear",
|
||||
RemoveItemName = "remove")]
|
||||
public RepositoryCollection Repositories
|
||||
{
|
||||
get
|
||||
{
|
||||
return (RepositoryCollection)base["repositories"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class RepositoryCollection : ConfigurationElementCollection
|
||||
{
|
||||
public RepositoryCollection()
|
||||
{
|
||||
Console.WriteLine("RepositoryCollection Constructor");
|
||||
}
|
||||
|
||||
public RepositoryElement this[int index]
|
||||
{
|
||||
get { return (RepositoryElement)BaseGet(index); }
|
||||
set
|
||||
{
|
||||
if (BaseGet(index) != null)
|
||||
{
|
||||
BaseRemoveAt(index);
|
||||
}
|
||||
BaseAdd(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(RepositoryElement repositoryElement)
|
||||
{
|
||||
BaseAdd(repositoryElement);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
BaseClear();
|
||||
}
|
||||
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new RepositoryElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((RepositoryElement)element).Name;
|
||||
}
|
||||
|
||||
public void Remove(RepositoryElement repositoryElement)
|
||||
{
|
||||
BaseRemove(repositoryElement.Name);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
BaseRemoveAt(index);
|
||||
}
|
||||
|
||||
public void Remove(string name)
|
||||
{
|
||||
BaseRemove(name);
|
||||
}
|
||||
}
|
||||
|
||||
internal class RepositoryElement : ConfigurationElement
|
||||
{
|
||||
private const string NameKey = "name";
|
||||
private const string ModelTypeKey = "modelType";
|
||||
private const string RepositoryTypeKey = "repositoryType";
|
||||
|
||||
public RepositoryElement() { }
|
||||
|
||||
public RepositoryElement(string name, string modelType, string repositoryType)
|
||||
{
|
||||
Name = name;
|
||||
ModelType = modelType;
|
||||
RepositoryType = repositoryType;
|
||||
}
|
||||
|
||||
[ConfigurationProperty(NameKey, IsRequired = true, IsKey = true)]
|
||||
public string Name
|
||||
{
|
||||
get { return (string)this[NameKey]; }
|
||||
|
||||
set { this[NameKey] = value; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(ModelTypeKey, IsRequired = true, IsKey = false)]
|
||||
public string ModelType
|
||||
{
|
||||
get { return (string)this[ModelTypeKey]; }
|
||||
|
||||
set { this[ModelTypeKey] = value; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(RepositoryTypeKey, IsRequired = true, IsKey = false)]
|
||||
public string RepositoryType
|
||||
{
|
||||
get { return (string)this[RepositoryTypeKey]; }
|
||||
|
||||
set { this[RepositoryTypeKey] = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Repositories
|
||||
{
|
||||
internal sealed class RepositoryMappingCollection : ConfigurationElementCollection
|
||||
{
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new RepositoryMappingElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((RepositoryMappingElement)element).InterfaceShortTypeName;
|
||||
}
|
||||
|
||||
public override ConfigurationElementCollectionType CollectionType
|
||||
{
|
||||
get { return ConfigurationElementCollectionType.BasicMap; }
|
||||
}
|
||||
|
||||
protected override string ElementName
|
||||
{
|
||||
get { return RepositoryMappingConstants.ConfigurationElementName; }
|
||||
}
|
||||
|
||||
public RepositoryMappingElement this[int index]
|
||||
{
|
||||
get { return (RepositoryMappingElement)this.BaseGet(index); }
|
||||
set
|
||||
{
|
||||
if (this.BaseGet(index) != null)
|
||||
{
|
||||
this.BaseRemoveAt(index);
|
||||
}
|
||||
this.BaseAdd(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
public new RepositoryMappingElement this[string interfaceShortTypeName]
|
||||
{
|
||||
get { return (RepositoryMappingElement)this.BaseGet(interfaceShortTypeName); }
|
||||
}
|
||||
|
||||
public bool ContainsKey(string keyName)
|
||||
{
|
||||
bool result = false;
|
||||
object[] keys = this.BaseGetAllKeys();
|
||||
foreach (object key in keys)
|
||||
{
|
||||
if ((string)key == keyName)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Core.Configuration.Repositories
|
||||
{
|
||||
internal static class RepositoryMappingConstants
|
||||
{
|
||||
internal const string CacheProviderFullTypeNameAttributeName = "cacheProviderFullTypeName";
|
||||
internal const string ConfigurationPropertyName = "repositoryMappings";
|
||||
internal const string ConfigurationElementName = "repositoryMapping";
|
||||
internal const string InterfaceShortTypeNameAttributeName = "interfaceShortTypeName";
|
||||
internal const string RepositoryFullTypeNameAttributeName = "repositoryFullTypeName";
|
||||
internal const string RepositoryMappingsConfigurationSectionName = "repositoryMappingsConfiguration";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Repositories
|
||||
{
|
||||
internal sealed class RepositoryMappingElement : ConfigurationElement
|
||||
{
|
||||
[ConfigurationProperty(RepositoryMappingConstants.InterfaceShortTypeNameAttributeName,
|
||||
IsKey = true, IsRequired = true)]
|
||||
public string InterfaceShortTypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[RepositoryMappingConstants.InterfaceShortTypeNameAttributeName];
|
||||
}
|
||||
set
|
||||
{
|
||||
this[RepositoryMappingConstants.InterfaceShortTypeNameAttributeName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(RepositoryMappingConstants.RepositoryFullTypeNameAttributeName,
|
||||
IsRequired = true)]
|
||||
public string RepositoryFullTypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[RepositoryMappingConstants.RepositoryFullTypeNameAttributeName];
|
||||
}
|
||||
set
|
||||
{
|
||||
this[RepositoryMappingConstants.RepositoryFullTypeNameAttributeName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(RepositoryMappingConstants.CacheProviderFullTypeNameAttributeName,
|
||||
IsRequired = true)]
|
||||
public string CacheProviderFullTypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)this[RepositoryMappingConstants.CacheProviderFullTypeNameAttributeName];
|
||||
}
|
||||
set
|
||||
{
|
||||
this[RepositoryMappingConstants.CacheProviderFullTypeNameAttributeName] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Repositories
|
||||
{
|
||||
internal class RepositorySettings : ConfigurationSection
|
||||
{
|
||||
[ConfigurationProperty(RepositoryMappingConstants.ConfigurationPropertyName,
|
||||
IsDefaultCollection = true)]
|
||||
public RepositoryMappingCollection RepositoryMappings
|
||||
{
|
||||
get { return (RepositoryMappingCollection)base[RepositoryMappingConstants.ConfigurationPropertyName]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
164
src/Umbraco.Core/Persistence/RepositoryResolver.cs
Normal file
164
src/Umbraco.Core/Persistence/RepositoryResolver.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration.Repositories;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
{
|
||||
internal class RepositoryResolver
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, object> Repositories = new ConcurrentDictionary<string, object>();
|
||||
|
||||
//GetRepository based on Repository Interface and Model Entity.
|
||||
//Check if Dictionary contains the interface name of the Repository, ea. IContentRepository
|
||||
//- If it does return the repository from the dictionary and set the new UnitOfWork object
|
||||
//Otherwise look for the full type for the repository in config
|
||||
//- If type exists check depedencies, create new object, add it to dictionary and return it
|
||||
//Otherwise look for an entity type in the config
|
||||
//- If type exists check dependencies, create new object, add it to dictionary and return it
|
||||
//If we have come this far the correct types wasn't found and we throw an exception
|
||||
internal static TRepository ResolveByType<TRepository, TEntity>(IUnitOfWork<Database> unitOfWork)
|
||||
where TRepository : class, IRepository<TEntity>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
//Initialize the provider's default value
|
||||
TRepository repository = default(TRepository);
|
||||
|
||||
string interfaceShortName = typeof(TRepository).Name;
|
||||
string entityTypeName = typeof(TEntity).Name;
|
||||
|
||||
//Check if the repository has already been created and is in the cache
|
||||
if (Repositories.ContainsKey(interfaceShortName))
|
||||
{
|
||||
repository = (TRepository)Repositories[interfaceShortName];
|
||||
if (unitOfWork != null && repository.GetType().IsSubclassOf(typeof(IRepository<TEntity>)))
|
||||
{
|
||||
repository.SetUnitOfWork(unitOfWork);
|
||||
}
|
||||
return repository;
|
||||
}
|
||||
|
||||
var settings =
|
||||
(RepositorySettings)
|
||||
ConfigurationManager.GetSection(RepositoryMappingConstants.RepositoryMappingsConfigurationSectionName);
|
||||
|
||||
Type repositoryType = null;
|
||||
|
||||
//Check if a valid interfaceShortName was passed in
|
||||
if (settings.RepositoryMappings.ContainsKey(interfaceShortName))
|
||||
{
|
||||
repositoryType = Type.GetType(settings.RepositoryMappings[interfaceShortName].RepositoryFullTypeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (RepositoryMappingElement element in settings.RepositoryMappings)
|
||||
{
|
||||
if (element.InterfaceShortTypeName.Contains(entityTypeName))
|
||||
{
|
||||
repositoryType = Type.GetType(settings.RepositoryMappings[element.InterfaceShortTypeName].RepositoryFullTypeName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If the repository type is null we should stop and throw an exception
|
||||
if (repositoryType == null)
|
||||
{
|
||||
throw new Exception(string.Format("No repository matching the Repository interface '{0}' or Entity type '{1}' could be resolved",
|
||||
interfaceShortName, entityTypeName));
|
||||
}
|
||||
|
||||
//Resolve the repository with its constructor dependencies
|
||||
repository = Resolve(repositoryType, unitOfWork) as TRepository;
|
||||
|
||||
//Add the new repository instance to the cache
|
||||
Repositories.AddOrUpdate(interfaceShortName, repository, (x, y) => repository);
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
//Recursive create and dependency check
|
||||
private static object Resolve(Type repositoryType, IUnitOfWork<Database> unitOfWork)
|
||||
{
|
||||
var constructor = repositoryType.GetConstructors().SingleOrDefault();
|
||||
if (constructor == null)
|
||||
{
|
||||
throw new Exception(string.Format("No public constructor was found on {0}", repositoryType.FullName));
|
||||
}
|
||||
|
||||
var constructorArgs = new List<object>();
|
||||
var settings = (RepositorySettings)ConfigurationManager.GetSection(RepositoryMappingConstants.RepositoryMappingsConfigurationSectionName);
|
||||
|
||||
var parameters = constructor.GetParameters();
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
if (parameter.ParameterType.Name.Equals("IUnitOfWork"))
|
||||
{
|
||||
constructorArgs.Add(unitOfWork);
|
||||
}
|
||||
else if (Repositories.ContainsKey(parameter.ParameterType.Name))
|
||||
{
|
||||
var repo = Repositories[parameter.ParameterType.Name];
|
||||
constructorArgs.Add(repo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settings.RepositoryMappings.ContainsKey(parameter.ParameterType.Name))
|
||||
{
|
||||
//Get the Type of the repository and resolve the object
|
||||
var repoType = Type.GetType(settings.RepositoryMappings[parameter.ParameterType.Name].RepositoryFullTypeName);
|
||||
var repo = Resolve(repoType, unitOfWork);
|
||||
|
||||
// Add the new repository instance to the cache
|
||||
Repositories.AddOrUpdate(parameter.ParameterType.Name, repo, (x, y) => repo);
|
||||
|
||||
//Add the new repository to the constructor
|
||||
constructorArgs.Add(repo);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Cannot create the Repository. There was one or more invalid repositoryMapping configuration settings.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var repositoryObj = Activator.CreateInstance(repositoryType, constructorArgs.ToArray());
|
||||
return repositoryObj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register all repositories by interating the configuration and adding the repositories to the internal cache (Dictionary)
|
||||
/// </summary>
|
||||
internal static void RegisterRepositories()
|
||||
{
|
||||
var settings =
|
||||
(RepositorySettings)
|
||||
ConfigurationManager.GetSection(RepositoryMappingConstants.RepositoryMappingsConfigurationSectionName);
|
||||
|
||||
foreach (RepositoryMappingElement element in settings.RepositoryMappings)
|
||||
{
|
||||
if (Repositories.ContainsKey(element.InterfaceShortTypeName)) continue;
|
||||
|
||||
var repositoryType = Type.GetType(settings.RepositoryMappings[element.InterfaceShortTypeName].RepositoryFullTypeName);
|
||||
var repository = Resolve(repositoryType, null);
|
||||
|
||||
//Add the new repository instance to the cache
|
||||
Repositories.AddOrUpdate(element.InterfaceShortTypeName, repository, (x, y) => repository);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of repositories that has been registered
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static int RegisteredRepositories()
|
||||
{
|
||||
return Repositories.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Unit Of Work
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IUnitOfWork<T> : IDisposable
|
||||
{
|
||||
void Commit();
|
||||
T Storage { get; }
|
||||
T Storage { get; }//TODO This won't work! Need to change it
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Unit of Work Provider
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IUnitOfWorkProvider<T>
|
||||
{
|
||||
IUnitOfWork<T> GetUnitOfWork();
|
||||
|
||||
@@ -58,6 +58,11 @@
|
||||
<Compile Include="Configuration\FileSystemProviderElement.cs" />
|
||||
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
|
||||
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
|
||||
<Compile Include="Configuration\Repositories\RepositoryConfigurationSection.cs" />
|
||||
<Compile Include="Configuration\Repositories\RepositoryMappingCollection.cs" />
|
||||
<Compile Include="Configuration\Repositories\RepositoryMappingConstants.cs" />
|
||||
<Compile Include="Configuration\Repositories\RepositoryMappingElement.cs" />
|
||||
<Compile Include="Configuration\Repositories\RepositorySettings.cs" />
|
||||
<Compile Include="CoreBootManager.cs" />
|
||||
<Compile Include="DataTableExtensions.cs" />
|
||||
<Compile Include="DictionaryExtensions.cs" />
|
||||
@@ -98,6 +103,7 @@
|
||||
<Compile Include="Persistence\Repositories\IRepository.cs" />
|
||||
<Compile Include="Persistence\Relators\TabPropertyTypeRelator.cs" />
|
||||
<Compile Include="Persistence\Repositories\Repository.cs" />
|
||||
<Compile Include="Persistence\RepositoryResolver.cs" />
|
||||
<Compile Include="Persistence\UnitOfWork\IUnitOfWork.cs" />
|
||||
<Compile Include="Persistence\UnitOfWork\IUnitOfWorkProvider.cs" />
|
||||
<Compile Include="Persistence\UnitOfWork\PetaPocoUnitOfWork.cs" />
|
||||
|
||||
Reference in New Issue
Block a user