Created RepositoryInstanceResolver/Factory to replace the configuration based instantiation of Repo's.

Added unit tests to support resoling each instance, fixed CodeFirstTests to ensure that the base.TearDown() method is
called. Changed the BaseMapper's to internal.
This commit is contained in:
Shannon Deminick
2012-12-09 09:01:00 +05:00
parent 0c29704d09
commit e0a7be7237
13 changed files with 338 additions and 42 deletions

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core
@@ -96,6 +97,9 @@ namespace Umbraco.Core
/// </summary>
protected virtual void InitializeResolvers()
{
RepositoryInstanceResolver.Current = new RepositoryInstanceResolver(
new RepositoryInstanceFactory());
CacheRefreshersResolver.Current = new CacheRefreshersResolver(
PluginManager.Current.ResolveCacheRefreshers());

View File

@@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Mappers
{
public abstract class BaseMapper
internal abstract class BaseMapper
{
internal abstract void BuildMap();

View File

@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Mappers
/// Represents a <see cref="DictionaryTranslation"/> to DTO mapper used to translate the properties of the public api
/// implementation to that of the database's DTO as sql: [tableName].[columnName].
/// </summary>
public class DictionaryTranslationMapper : BaseMapper
internal class DictionaryTranslationMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache = new ConcurrentDictionary<string, DtoMapModel>();

View File

@@ -0,0 +1,140 @@
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence
{
/// <summary>
/// Used to instantiate each repository type
/// </summary>
public class RepositoryInstanceFactory
{
[RepositoryInstanceType(typeof(IUserTypeRepository))]
internal virtual IUserTypeRepository CreateUserTypeRepository(IUnitOfWork uow)
{
return new UserTypeRepository(
uow,
NullCacheProvider.Current);
}
[RepositoryInstanceType(typeof(IUserRepository))]
internal virtual IUserRepository CreateUserRepository(IUnitOfWork uow)
{
return new UserRepository(
uow,
NullCacheProvider.Current,
CreateUserTypeRepository(uow));
}
[RepositoryInstanceType(typeof(IContentRepository))]
public virtual IContentRepository CreateContentRepository(IUnitOfWork uow)
{
return new ContentRepository(
uow,
RuntimeCacheProvider.Current,
CreateContentTypeRepository(uow),
CreateTemplateRepository(uow));
}
[RepositoryInstanceType(typeof(IContentTypeRepository))]
public virtual IContentTypeRepository CreateContentTypeRepository(IUnitOfWork uow)
{
return new ContentTypeRepository(
uow,
InMemoryCacheProvider.Current,
new TemplateRepository(uow, NullCacheProvider.Current));
}
[RepositoryInstanceType(typeof(IDataTypeDefinitionRepository))]
public virtual IDataTypeDefinitionRepository CreateDataTypeDefinitionRepository(IUnitOfWork uow)
{
return new DataTypeDefinitionRepository(
uow,
NullCacheProvider.Current);
}
//TODO: Shouldn't IDictionaryRepository be public?
[RepositoryInstanceType(typeof(IDictionaryRepository))]
internal virtual IDictionaryRepository CreateDictionaryRepository(IUnitOfWork uow)
{
return new DictionaryRepository(
uow,
InMemoryCacheProvider.Current,
CreateLanguageRepository(uow));
}
[RepositoryInstanceType(typeof(ILanguageRepository))]
public virtual ILanguageRepository CreateLanguageRepository(IUnitOfWork uow)
{
return new LanguageRepository(
uow,
InMemoryCacheProvider.Current);
}
//TODO: Shouldn't IMacroRepository be public?
[RepositoryInstanceType(typeof(IMacroRepository))]
internal virtual IMacroRepository CreateMacroRepository(IUnitOfWork uow)
{
return new MacroRepository(
uow,
InMemoryCacheProvider.Current);
}
[RepositoryInstanceType(typeof(IMediaRepository))]
public virtual IMediaRepository CreateMediaRepository(IUnitOfWork uow)
{
return new MediaRepository(
uow,
RuntimeCacheProvider.Current,
CreateMediaTypeRepository(uow));
}
[RepositoryInstanceType(typeof(IMediaTypeRepository))]
public virtual IMediaTypeRepository CreateMediaTypeRepository(IUnitOfWork uow)
{
return new MediaTypeRepository(
uow,
InMemoryCacheProvider.Current);
}
[RepositoryInstanceType(typeof(IRelationRepository))]
public virtual IRelationRepository CreateRelationRepository(IUnitOfWork uow)
{
return new RelationRepository(
uow,
NullCacheProvider.Current,
CreateRelationTypeRepository(uow));
}
[RepositoryInstanceType(typeof(IRelationTypeRepository))]
public virtual IRelationTypeRepository CreateRelationTypeRepository(IUnitOfWork uow)
{
return new RelationTypeRepository(
uow,
NullCacheProvider.Current);
}
[RepositoryInstanceType(typeof(IScriptRepository))]
public virtual IScriptRepository CreateScriptRepository(IUnitOfWork uow)
{
return new ScriptRepository(uow);
}
[RepositoryInstanceType(typeof(IStylesheetRepository))]
public virtual IStylesheetRepository CreateStylesheetRepository(IUnitOfWork uow)
{
return new StylesheetRepository(uow);
}
[RepositoryInstanceType(typeof(ITemplateRepository))]
public virtual ITemplateRepository CreateTemplateRepository(IUnitOfWork uow)
{
return new TemplateRepository(uow, NullCacheProvider.Current);
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Linq;
using System.Reflection;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence
{
/// <summary>
/// A resolver used to return the current implementation of the RepositoryInstanceFactory
/// </summary>
internal class RepositoryInstanceResolver : SingleObjectResolverBase<RepositoryInstanceResolver,RepositoryInstanceFactory>
{
internal RepositoryInstanceResolver(RepositoryInstanceFactory registrar)
: base(registrar)
{
}
/// <summary>
/// Return the repository based on the type
/// </summary>
/// <typeparam name="TRepository"></typeparam>
/// <param name="unitOfWork"></param>
/// <returns></returns>
internal TRepository ResolveByType<TRepository>(IUnitOfWork unitOfWork)
{
//TODO: REMOVE all of these binding flags once the IDictionaryRepository, IMacroRepository are public! As this probably
// wont work in medium trust!
var createMethod = this.Value.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.First(x => x.GetCustomAttributes(true).OfType<RepositoryInstanceTypeAttribute>()
.Any(instance => instance.InterfaceType.IsType<TRepository>()));
if (createMethod.GetParameters().Count() != 1
|| !createMethod.GetParameters().Single().ParameterType.IsType<IUnitOfWork>())
{
throw new FormatException("The method " + createMethod.Name + " must only contain one parameter of type " + typeof(IUnitOfWork).FullName);
}
if (!createMethod.ReturnType.IsType<TRepository>())
{
throw new FormatException("The method " + createMethod.Name + " must return the type " + typeof(TRepository).FullName);
}
return (TRepository) createMethod.Invoke(this.Value, new object[] {unitOfWork});
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
namespace Umbraco.Core.Persistence
{
/// <summary>
/// Used to decorate each method of the class RepositoryInstanceFactory (or derived classes) to inform the system of what
/// type of Repository the method will be returning.
/// </summary>
/// <remarks>
/// The reason for this attribute is because the RepositoryInstanceFactory (or derived classes) might contain methods multiple
/// methods that return the interface repository being asked for so we cannot simply rely on the return type of the methods.
/// </remarks>
[AttributeUsage(AttributeTargets.Method)]
public class RepositoryInstanceTypeAttribute : Attribute
{
public Type InterfaceType { get; private set; }
public RepositoryInstanceTypeAttribute(Type interfaceType)
{
InterfaceType = interfaceType;
}
}
}

View File

@@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence
{
internal class RepositoryResolver
internal class RepositoryResolver
{
private static readonly ConcurrentDictionary<string, object> Repositories = new ConcurrentDictionary<string, object>();
@@ -32,47 +32,52 @@ namespace Umbraco.Core.Persistence
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 && (typeof(IRepository<TId, TEntity>).IsInstanceOfType(repository)))
{
repository.SetUnitOfWork(unitOfWork);
}
return repository;
}
var settings = Infrastructure.Instance.Repositories;
//Check if the repository has already been created and is in the cache
//SD: Changed to TryGetValue as this will be a bit quicker since if we do a ContainsKey and then resolve,
// the underlying ConcurrentDictionary does this twice and thus does two locks.
object repositoryObject;
if (Repositories.TryGetValue(interfaceShortName, out repositoryObject))
{
repository = (TRepository)repositoryObject;
if (unitOfWork != null && (typeof(IRepository<TId, TEntity>).IsInstanceOfType(repository)))
{
repository.SetUnitOfWork(unitOfWork);
}
return repository;
}
Type repositoryType = null;
repository = RepositoryInstanceResolver.Current.ResolveByType<TRepository>(unitOfWork);
//Check if a valid interfaceShortName was passed in
if (settings.Repository.ContainsKey(interfaceShortName))
{
repositoryType = Type.GetType(settings.Repository[interfaceShortName].RepositoryFullTypeName);
}
else
{
foreach (Repository element in settings.Repository)
{
if (element.InterfaceShortTypeName.Contains(entityTypeName))
{
repositoryType = Type.GetType(settings.Repository[element.InterfaceShortTypeName].RepositoryFullTypeName);
break;
}
}
}
//var settings = Infrastructure.Instance.Repositories;
//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));
}
//Type repositoryType = null;
//Resolve the repository with its constructor dependencies
repository = Resolve(repositoryType, unitOfWork, interfaceShortName) as TRepository;
////Check if a valid interfaceShortName was passed in
//if (settings.Repository.ContainsKey(interfaceShortName))
//{
// repositoryType = Type.GetType(settings.Repository[interfaceShortName].RepositoryFullTypeName);
//}
//else
//{
// foreach (Repository element in settings.Repository)
// {
// if (element.InterfaceShortTypeName.Contains(entityTypeName))
// {
// repositoryType = Type.GetType(settings.Repository[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, interfaceShortName) as TRepository;
//Add the new repository instance to the cache
Repositories.AddOrUpdate(interfaceShortName, repository, (x, y) => repository);

View File

@@ -383,6 +383,9 @@
<Compile Include="Persistence\Repositories\TemplateRepository.cs" />
<Compile Include="Persistence\Repositories\UserRepository.cs" />
<Compile Include="Persistence\Repositories\UserTypeRepository.cs" />
<Compile Include="Persistence\RepositoryInstanceFactory.cs" />
<Compile Include="Persistence\RepositoryInstanceResolver.cs" />
<Compile Include="Persistence\RepositoryInstanceTypeAttribute.cs" />
<Compile Include="Persistence\RepositoryResolver.cs" />
<Compile Include="Persistence\SqlSyntax\DbTypes.cs" />
<Compile Include="Persistence\SqlSyntax\ISqlSyntaxProvider.cs" />

View File

@@ -228,8 +228,11 @@ namespace Umbraco.Tests.CodeFirst
[TearDown]
public override void TearDown()
{
DatabaseContext.Database.Dispose();
base.TearDown();
//reset the app context
DataTypesResolver.Reset();
ApplicationContext.Current = null;

View File

@@ -0,0 +1,52 @@
using System;
using System.Reflection;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Tests.Persistence
{
[TestFixture]
public class RepositoryInstanceResolverTests
{
[SetUp]
public void Setup()
{
RepositoryInstanceResolver.Current = new RepositoryInstanceResolver(
new RepositoryInstanceFactory());
}
[TearDown]
public void Teardown()
{
RepositoryInstanceResolver.Reset();
}
[TestCase(typeof(IUserTypeRepository))]
[TestCase(typeof(IUserRepository))]
[TestCase(typeof(IContentRepository))]
[TestCase(typeof(IMediaRepository))]
[TestCase(typeof(IMediaTypeRepository))]
[TestCase(typeof(IContentTypeRepository))]
[TestCase(typeof(IDataTypeDefinitionRepository))]
[TestCase(typeof(IDictionaryRepository))]
[TestCase(typeof(ILanguageRepository))]
[TestCase(typeof(IMacroRepository))]
[TestCase(typeof(IRelationRepository))]
[TestCase(typeof(IRelationTypeRepository))]
[TestCase(typeof(IScriptRepository))]
[TestCase(typeof(IStylesheetRepository))]
[TestCase(typeof(ITemplateRepository))]
public void ResolveRepository(Type repoType)
{
var method = typeof (RepositoryInstanceResolver).GetMethod("ResolveByType", BindingFlags.NonPublic | BindingFlags.Instance);
var gMethod = method.MakeGenericMethod(repoType);
var repo = gMethod.Invoke(RepositoryInstanceResolver.Current, new object[] {new PetaPocoUnitOfWork()});
Assert.IsNotNull(repo);
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(repoType, repo.GetType()));
}
}
}

View File

@@ -7,9 +7,23 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Tests.Persistence
{
[TestFixture]
[TestFixture]
public class RepositoryResolverTests
{
[SetUp]
public void Setup()
{
RepositoryInstanceResolver.Current = new RepositoryInstanceResolver(
new RepositoryInstanceFactory());
}
[TearDown]
public void Teardown()
{
RepositoryInstanceResolver.Reset();
}
[Test]
public void Can_Resolve_All_Repositories()
{

View File

@@ -35,7 +35,10 @@ namespace Umbraco.Tests.TestHelpers
AppDomain.CurrentDomain.SetData("DataDirectory", path);
UmbracoSettings.UseLegacyXmlSchema = false;
RepositoryInstanceResolver.Current = new RepositoryInstanceResolver(
new RepositoryInstanceFactory());
//Delete database file before continueing
string filePath = string.Concat(path, "\\UmbracoPetaPocoTests.sdf");
if (File.Exists(filePath))
@@ -74,6 +77,8 @@ namespace Umbraco.Tests.TestHelpers
ServiceContext = null;
Resolution.IsFrozen = false;
RepositoryInstanceResolver.Reset();
string path = TestHelper.CurrentAssemblyDirectory;
AppDomain.CurrentDomain.SetData("DataDirectory", null);

View File

@@ -167,6 +167,7 @@
<Compile Include="Migrations\Stubs\FourNineMigration.cs" />
<Compile Include="Persistence\Querying\ContentRepositorySqlClausesTest.cs" />
<Compile Include="Persistence\Querying\ContentTypeRepositorySqlClausesTest.cs" />
<Compile Include="Persistence\RepositoryInstanceResolverTests.cs" />
<Compile Include="PublishedContent\DynamicXmlTests.cs" />
<Compile Include="PublishedContent\PublishedContentDataTableTests.cs" />
<Compile Include="PublishedContent\PublishedContentTests.cs" />