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:
Morten@Thinkpad-X220
2012-10-05 12:59:59 -02:00
parent 590bc23ef6
commit ca6356170c
9 changed files with 432 additions and 1 deletions

View File

@@ -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; }
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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]; }
}
}
}

View 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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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();

View File

@@ -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" />