diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index e04e20ff0a..c9967c0688 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -61,6 +61,7 @@ namespace Umbraco.Core.Models [FriendlyName("Member Group")] MemberGroup, + //TODO: What is a 'Content Item' supposed to be??? /// /// Content Item /// diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index ec3f48a0ca..e42397e11c 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -1,13 +1,29 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; +using System.Reflection; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Repositories; namespace Umbraco.Core.Persistence.Factories { internal class UmbracoEntityFactory : IEntityFactory { + internal void AddAdditionalData(UmbracoEntity entity, IDictionary originalEntityProperties) + { + var entityProps = TypeHelper.GetPublicProperties(typeof(IUmbracoEntity)).Select(x => x.Name).ToArray(); + + //figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data + foreach (var k in originalEntityProperties.Keys + .Select(x => new { orig = x, title = x.ConvertCase(StringAliasCaseType.PascalCase) }) + .Where(x => entityProps.InvariantContains(x.title) == false)) + { + entity.AdditionalData[k.title] = originalEntityProperties[k.orig]; + } + } + internal UmbracoEntity BuildEntityFromDynamic(dynamic d) { var entity = new UmbracoEntity(d.trashed) @@ -38,12 +54,18 @@ namespace Umbraco.Core.Persistence.Factories Guid.TryParse(d.publishedVersion.ToString(), out publishedVersion); } var newestVersion = default(Guid); - Guid.TryParse(d.newestVersion.ToString(), out newestVersion); + if (d.newestVersion != null) + { + 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; - + + //Now we can assign the additional data! + AddAdditionalData(entity, asDictionary); + return entity; } diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index 0fd0fc4e76..7336e36ab7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -52,12 +52,12 @@ namespace Umbraco.Core.Persistence.Repositories public IUmbracoEntity GetByKey(Guid key) { var sql = GetBaseWhere(GetBase, false, false, key); - var nodeDto = _work.Database.FirstOrDefault(sql); + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); - var entity = factory.BuildEntity(nodeDto); + var entity = factory.BuildEntityFromDynamic(nodeDto); return entity; } @@ -67,12 +67,12 @@ namespace Umbraco.Core.Persistence.Repositories bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, key).Append(GetGroupBy(isContent, isMedia)); - var nodeDto = _work.Database.FirstOrDefault(sql); + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); - var entity = factory.BuildEntity(nodeDto); + var entity = factory.BuildEntityFromDynamic(nodeDto); return entity; } @@ -80,12 +80,12 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IUmbracoEntity Get(int id) { var sql = GetBaseWhere(GetBase, false, false, id); - var nodeDto = _work.Database.FirstOrDefault(sql); + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); - var entity = factory.BuildEntity(nodeDto); + var entity = factory.BuildEntityFromDynamic(nodeDto); return entity; } @@ -95,12 +95,13 @@ namespace Umbraco.Core.Persistence.Repositories bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, id).Append(GetGroupBy(isContent, isMedia)); - var nodeDto = _work.Database.FirstOrDefault(sql); + + var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; var factory = new UmbracoEntityFactory(); - var entity = factory.BuildEntity(nodeDto); + var entity = factory.BuildEntityFromDynamic(nodeDto); return entity; } @@ -119,16 +120,27 @@ namespace Umbraco.Core.Persistence.Repositories bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); var sql = GetBaseWhere(GetBase, isContent, isMedia, string.Empty, objectTypeId).Append(GetGroupBy(isContent, isMedia)); - var dtos = isMedia - ? _work.Database.Fetch( - new UmbracoEntityRelator().Map, sql) - : _work.Database.Fetch(sql); + var factory = new UmbracoEntityFactory(); - foreach (var dto in dtos) + if (isMedia) { - var entity = factory.BuildEntity(dto); - yield return entity; + //for now treat media differently + //TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields + var entities = _work.Database.Fetch( + new UmbracoEntityRelator().Map, sql); + foreach (var entity in entities) + { + yield return entity; + } + } + else + { + var dtos = _work.Database.Fetch(sql); + foreach (var entity in dtos.Select(dto => factory.BuildEntityFromDynamic(dto))) + { + yield return entity; + } } } } @@ -140,10 +152,10 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate().Append(GetGroupBy(false, false)); - var dtos = _work.Database.Fetch(sql); + var dtos = _work.Database.Fetch(sql); var factory = new UmbracoEntityFactory(); - var list = dtos.Select(factory.BuildEntity).Cast().ToList(); + var list = dtos.Select(factory.BuildEntityFromDynamic).Cast().ToList(); return list; } @@ -162,42 +174,22 @@ namespace Umbraco.Core.Persistence.Repositories if (isMedia) { - //treat media differently for now - var dtos = _work.Database.Fetch( + //treat media differently for now + //TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields + var entities = _work.Database.Fetch( new UmbracoEntityRelator().Map, sql); - return dtos.Select(factory.BuildEntity).Cast().ToArray(); + return entities; } 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. + //use dynamic so that we can get ALL properties from the SQL so we can chuck that data into our AdditionalData 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; + return dtos.Select(factory.BuildEntityFromDynamic).Cast().ToList(); } } #endregion + #region Sql Statements @@ -396,11 +388,20 @@ namespace Umbraco.Core.Persistence.Repositories public string UmbracoFile { get; set; } } + /// + /// This is a special relator in that it is not returning a DTO but a real resolved entity and that it accepts + /// a dynamic instance. + /// + /// + /// We're doing this because when we query the db, we want to use dynamic so that it returns all available fields not just the ones + /// defined on the entity so we can them to additional data + /// internal class UmbracoEntityRelator { - internal UmbracoEntityDto Current; + internal UmbracoEntity Current; + private readonly UmbracoEntityFactory _factory = new UmbracoEntityFactory(); - internal UmbracoEntityDto Map(UmbracoEntityDto a, UmbracoPropertyDto p) + internal UmbracoEntity Map(dynamic a, UmbracoPropertyDto p) { // Terminating call. Since we can return null from this function // we need to be ready for PetaPoco to callback later with null @@ -408,28 +409,44 @@ namespace Umbraco.Core.Persistence.Repositories if (a == null) return Current; - // Is this the same UmbracoEntityDto as the current one we're processing - if (Current != null && Current.UniqueId == a.UniqueId) + // Is this the same UmbracoEntity as the current one we're processing + if (Current != null && Current.Key == a.uniqueID) { - // Yes, just add this UmbracoPropertyDto to the current UmbracoEntityDto's collection - Current.UmbracoPropertyDtos.Add(p); - - // Return null to indicate we're not done with this UmbracoEntityDto yet + // Yes, just add this UmbracoProperty to the current UmbracoEntity's collection + if (Current.UmbracoProperties == null) + { + Current.UmbracoProperties = new List(); + } + Current.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty + { + PropertyEditorAlias = p.PropertyEditorAlias, + Value = p.UmbracoFile + }); + // Return null to indicate we're not done with this UmbracoEntity yet return null; } - // This is a different UmbracoEntityDto to the current one, or this is the + // This is a different UmbracoEntity to the current one, or this is the // first time through and we don't have a Tab yet // Save the current UmbracoEntityDto var prev = Current; - // Setup the new current UmbracoEntityDto - Current = a; - Current.UmbracoPropertyDtos = new List(); - Current.UmbracoPropertyDtos.Add(p); + // Setup the new current UmbracoEntity + + Current = _factory.BuildEntityFromDynamic(a); - // Return the now populated previous UmbracoEntityDto (or null if first time through) + //add the property/create the prop list if null + Current.UmbracoProperties = new List + { + new UmbracoEntity.UmbracoProperty + { + PropertyEditorAlias = p.PropertyEditorAlias, + Value = p.UmbracoFile + } + }; + + // Return the now populated previous UmbracoEntity (or null if first time through) return prev; } }