From a8b037f2e0de10b3666b9ec5050dc4c63a47da3d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 12:09:28 +1100 Subject: [PATCH 1/4] Fixes member type repository when member types don't have properties declared and adds tests. Adds IMemberTypeService. Conflicts: src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs src/Umbraco.Core/Services/ServiceContext.cs --- .../PropertyTypePropertyGroupRelator.cs | 6 ++- .../Repositories/MemberTypeRepository.cs | 4 +- .../Services/IMemberTypeService.cs | 15 ++++++ .../Services/MemberTypeService.cs | 39 ++++++++++++++ src/Umbraco.Core/Services/ServiceContext.cs | 12 +++++ .../Repositories/MemberTypeRepositoryTest.cs | 52 ++++++++++++++++++- 6 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Core/Services/IMemberTypeService.cs create mode 100644 src/Umbraco.Core/Services/MemberTypeService.cs diff --git a/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs b/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs index a9129498c5..f02ca8e1b1 100644 --- a/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs +++ b/src/Umbraco.Core/Persistence/Relators/PropertyTypePropertyGroupRelator.cs @@ -38,7 +38,11 @@ namespace Umbraco.Core.Persistence.Relators // Setup the new current MemberTypeReadOnlyDto Current = a; Current.PropertyTypes = new List(); - Current.PropertyTypes.Add(p); + //this can be null since we are doing a left join + if (p.Id.HasValue) + { + Current.PropertyTypes.Add(p); + } Current.PropertyTypeGroups = new List(); if (g.Id.HasValue) diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 9ee215d4f7..73ba016264 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -108,7 +108,7 @@ namespace Umbraco.Core.Persistence.Repositories .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) .LeftJoin().On(left => left.ContentTypeId, right => right.NodeId) - .InnerJoin().On(left => left.PropertyTypeId, right => right.Id) + .LeftJoin().On(left => left.PropertyTypeId, right => right.Id) .LeftJoin().On(left => left.DataTypeId, right => right.DataTypeId) .LeftJoin().On(left => left.ContentTypeNodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectTypeId); @@ -122,7 +122,7 @@ namespace Umbraco.Core.Persistence.Repositories .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) .LeftJoin().On(left => left.ContentTypeId, right => right.NodeId) - .InnerJoin().On(left => left.PropertyTypeId, right => right.Id) + .LeftJoin().On(left => left.PropertyTypeId, right => right.Id) .LeftJoin().On(left => left.DataTypeId, right => right.DataTypeId) .LeftJoin().On(left => left.ContentTypeNodeId, right => right.NodeId) .Where(x => x.NodeObjectType == NodeObjectTypeId); diff --git a/src/Umbraco.Core/Services/IMemberTypeService.cs b/src/Umbraco.Core/Services/IMemberTypeService.cs new file mode 100644 index 0000000000..c2cbb8ab40 --- /dev/null +++ b/src/Umbraco.Core/Services/IMemberTypeService.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + internal interface IMemberTypeService : IService + { + /// + /// Gets a list of all available objects + /// + /// Optional list of ids + /// An Enumerable list of objects + IEnumerable GetAllMemberTypes(params int[] ids); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberTypeService.cs b/src/Umbraco.Core/Services/MemberTypeService.cs new file mode 100644 index 0000000000..99b90b83d4 --- /dev/null +++ b/src/Umbraco.Core/Services/MemberTypeService.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Services +{ + internal class MemberTypeService : IMemberTypeService + { + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly RepositoryFactory _repositoryFactory; + + public MemberTypeService() + : this(new PetaPocoUnitOfWorkProvider(), new RepositoryFactory()) + {} + + public MemberTypeService(RepositoryFactory repositoryFactory) + : this(new PetaPocoUnitOfWorkProvider(), repositoryFactory) + { } + + public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory) + { + if (provider == null) throw new ArgumentNullException("provider"); + if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); + _uowProvider = provider; + _repositoryFactory = repositoryFactory; + } + + public IEnumerable GetAllMemberTypes(params int[] ids) + { + using (var repository = _repositoryFactory.CreateMemberTypeRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetAll(ids); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index cc850d94ba..d2597f0a7a 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -24,6 +24,7 @@ namespace Umbraco.Core.Services private Lazy _serverRegistrationService; private Lazy _entityService; private Lazy _relationService; + private Lazy _memberTypeService; /// /// Constructor @@ -86,6 +87,9 @@ namespace Umbraco.Core.Services if(_relationService == null) _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); + + if (_memberTypeService == null) + _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); } /// @@ -183,5 +187,13 @@ namespace Umbraco.Core.Services { get { return _memberService.Value; } } + } + + /// + /// Gets the MemberTypeService + /// + internal MemberTypeService MemberTypeService + { + get { return _memberTypeService.Value; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index e7a7285f7d..f51e50b57a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void MemberRepository_Can_Persist_Member() + public void MemberRepository_Can_Persist_Member_Type() { IMemberType sut; var provider = new PetaPocoUnitOfWorkProvider(); @@ -67,6 +67,56 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void MemberRepository_Can_Get_All_Member_Types() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var memberType1 = MockedContentTypes.CreateSimpleMemberType(); + repository.AddOrUpdate(memberType1); + unitOfWork.Commit(); + + var memberType2 = MockedContentTypes.CreateSimpleMemberType(); + memberType2.Name = "AnotherType"; + memberType2.Alias = "anotherType"; + repository.AddOrUpdate(memberType2); + unitOfWork.Commit(); + + var result = repository.GetAll(); + + //there are 3 because of the Member type created for init data + Assert.AreEqual(3, result.Count()); + } + } + + [Test] + public void MemberRepository_Can_Get_All_Member_When_No_Properties() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var memberType1 = MockedContentTypes.CreateSimpleMemberType(); + memberType1.PropertyTypeCollection.Clear(); + repository.AddOrUpdate(memberType1); + unitOfWork.Commit(); + + var memberType2 = MockedContentTypes.CreateSimpleMemberType(); + memberType2.PropertyTypeCollection.Clear(); + memberType2.Name = "AnotherType"; + memberType2.Alias = "anotherType"; + repository.AddOrUpdate(memberType2); + unitOfWork.Commit(); + + var result = repository.GetAll(); + + //there are 3 because of the Member type created for init data + Assert.AreEqual(3, result.Count()); + } + } + [Test, NUnit.Framework.Ignore] public void MemberTypeRepository_Can_Get_MemberType_By_Id() { From 49f10ccd3201f16ef5f39a6829e49886fb10e487 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 12:18:42 +1100 Subject: [PATCH 2/4] Fixes merge issue , fixes the ServiceContext lazy fields to be the underlying interfaces, adds public ctor --- src/Umbraco.Core/Services/ServiceContext.cs | 136 ++++++++++++-------- src/Umbraco.Core/Umbraco.Core.csproj | 2 + 2 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index d2597f0a7a..4e18f16063 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -12,42 +12,71 @@ namespace Umbraco.Core.Services /// public class ServiceContext { - private Lazy _contentService; - private Lazy _userService; - private Lazy _memberService; - private Lazy _mediaService; - private Lazy _contentTypeService; - private Lazy _dataTypeService; - private Lazy _fileService; - private Lazy _localizationService; + private Lazy _contentService; + private Lazy _userService; + private Lazy _memberService; + private Lazy _mediaService; + private Lazy _contentTypeService; + private Lazy _dataTypeService; + private Lazy _fileService; + private Lazy _localizationService; private Lazy _packagingService; private Lazy _serverRegistrationService; private Lazy _entityService; private Lazy _relationService; - private Lazy _memberTypeService; + private Lazy _memberTypeService; - /// - /// Constructor - /// - /// - /// - /// - internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy) - { - BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, - //this needs to be lazy because when we create the service context it's generally before the - //resolvers have been initialized! - new Lazy(() => RepositoryResolver.Current.Factory)); - } + /// + /// public ctor - will generally just be used for unit testing + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public ServiceContext(Lazy contentService, Lazy mediaService, Lazy contentTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy memberTypeService) + { + _contentService = contentService; + _mediaService = mediaService; + _contentTypeService = contentTypeService; + _dataTypeService = dataTypeService; + _fileService = fileService; + _localizationService = localizationService; + _packagingService = packagingService; + _serverRegistrationService = serverRegistrationService; + _entityService = entityService; + _relationService = relationService; + _memberTypeService = memberTypeService; + } + + /// + /// Constructor used to instantiate the core services + /// + /// + /// + /// + internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy) + { + BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, + //this needs to be lazy because when we create the service context it's generally before the + //resolvers have been initialized! + new Lazy(() => RepositoryResolver.Current.Factory)); + } /// /// Builds the various services /// - private void BuildServiceCache( - IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, - IUnitOfWorkProvider fileUnitOfWorkProvider, - BasePublishingStrategy publishingStrategy, - Lazy repositoryFactory) + private void BuildServiceCache( + IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, + IUnitOfWorkProvider fileUnitOfWorkProvider, + BasePublishingStrategy publishingStrategy, + Lazy repositoryFactory) { var provider = dbUnitOfWorkProvider; var fileProvider = fileUnitOfWorkProvider; @@ -55,41 +84,41 @@ namespace Umbraco.Core.Services if (_serverRegistrationService == null) _serverRegistrationService = new Lazy(() => new ServerRegistrationService(provider, repositoryFactory.Value)); - if (_userService == null) - _userService = new Lazy(() => new UserService(provider, repositoryFactory.Value)); + if (_userService == null) + _userService = new Lazy(() => new UserService(provider, repositoryFactory.Value)); if (_memberService == null) - _memberService = new Lazy(() => new MemberService(provider, repositoryFactory.Value)); + _memberService = new Lazy(() => new MemberService(provider, repositoryFactory.Value)); if (_contentService == null) - _contentService = new Lazy(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy)); + _contentService = new Lazy(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy)); - if(_mediaService == null) - _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory.Value)); + if (_mediaService == null) + _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory.Value)); - if(_contentTypeService == null) - _contentTypeService = new Lazy(() => new ContentTypeService(provider, repositoryFactory.Value, _contentService.Value, _mediaService.Value)); + if (_contentTypeService == null) + _contentTypeService = new Lazy(() => new ContentTypeService(provider, repositoryFactory.Value, _contentService.Value, _mediaService.Value)); - if(_dataTypeService == null) - _dataTypeService = new Lazy(() => new DataTypeService(provider, repositoryFactory.Value)); + if (_dataTypeService == null) + _dataTypeService = new Lazy(() => new DataTypeService(provider, repositoryFactory.Value)); - if(_fileService == null) - _fileService = new Lazy(() => new FileService(fileProvider, provider, repositoryFactory.Value)); + if (_fileService == null) + _fileService = new Lazy(() => new FileService(fileProvider, provider, repositoryFactory.Value)); - if(_localizationService == null) - _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory.Value)); + if (_localizationService == null) + _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory.Value)); - if(_packagingService == null) + if (_packagingService == null) _packagingService = new Lazy(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, repositoryFactory.Value, provider)); if (_entityService == null) _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); - if(_relationService == null) + if (_relationService == null) _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); if (_memberTypeService == null) - _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); + _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); } /// @@ -129,7 +158,7 @@ namespace Umbraco.Core.Services /// public IContentTypeService ContentTypeService { - get { return _contentTypeService.Value; } + get { return _contentTypeService.Value; } } /// @@ -137,7 +166,7 @@ namespace Umbraco.Core.Services /// public IDataTypeService DataTypeService { - get { return _dataTypeService.Value; } + get { return _dataTypeService.Value; } } /// @@ -145,7 +174,7 @@ namespace Umbraco.Core.Services /// public IFileService FileService { - get { return _fileService.Value; } + get { return _fileService.Value; } } /// @@ -153,7 +182,7 @@ namespace Umbraco.Core.Services /// public ILocalizationService LocalizationService { - get { return _localizationService.Value; } + get { return _localizationService.Value; } } /// @@ -161,7 +190,7 @@ namespace Umbraco.Core.Services /// public IMediaService MediaService { - get { return _mediaService.Value; } + get { return _mediaService.Value; } } /// @@ -177,7 +206,7 @@ namespace Umbraco.Core.Services /// internal IUserService UserService { - get { return _userService.Value; } + get { return _userService.Value; } } /// @@ -187,13 +216,14 @@ namespace Umbraco.Core.Services { get { return _memberService.Value; } } - } - + /// /// Gets the MemberTypeService /// - internal MemberTypeService MemberTypeService + internal IMemberTypeService MemberTypeService { get { return _memberTypeService.Value; } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1386f52e2a..53672bb4aa 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -719,11 +719,13 @@ + + From c6389852ea8dddccc19772ae4fc69cb5a9db723d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 13:17:19 +1100 Subject: [PATCH 3/4] Adds public ctors for ApplicationContext, ServiceContext, DatabaseContext, RepositoryFactory, makes IDatabaseFactory public, makes an interfaces for IEntityService, adds EnsureContext methods for ApplicationContext - to set the singleton. --- src/Umbraco.Core/ApplicationContext.cs | 57 ++++++- src/Umbraco.Core/DatabaseContext.cs | 2 +- .../Persistence/IDatabaseFactory.cs | 2 +- .../Persistence/RepositoryFactory.cs | 2 +- src/Umbraco.Core/Services/EntityService.cs | 2 +- src/Umbraco.Core/Services/IEntityService.cs | 146 ++++++++++++++++++ src/Umbraco.Core/Services/RelationService.cs | 4 +- src/Umbraco.Core/Services/ServiceContext.cs | 30 ++-- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/MockTests.cs | 124 +++++++++++++++ .../{ => Persistence}/Auditing/AuditTests.cs | 68 ++++---- .../Routing/RenderRouteHandlerTests.cs | 2 +- .../Routing/uQueryGetNodeIdByUrlTests.cs | 2 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 1 - .../TestHelpers/BaseRoutingTest.cs | 2 +- src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 1 - .../Stubs/FakeLastChanceFinder.cs | 22 +-- .../Stubs/TestControllerFactory.cs | 78 +++++----- src/Umbraco.Tests/Umbraco.Tests.csproj | 7 +- 19 files changed, 432 insertions(+), 121 deletions(-) create mode 100644 src/Umbraco.Core/Services/IEntityService.cs create mode 100644 src/Umbraco.Tests/MockTests.cs rename src/Umbraco.Tests/{ => Persistence}/Auditing/AuditTests.cs (92%) rename src/Umbraco.Tests/{ => TestHelpers}/Stubs/FakeLastChanceFinder.cs (81%) rename src/Umbraco.Tests/{ => TestHelpers}/Stubs/TestControllerFactory.cs (94%) diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 78ed2c755a..ed3a0d127d 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -35,12 +35,9 @@ namespace Umbraco.Core /// /// /// - internal ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache) + public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache) : this(enableCache) { - if (dbContext == null) throw new ArgumentNullException("dbContext"); - if (serviceContext == null) throw new ArgumentNullException("serviceContext"); - _databaseContext = dbContext; _services = serviceContext; } @@ -57,7 +54,7 @@ namespace Umbraco.Core /// Constructor used to specify if we will enable application cache or not /// /// - internal ApplicationContext(bool enableCache) + public ApplicationContext(bool enableCache) { //create a new application cache from the HttpRuntime.Cache ApplicationCache = HttpRuntime.Cache == null @@ -65,7 +62,55 @@ namespace Umbraco.Core : new CacheHelper(HttpRuntime.Cache, enableCache); } - /// + /// + /// A method used to set and/or ensure that a global ApplicationContext singleton is created. + /// + /// + /// The instance to set on the global application singleton + /// + /// If set to true and the singleton is already set, it will be replaced + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext) + { + if (ApplicationContext.Current != null) + { + if (!replaceContext) + return ApplicationContext.Current; + } + ApplicationContext.Current = appContext; + return ApplicationContext.Current; + } + + /// + /// A method used to create and ensure that a global ApplicationContext singleton is created. + /// + /// + /// + /// If set to true will replace the current singleton instance - This should only be used for unit tests or on app + /// startup if for some reason the boot manager is not the umbraco boot manager. + /// + /// + /// + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache, bool replaceContext) + { + if (ApplicationContext.Current != null) + { + if (!replaceContext) + return ApplicationContext.Current; + } + var ctx = new ApplicationContext(dbContext, serviceContext, enableCache); + ApplicationContext.Current = ctx; + return ApplicationContext.Current; + } + + /// /// Singleton accessor /// public static ApplicationContext Current { get; internal set; } diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index dbbc476ae9..4d5170ec80 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core private string _providerName; private DatabaseSchemaResult _result; - internal DatabaseContext(IDatabaseFactory factory) + public DatabaseContext(IDatabaseFactory factory) { _factory = factory; } diff --git a/src/Umbraco.Core/Persistence/IDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IDatabaseFactory.cs index 87c146fa0f..b0efb7f94a 100644 --- a/src/Umbraco.Core/Persistence/IDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/IDatabaseFactory.cs @@ -5,7 +5,7 @@ namespace Umbraco.Core.Persistence /// /// Used to create the UmbracoDatabase for use in the DatabaseContext /// - internal interface IDatabaseFactory : IDisposable + public interface IDatabaseFactory : IDisposable { UmbracoDatabase CreateDatabase(); } diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index 710dc27d87..ab0a5e8e65 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence } - internal RepositoryFactory(bool disableAllCache) + public RepositoryFactory(bool disableAllCache) { _disableAllCache = disableAllCache; } diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index 882aa86b4e..7e103c687b 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class EntityService : IService + public class EntityService : IService, IEntityService { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly RepositoryFactory _repositoryFactory; diff --git a/src/Umbraco.Core/Services/IEntityService.cs b/src/Umbraco.Core/Services/IEntityService.cs new file mode 100644 index 0000000000..c32d755c3f --- /dev/null +++ b/src/Umbraco.Core/Services/IEntityService.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Services +{ + public interface IEntityService + { + /// + /// Gets an UmbracoEntity by its Id, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id and UmbracoObjectType, and optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Id of the object to retrieve + /// UmbracoObjectType of the entity to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, UmbracoObjectTypes umbracoObjectType, bool loadBaseType = true); + + /// + /// Gets an UmbracoEntity by its Id and specified Type. Optionally loads the complete object graph. + /// + /// + /// By default this will load the base type with a minimum set of properties. + /// + /// Type of the model to retrieve. Must be based on an + /// Id of the object to retrieve + /// Optional bool to load the complete object graph when set to False. + /// An + IUmbracoEntity Get(int id, bool loadBaseType = true) where T : IUmbracoEntity; + + /// + /// Gets the parent of entity by its id + /// + /// Id of the entity to retrieve the Parent for + /// An + IUmbracoEntity GetParent(int id); + + /// + /// Gets the parent of entity by its id and UmbracoObjectType + /// + /// Id of the entity to retrieve the Parent for + /// UmbracoObjectType of the parent to retrieve + /// An + IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of children by the parents Id + /// + /// Id of the parent to retrieve children for + /// An enumerable list of objects + IEnumerable GetChildren(int parentId); + + /// + /// Gets a collection of children by the parents Id and UmbracoObjectType + /// + /// Id of the parent to retrieve children for + /// UmbracoObjectType of the children to retrieve + /// An enumerable list of objects + IEnumerable GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of descendents by the parents Id + /// + /// Id of entity to retrieve descendents for + /// An enumerable list of objects + IEnumerable GetDescendents(int id); + + /// + /// Gets a collection of descendents by the parents Id + /// + /// Id of entity to retrieve descendents for + /// UmbracoObjectType of the descendents to retrieve + /// An enumerable list of objects + IEnumerable GetDescendents(int id, UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of the entities at the root, which corresponds to the entities with a Parent Id of -1. + /// + /// UmbracoObjectType of the root entities to retrieve + /// An enumerable list of objects + IEnumerable GetRootEntities(UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of all of a given type. + /// + /// Type of the entities to retrieve + /// An enumerable list of objects + IEnumerable GetAll() where T : IUmbracoEntity; + + /// + /// Gets a collection of all of a given type. + /// + /// UmbracoObjectType of the entities to return + /// An enumerable list of objects + IEnumerable GetAll(UmbracoObjectTypes umbracoObjectType); + + /// + /// Gets a collection of + /// + /// Guid id of the UmbracoObjectType + /// An enumerable list of objects + IEnumerable GetAll(Guid objectTypeId); + + /// + /// Gets the UmbracoObjectType from the integer id of an IUmbracoEntity. + /// + /// Id of the entity + /// + UmbracoObjectTypes GetObjectType(int id); + + /// + /// Gets the UmbracoObjectType from an IUmbracoEntity. + /// + /// + /// + UmbracoObjectTypes GetObjectType(IUmbracoEntity entity); + + /// + /// Gets the Type of an entity by its Id + /// + /// Id of the entity + /// Type of the entity + Type GetEntityType(int id); + + /// + /// Gets the Type of an entity by its + /// + /// + /// Type of the entity + Type GetEntityType(UmbracoObjectTypes umbracoObjectType); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index 83d09023ab..29a42da115 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -13,10 +13,10 @@ namespace Umbraco.Core.Services { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly RepositoryFactory _repositoryFactory; - private readonly EntityService _entityService; + private readonly IEntityService _entityService; public RelationService(IDatabaseUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory, - EntityService entityService) + IEntityService entityService) { _uowProvider = uowProvider; _repositoryFactory = repositoryFactory; diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index 4e18f16063..b6ccfc3d81 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Services private Lazy _localizationService; private Lazy _packagingService; private Lazy _serverRegistrationService; - private Lazy _entityService; + private Lazy _entityService; private Lazy _relationService; private Lazy _memberTypeService; @@ -36,23 +36,19 @@ namespace Umbraco.Core.Services /// /// /// - /// /// /// - /// - public ServiceContext(Lazy contentService, Lazy mediaService, Lazy contentTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy memberTypeService) + public ServiceContext(IContentService contentService, IMediaService mediaService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, PackagingService packagingService, IEntityService entityService, RelationService relationService) { - _contentService = contentService; - _mediaService = mediaService; - _contentTypeService = contentTypeService; - _dataTypeService = dataTypeService; - _fileService = fileService; - _localizationService = localizationService; - _packagingService = packagingService; - _serverRegistrationService = serverRegistrationService; - _entityService = entityService; - _relationService = relationService; - _memberTypeService = memberTypeService; + _contentService = new Lazy(() => contentService); + _mediaService = new Lazy(() => mediaService); + _contentTypeService = new Lazy(() => contentTypeService); + _dataTypeService = new Lazy(() => dataTypeService); + _fileService = new Lazy(() => fileService); + _localizationService = new Lazy(() => localizationService); + _packagingService = new Lazy(() => packagingService); + _entityService = new Lazy(() => entityService); + _relationService = new Lazy(() => relationService); } /// @@ -112,7 +108,7 @@ namespace Umbraco.Core.Services _packagingService = new Lazy(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, repositoryFactory.Value, provider)); if (_entityService == null) - _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); + _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); if (_relationService == null) _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); @@ -132,7 +128,7 @@ namespace Umbraco.Core.Services /// /// Gets the /// - public EntityService EntityService + public IEntityService EntityService { get { return _entityService.Value; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 53672bb4aa..5db9dd7d56 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -714,6 +714,7 @@ + diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs new file mode 100644 index 0000000000..be31d2dbf4 --- /dev/null +++ b/src/Umbraco.Tests/MockTests.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Services; +using Moq; +using Umbraco.Web; + +namespace Umbraco.Tests +{ + [TestFixture] + public class MockTests + { + + [Test] + public void Can_Create_Empty_App_Context() + { + var appCtx = new ApplicationContext(false); + Assert.Pass(); + } + + [Test] + public void Can_Create_Service_Context() + { + var svcCtx = new ServiceContext( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new PackagingService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new RelationService( + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object)); + Assert.Pass(); + } + + [Test] + public void Can_Create_Db_Context() + { + var dbCtx = new DatabaseContext(new Mock().Object); + Assert.Pass(); + } + + [Test] + public void Can_Create_App_Context_With_Services() + { + var appCtx = new ApplicationContext( + new DatabaseContext(new Mock().Object), + new ServiceContext( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new PackagingService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object), + new Mock().Object, + new RelationService( + new Mock().Object, + new RepositoryFactory(true), + new Mock().Object)), + false); + Assert.Pass(); + } + + [Test] + public void Can_Assign_App_Context_Singleton() + { + var appCtx = new ApplicationContext(false); + var result = ApplicationContext.EnsureContext(appCtx, true); + Assert.AreEqual(appCtx, result); + } + + [Test] + public void Does_Not_Overwrite_App_Context_Singleton() + { + ApplicationContext.EnsureContext(new ApplicationContext(false), true); + var appCtx = new ApplicationContext(false); + var result = ApplicationContext.EnsureContext(appCtx, false); + Assert.AreNotEqual(appCtx, result); + } + + [NUnit.Framework.Ignore("Need to fix more stuff up, this is ignore because an exception occurs because it wants to ensure we have a resolver initialized - need to make that process better for testability")] + [Test] + public void Can_Get_Umbraco_Context() + { + var appCtx = new ApplicationContext(false); + ApplicationContext.EnsureContext(appCtx, true); + + var umbCtx = UmbracoContext.EnsureContext( + new Mock().Object, + appCtx, + true); + + Assert.AreEqual(umbCtx, UmbracoContext.Current); + } + + } +} diff --git a/src/Umbraco.Tests/Auditing/AuditTests.cs b/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs similarity index 92% rename from src/Umbraco.Tests/Auditing/AuditTests.cs rename to src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs index ff5e6bb164..68e7bcb003 100644 --- a/src/Umbraco.Tests/Auditing/AuditTests.cs +++ b/src/Umbraco.Tests/Persistence/Auditing/AuditTests.cs @@ -1,35 +1,35 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Core.Auditing; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Auditing -{ - [TestFixture] - public class AuditTests : BaseDatabaseFactoryTest - { - [SetUp] - public override void Initialize() - { - base.Initialize(); - } - - [Test] - public void Can_Add_Audit_Entry() - { - Audit.Add(AuditTypes.System, "This is a System audit trail", 0, -1); - - var dtos = DatabaseContext.Database.Fetch("WHERE id > -1"); - - Assert.That(dtos.Any(), Is.True); - Assert.That(dtos.First().Comment, Is.EqualTo("This is a System audit trail")); - } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } - } +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Auditing; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence.Auditing +{ + [TestFixture] + public class AuditTests : BaseDatabaseFactoryTest + { + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [Test] + public void Can_Add_Audit_Entry() + { + Audit.Add(AuditTypes.System, "This is a System audit trail", 0, -1); + + var dtos = DatabaseContext.Database.Fetch("WHERE id > -1"); + + Assert.That(dtos.Any(), Is.True); + Assert.That(dtos.First().Comment, Is.EqualTo("This is a System audit trail")); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 2b00ea75cf..4b13acf7ee 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -3,8 +3,8 @@ using System.Web.Routing; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Tests.Stubs; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; diff --git a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs index 24bc912f91..53d5301246 100644 --- a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; -using Umbraco.Tests.Stubs; using Umbraco.Tests.TestHelpers; using System.Configuration; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 00896040a9..57355f3e7a 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -19,7 +19,6 @@ using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; using Umbraco.Core.Services; -using Umbraco.Tests.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; diff --git a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs index ab787bf105..d55a437976 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs @@ -4,7 +4,7 @@ using System.Web.Routing; using NUnit.Framework; using Umbraco.Core.Configuration; using Umbraco.Core.Models; -using Umbraco.Tests.Stubs; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 95e89468c3..a635a42269 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -12,7 +12,6 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; using Umbraco.Core.Services; -using Umbraco.Tests.Stubs; using Umbraco.Web; using Umbraco.Web.Routing; using umbraco.BusinessLogic; diff --git a/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs b/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs similarity index 81% rename from src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs rename to src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs index 9090909baa..c9f5dbe024 100644 --- a/src/Umbraco.Tests/Stubs/FakeLastChanceFinder.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/FakeLastChanceFinder.cs @@ -1,12 +1,12 @@ -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Stubs -{ - internal class FakeLastChanceFinder : IContentFinder - { - public bool TryFindContent(PublishedContentRequest docRequest) - { - return false; - } - } +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + internal class FakeLastChanceFinder : IContentFinder + { + public bool TryFindContent(PublishedContentRequest docRequest) + { + return false; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs similarity index 94% rename from src/Umbraco.Tests/Stubs/TestControllerFactory.cs rename to src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index 872093f896..5796ee5bc3 100644 --- a/src/Umbraco.Tests/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -1,40 +1,40 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Web.Mvc; -using System.Web.Routing; -using System.Web.SessionState; -using Umbraco.Core; - -namespace Umbraco.Tests.Stubs -{ - /// - /// Used in place of the UmbracoControllerFactory which relies on BuildManager which throws exceptions in a unit test context - /// - internal class TestControllerFactory : IControllerFactory - { - - public IController CreateController(RequestContext requestContext, string controllerName) - { - var types = TypeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); - - var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); - var t = controllerTypes.SingleOrDefault(); - - if (t == null) - return null; - - return Activator.CreateInstance(t) as IController; - } - - public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) - { - return SessionStateBehavior.Disabled; - } - - public void ReleaseController(IController controller) - { - controller.DisposeIfDisposable(); - } - } +using System; +using System.Linq; +using System.Reflection; +using System.Web.Mvc; +using System.Web.Routing; +using System.Web.SessionState; +using Umbraco.Core; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + /// + /// Used in place of the UmbracoControllerFactory which relies on BuildManager which throws exceptions in a unit test context + /// + internal class TestControllerFactory : IControllerFactory + { + + public IController CreateController(RequestContext requestContext, string controllerName) + { + var types = TypeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); + + var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); + var t = controllerTypes.SingleOrDefault(); + + if (t == null) + return null; + + return Activator.CreateInstance(t) as IController; + } + + public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) + { + return SessionStateBehavior.Disabled; + } + + public void ReleaseController(IController controller) + { + controller.DisposeIfDisposable(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 469caa926c..da950ed3b1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -144,7 +144,8 @@ - + + @@ -355,7 +356,7 @@ - + @@ -391,7 +392,7 @@ - + From 09b71867136e29de33ff5260d4ced7593c442e93 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 9 Oct 2013 13:32:58 +1100 Subject: [PATCH 4/4] Completes: U4-2963 Updated EntityService / IUmbracoEntity to have a dictionary to fill additional data - fixes merge issues and explicitly implements interface for entities that are not UmbracoEntity and hides other implementations from intellisense. --- src/Umbraco.Core/Models/ContentBase.cs | 13 +++++ src/Umbraco.Core/Models/ContentTypeBase.cs | 13 +++++ src/Umbraco.Core/Models/DataTypeDefinition.cs | 23 +++++++- .../Models/EntityBase/IUmbracoEntity.cs | 9 +++- src/Umbraco.Core/Models/UmbracoEntity.cs | 36 ++++++++++++- .../Persistence/Factories/IEntityFactory.cs | 2 + .../Factories/UmbracoEntityFactory.cs | 39 ++++++++++++++ .../Repositories/EntityRepository.cs | 54 +++++++++++++++---- src/Umbraco.Core/TypeHelper.cs | 47 ++++++++++++++++ 9 files changed, 223 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 738a896155..9a5e735e9e 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -50,6 +51,7 @@ namespace Umbraco.Core.Models _contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture)); _properties = properties; _properties.EnsurePropertyTypes(PropertyTypes); + _additionalData = new Dictionary(); } /// @@ -73,6 +75,7 @@ namespace Umbraco.Core.Models _contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture)); _properties = properties; _properties.EnsurePropertyTypes(PropertyTypes); + _additionalData = new Dictionary(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -253,6 +256,16 @@ namespace Umbraco.Core.Models } } + private readonly IDictionary _additionalData; + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + [EditorBrowsable(EditorBrowsableState.Never)] + IDictionary IUmbracoEntity.AdditionalData + { + get { return _additionalData; } + } + /// /// List of PropertyGroups available on this Content object /// diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index e8d2effbc1..94000e41c9 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.Serialization; @@ -42,6 +43,7 @@ namespace Umbraco.Core.Models _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); _propertyTypes = new PropertyTypeCollection(); + _additionalData = new Dictionary(); } protected ContentTypeBase(IContentTypeBase parent) @@ -52,6 +54,7 @@ namespace Umbraco.Core.Models _allowedContentTypes = new List(); _propertyGroups = new PropertyGroupCollection(); _propertyTypes = new PropertyTypeCollection(); + _additionalData = new Dictionary(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -314,6 +317,16 @@ namespace Umbraco.Core.Models } } + private readonly IDictionary _additionalData; + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + [EditorBrowsable(EditorBrowsableState.Never)] + IDictionary IUmbracoEntity.AdditionalData + { + get { return _additionalData; } + } + /// /// Gets or sets a list of integer Ids for allowed ContentTypes /// diff --git a/src/Umbraco.Core/Models/DataTypeDefinition.cs b/src/Umbraco.Core/Models/DataTypeDefinition.cs index f7068a3b6e..387101f5d8 100644 --- a/src/Umbraco.Core/Models/DataTypeDefinition.cs +++ b/src/Umbraco.Core/Models/DataTypeDefinition.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.ComponentModel; using System.Reflection; using System.Runtime.Serialization; using Umbraco.Core.Models.EntityBase; @@ -31,6 +33,8 @@ namespace Umbraco.Core.Models { _parentId = parentId; _controlId = controlId; + _additionalData = new Dictionary(); + _additionalData = new Dictionary(); } private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); @@ -160,9 +164,11 @@ namespace Umbraco.Core.Models _trashed = value; return _trashed; }, _trashed, TrashedSelector); + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + _additionalData["Trashed"] = value; } } - + /// /// Id of the DataType control /// @@ -177,6 +183,8 @@ namespace Umbraco.Core.Models _controlId = value; return _controlId; }, _controlId, ControlIdSelector); + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + _additionalData["ControlId"] = value; } } @@ -194,9 +202,22 @@ namespace Umbraco.Core.Models _databaseType = value; return _databaseType; }, _databaseType, DatabaseTypeSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + _additionalData["DatabaseType"] = value; } } + private readonly IDictionary _additionalData; + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + [EditorBrowsable(EditorBrowsableState.Never)] + IDictionary IUmbracoEntity.AdditionalData + { + get { return _additionalData; } + } + internal override void AddingEntity() { base.AddingEntity(); diff --git a/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs b/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs index 62130fdab9..397f4518a9 100644 --- a/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs +++ b/src/Umbraco.Core/Models/EntityBase/IUmbracoEntity.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Models.EntityBase +using System.Collections.Generic; + +namespace Umbraco.Core.Models.EntityBase { public interface IUmbracoEntity : IAggregateRoot { @@ -41,5 +43,10 @@ /// Not all entities support being trashed, they'll always return false. /// bool Trashed { get; } + + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// + IDictionary AdditionalData { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs index 451c0fe9c1..0a8302dea1 100644 --- a/src/Umbraco.Core/Models/UmbracoEntity.cs +++ b/src/Umbraco.Core/Models/UmbracoEntity.cs @@ -44,10 +44,12 @@ namespace Umbraco.Core.Models public UmbracoEntity() { + AdditionalData = new Dictionary(); } public UmbracoEntity(bool trashed) { + AdditionalData = new Dictionary(); Trashed = trashed; } @@ -142,6 +144,9 @@ namespace Umbraco.Core.Models } } + public IDictionary AdditionalData { get; private set; } + + public bool HasChildren { get { return _hasChildren; } @@ -152,6 +157,9 @@ namespace Umbraco.Core.Models _hasChildren = value; return _hasChildren; }, _hasChildren, HasChildrenSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["HasChildren"] = value; } } @@ -164,7 +172,10 @@ namespace Umbraco.Core.Models { _isPublished = value; return _isPublished; - }, _isPublished, IsPublishedSelector); + }, _isPublished, IsPublishedSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["IsPublished"] = value; } } @@ -178,6 +189,9 @@ namespace Umbraco.Core.Models _isDraft = value; return _isDraft; }, _isDraft, IsDraftSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["IsDraft"] = value; } } @@ -191,6 +205,9 @@ namespace Umbraco.Core.Models _hasPendingChanges = value; return _hasPendingChanges; }, _hasPendingChanges, HasPendingChangesSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["HasPendingChanges"] = value; } } @@ -204,6 +221,9 @@ namespace Umbraco.Core.Models _contentTypeAlias = value; return _contentTypeAlias; }, _contentTypeAlias, ContentTypeAliasSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["ContentTypeAlias"] = value; } } @@ -217,6 +237,9 @@ namespace Umbraco.Core.Models _contentTypeIcon = value; return _contentTypeIcon; }, _contentTypeIcon, ContentTypeIconSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["ContentTypeIcon"] = value; } } @@ -230,6 +253,9 @@ namespace Umbraco.Core.Models _contentTypeThumbnail = value; return _contentTypeThumbnail; }, _contentTypeThumbnail, ContentTypeThumbnailSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["ContentTypeThumbnail"] = value; } } @@ -242,10 +268,16 @@ namespace Umbraco.Core.Models { _nodeObjectTypeId = value; return _nodeObjectTypeId; - }, _nodeObjectTypeId, NodeObjectTypeIdSelector); + }, _nodeObjectTypeId, NodeObjectTypeIdSelector); + + //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data + AdditionalData["NodeObjectTypeId"] = value; } } + /// + /// Some entities may expose additional data that other's might not, this custom data will be available in this collection + /// public IList UmbracoProperties { get; set; } internal class UmbracoProperty diff --git a/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs index 6053090178..a0d8823d68 100644 --- a/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/IEntityFactory.cs @@ -1,5 +1,7 @@ namespace Umbraco.Core.Persistence.Factories { + //TODO: Not sure why we need this interface as it's never referenced in code. + internal interface IEntityFactory where TEntity : class where TDto : class diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index d7b8247364..226294be0f 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -8,6 +8,45 @@ namespace Umbraco.Core.Persistence.Factories { internal class UmbracoEntityFactory : IEntityFactory { + internal UmbracoEntity BuildEntityFromDynamic(dynamic d) + { + var entity = new UmbracoEntity(d.trashed) + { + CreateDate = d.createDate, + CreatorId = d.nodeUser, + Id = d.id, + Key = d.uniqueID, + Level = d.level, + Name = d.text, + NodeObjectTypeId = d.nodeObjectType, + ParentId = d.parentID, + Path = d.path, + SortOrder = d.sortOrder, + HasChildren = d.children > 0, + ContentTypeAlias = d.alias ?? string.Empty, + ContentTypeIcon = d.icon ?? string.Empty, + ContentTypeThumbnail = d.thumbnail ?? string.Empty, + UmbracoProperties = new List() + }; + + var asDictionary = (IDictionary)d; + + var publishedVersion = default(Guid); + //some content items don't have a published version + if (asDictionary.ContainsKey("publishedVersion") && asDictionary["publishedVersion"] != null) + { + Guid.TryParse(d.publishedVersion.ToString(), out publishedVersion); + } + var newestVersion = default(Guid); + Guid.TryParse(d.newestVersion.ToString(), out newestVersion); + + entity.IsPublished = publishedVersion != default(Guid) || (newestVersion != default(Guid) && publishedVersion == newestVersion); + entity.IsDraft = newestVersion != default(Guid) && (publishedVersion == default(Guid) || publishedVersion != newestVersion); + entity.HasPendingChanges = (publishedVersion != default(Guid) && newestVersion != default(Guid)) && publishedVersion != newestVersion; + + return entity; + } + public UmbracoEntity BuildEntity(EntityRepository.UmbracoEntityDto dto) { var entity = new UmbracoEntity(dto.Trashed) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index 8651068030..bbfea4082a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -1,12 +1,18 @@ using System; using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; using System.Linq; +using System.Reflection; +using System.Text; using Umbraco.Core.Models; +using Umbraco.Core; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Strings; namespace Umbraco.Core.Persistence.Repositories { @@ -124,15 +130,43 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate().Append(GetGroupBy(isContent, isMedia)); - var dtos = isMedia - ? _work.Database.Fetch( - new UmbracoEntityRelator().Map, sql) - : _work.Database.Fetch(sql); - var factory = new UmbracoEntityFactory(); - var list = dtos.Select(factory.BuildEntity).Cast().ToList(); - return list; + if (isMedia) + { + //treat media differently for now + var dtos = _work.Database.Fetch( + new UmbracoEntityRelator().Map, sql); + return dtos.Select(factory.BuildEntity).Cast().ToArray(); + } + else + { + //use dynamic so that we can get ALL properties from the SQL + //we'll have to stitch stuff together manually but we can get our + //additional data to put in the dictionary. + var dtos = _work.Database.Fetch(sql); + var entityProps = TypeHelper.GetPublicProperties(typeof (IUmbracoEntity)).Select(x => x.Name).ToArray(); + var result = new List(); + foreach (var d in dtos) + { + //build the initial entity + IUmbracoEntity entity = factory.BuildEntityFromDynamic(d); + + //convert the dynamic row to dictionary + var asDictionary = (IDictionary) d; + + //figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data + foreach (var k in asDictionary.Keys + .Select(x => new {orig = x, title = x.ConvertCase(StringAliasCaseType.PascalCase)}) + .Where(x => entityProps.InvariantContains(x.title) == false)) + { + entity.AdditionalData[k.title] = asDictionary[k.orig]; + } + + result.Add(entity); + } + return result; + } } #endregion @@ -159,11 +193,12 @@ namespace Umbraco.Core.Persistence.Repositories if (isContent || isMedia) { - columns.Add("published.versionId as publishedVerison"); + columns.Add("published.versionId as publishedVersion"); columns.Add("latest.versionId as newestVersion"); columns.Add("contenttype.alias"); columns.Add("contenttype.icon"); columns.Add("contenttype.thumbnail"); + columns.Add("contenttype.isContainer"); } if (isMedia) @@ -251,6 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories columns.Add("contenttype.alias"); columns.Add("contenttype.icon"); columns.Add("contenttype.thumbnail"); + columns.Add("contenttype.isContainer"); } if (isMedia) @@ -287,7 +323,7 @@ namespace Umbraco.Core.Persistence.Repositories [Column("children")] public int Children { get; set; } - [Column("publishedVerison")] + [Column("publishedVersion")] public Guid PublishedVersion { get; set; } [Column("newestVersion")] diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index adda1d3298..efb525e575 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -203,6 +203,53 @@ namespace Umbraco.Core }); } + /// + /// Returns all public properties including inherited properties even for interfaces + /// + /// + /// + /// + /// taken from http://stackoverflow.com/questions/358835/getproperties-to-return-all-properties-for-an-interface-inheritance-hierarchy + /// + public static PropertyInfo[] GetPublicProperties(Type type) + { + if (type.IsInterface) + { + var propertyInfos = new List(); + + var considered = new List(); + var queue = new Queue(); + considered.Add(type); + queue.Enqueue(type); + while (queue.Count > 0) + { + var subType = queue.Dequeue(); + foreach (var subInterface in subType.GetInterfaces()) + { + if (considered.Contains(subInterface)) continue; + + considered.Add(subInterface); + queue.Enqueue(subInterface); + } + + var typeProperties = subType.GetProperties( + BindingFlags.FlattenHierarchy + | BindingFlags.Public + | BindingFlags.Instance); + + var newPropertyInfos = typeProperties + .Where(x => !propertyInfos.Contains(x)); + + propertyInfos.InsertRange(0, newPropertyInfos); + } + + return propertyInfos.ToArray(); + } + + return type.GetProperties(BindingFlags.FlattenHierarchy + | BindingFlags.Public | BindingFlags.Instance); + } + /// /// Gets (and caches) discoverable in the current for a given . ///