From a53dde8a20437302516b4c69f2eab3bdee83c38e Mon Sep 17 00:00:00 2001 From: sitereactor Date: Tue, 23 Oct 2012 11:29:16 -0200 Subject: [PATCH] Adding two missing properties to ContentTypeBase. Creating mappers for the few types that supports "db queries" through the repository U4-978 --- src/Umbraco.Core/ExpressionHelper.cs | 37 ++++++++ src/Umbraco.Core/Models/ContentTypeBase.cs | 35 ++++++++ src/Umbraco.Core/Models/IContentTypeBase.cs | 13 +++ .../Factories/ContentTypeFactory.cs | 4 + .../Persistence/Factories/MediaTypeFactory.cs | 4 + .../Persistence/Mappers/BaseMapper.cs | 38 ++++++++ .../Persistence/Mappers/ContentMapper.cs | 67 ++++++++++++++ .../Persistence/Mappers/ContentTypeMapper.cs | 64 +++++++++++++ .../Mappers/DataTypeDefinitionMapper.cs | 58 ++++++++++++ .../Persistence/Mappers/DictionaryMapper.cs | 52 +++++++++++ .../Persistence/Mappers/DtoMapModel.cs | 19 ++++ .../Persistence/Mappers/LanguageMapper.cs | 48 ++++++++++ .../Persistence/Mappers/MediaMapper.cs | 57 ++++++++++++ .../Persistence/Mappers/ModelDtoMapper.cs | 90 ++++++++----------- .../Persistence/Mappers/RelationMapper.cs | 51 +++++++++++ .../Persistence/Mappers/RelationTypeMapper.cs | 51 +++++++++++ .../Repositories/ContentRepository.cs | 1 + .../Repositories/LanguageRepository.cs | 3 + src/Umbraco.Core/Umbraco.Core.csproj | 10 +++ 19 files changed, 650 insertions(+), 52 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/DataTypeDefinitionMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/DictionaryMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/LanguageMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs create mode 100644 src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs diff --git a/src/Umbraco.Core/ExpressionHelper.cs b/src/Umbraco.Core/ExpressionHelper.cs index 1293bd47fc..6aa4ebeaa8 100644 --- a/src/Umbraco.Core/ExpressionHelper.cs +++ b/src/Umbraco.Core/ExpressionHelper.cs @@ -82,6 +82,43 @@ namespace Umbraco.Core }); } + public static MemberInfo FindProperty(LambdaExpression lambdaExpression) + { + Expression expressionToCheck = lambdaExpression; + + bool done = false; + + while (!done) + { + switch (expressionToCheck.NodeType) + { + case ExpressionType.Convert: + expressionToCheck = ((UnaryExpression)expressionToCheck).Operand; + break; + case ExpressionType.Lambda: + expressionToCheck = ((LambdaExpression)expressionToCheck).Body; + break; + case ExpressionType.MemberAccess: + var memberExpression = ((MemberExpression)expressionToCheck); + + if (memberExpression.Expression.NodeType != ExpressionType.Parameter && + memberExpression.Expression.NodeType != ExpressionType.Convert) + { + throw new ArgumentException(string.Format("Expression '{0}' must resolve to top-level member and not any child object's properties. Use a custom resolver on the child type or the AfterMap option instead.", lambdaExpression), "lambdaExpression"); + } + + MemberInfo member = memberExpression.Member; + + return member; + default: + done = true; + break; + } + } + + throw new Exception("Configuration for members is only supported for top-level individual members on a type."); + } + public static IDictionary GetMethodParams(Expression> fromExpression) { if (fromExpression == null) return null; diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 94cc87d121..a0035c5f6f 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -22,6 +22,8 @@ namespace Umbraco.Core.Models private string _icon; private string _thumbnail; private int _userId; + private bool _allowedAsRoot; + private bool _isContainer; private bool _trashed; private PropertyGroupCollection _propertyGroups; private IEnumerable _allowedContentTypes; @@ -43,6 +45,8 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo IconSelector = ExpressionHelper.GetPropertyInfo(x => x.Icon); private static readonly PropertyInfo ThumbnailSelector = ExpressionHelper.GetPropertyInfo(x => x.Thumbnail); private static readonly PropertyInfo UserIdSelector = ExpressionHelper.GetPropertyInfo(x => x.UserId); + private static readonly PropertyInfo AllowedAsRootSelector = ExpressionHelper.GetPropertyInfo(x => x.AllowedAsRoot); + private static readonly PropertyInfo IsContainerSelector = ExpressionHelper.GetPropertyInfo(x => x.IsContainer); private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); private static readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedContentTypes); private static readonly PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); @@ -193,6 +197,37 @@ namespace Umbraco.Core.Models } } + /// + /// Gets or Sets a boolean indicating whether this ContentType is allowed at the root + /// + [DataMember] + public virtual bool AllowedAsRoot + { + get { return _allowedAsRoot; } + set + { + _allowedAsRoot = value; + OnPropertyChanged(AllowedAsRootSelector); + } + } + + /// + /// Gets or Sets a boolean indicating whether this ContentType is a Container + /// + /// + /// ContentType Containers doesn't show children in the tree, but rather in grid-type view. + /// + [DataMember] + public virtual bool IsContainer + { + get { return _isContainer; } + set + { + _isContainer = value; + OnPropertyChanged(IsContainerSelector); + } + } + /// /// Boolean indicating whether this ContentType is Trashed or not. /// If ContentType is Trashed it will be located in the Recyclebin. diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs index 973b99c592..5a0013e2b9 100644 --- a/src/Umbraco.Core/Models/IContentTypeBase.cs +++ b/src/Umbraco.Core/Models/IContentTypeBase.cs @@ -59,6 +59,19 @@ namespace Umbraco.Core.Models /// int UserId { get; set; } + /// + /// Gets or Sets a boolean indicating whether this ContentType is allowed at the root + /// + bool AllowedAsRoot { get; set; } + + /// + /// Gets or Sets a boolean indicating whether this ContentType is a Container + /// + /// + /// ContentType Containers doesn't show children in the tree, but rather in grid-type view. + /// + bool IsContainer { get; set; } + /// /// Gets or Sets a list of integer Ids of the ContentTypes allowed under the ContentType /// diff --git a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs index 8c256497dc..fe836449f5 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs @@ -38,6 +38,8 @@ namespace Umbraco.Core.Persistence.Factories dto.ContentTypeDto.NodeDto.UserId.HasValue ? dto.ContentTypeDto.NodeDto.UserId.Value : 0, + AllowedAsRoot = dto.ContentTypeDto.AllowAtRoot, + IsContainer = dto.ContentTypeDto.IsContainer, Trashed = dto.ContentTypeDto.NodeDto.Trashed }; return contentType; @@ -62,6 +64,8 @@ namespace Umbraco.Core.Persistence.Factories Icon = entity.Icon, Thumbnail = entity.Thumbnail, NodeId = entity.Id, + AllowAtRoot = entity.AllowedAsRoot, + IsContainer = entity.IsContainer, NodeDto = BuildNodeDto(entity) }; return contentTypeDto; diff --git a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs index a8fd54e945..b3254ab87f 100644 --- a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs @@ -38,6 +38,8 @@ namespace Umbraco.Core.Persistence.Factories dto.NodeDto.UserId.HasValue ? dto.NodeDto.UserId.Value : 0, + AllowedAsRoot = dto.AllowAtRoot, + IsContainer = dto.IsContainer, Trashed = dto.NodeDto.Trashed }; return contentType; @@ -52,6 +54,8 @@ namespace Umbraco.Core.Persistence.Factories Icon = entity.Icon, Thumbnail = entity.Thumbnail, NodeId = entity.Id, + AllowAtRoot = entity.AllowedAsRoot, + IsContainer = entity.IsContainer, NodeDto = BuildNodeDto(entity) }; return contentTypeDto; diff --git a/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs b/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs new file mode 100644 index 0000000000..86d5e026d8 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/BaseMapper.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Mappers +{ + internal abstract class BaseMapper + { + internal abstract void BuildMap(); + + internal abstract string Map(string propertyName); + + internal abstract void CacheMap(Expression> sourceMember, Expression> destinationMember); + + internal DtoMapModel ResolveMapping(Expression> sourceMember, Expression> destinationMember) + { + var source = ExpressionHelper.FindProperty(sourceMember); + var destination = ExpressionHelper.FindProperty(destinationMember) as PropertyInfo; + + return new DtoMapModel(typeof(TDestination), destination, source.Name); + } + + internal virtual string GetColumnName(Type dtoType, PropertyInfo dtoProperty) + { + var tableNameAttribute = dtoType.FirstAttribute(); + string tableName = tableNameAttribute.Value; + + var columnAttribute = dtoProperty.FirstAttribute(); + string columnName = columnAttribute.Name; + + string columnMap = string.Format("{0}.{1}", + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(tableName), + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(columnName)); + return columnMap; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs new file mode 100644 index 0000000000..5a627e9b09 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class ContentMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static ContentMapper Instance = new ContentMapper(); + + private ContentMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + if(PropertyInfoCache.IsEmpty) + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.Level, dto => dto.Level); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.Path, dto => dto.Path); + CacheMap(src => src.SortOrder, dto => dto.SortOrder); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Trashed, dto => dto.Trashed); + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.UserId, dto => dto.UserId); + //CacheMap(src => src.Name, dto => dto.Alias); + CacheMap(src => src.ExpireDate, dto => dto.ExpiresDate); + CacheMap(src => src.ReleaseDate, dto => dto.ReleaseDate); + CacheMap(src => src.Published, dto => dto.Published); + CacheMap(src => src.UpdateDate, dto => dto.UpdateDate); + //CacheMap(src => src, dto => dto.Newest); + //CacheMap(src => src.Template, dto => dto.TemplateId); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Version, dto => dto.VersionId); + } + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs new file mode 100644 index 0000000000..8a06fdebef --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class ContentTypeMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static ContentTypeMapper Instance = new ContentTypeMapper(); + + private ContentTypeMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + if (PropertyInfoCache.IsEmpty) + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.Level, dto => dto.Level); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.Path, dto => dto.Path); + CacheMap(src => src.SortOrder, dto => dto.SortOrder); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Trashed, dto => dto.Trashed); + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.UserId, dto => dto.UserId); + CacheMap(src => src.Alias, dto => dto.Alias); + CacheMap(src => src.AllowedAsRoot, dto => dto.AllowAtRoot); + CacheMap(src => src.Description, dto => dto.Description); + CacheMap(src => src.Icon, dto => dto.Icon); + CacheMap(src => src.IsContainer, dto => dto.IsContainer); + CacheMap(src => src.Thumbnail, dto => dto.Thumbnail); + } + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/DataTypeDefinitionMapper.cs b/src/Umbraco.Core/Persistence/Mappers/DataTypeDefinitionMapper.cs new file mode 100644 index 0000000000..575b4ee36d --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/DataTypeDefinitionMapper.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class DataTypeDefinitionMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static DataTypeDefinitionMapper Instance = new DataTypeDefinitionMapper(); + + private DataTypeDefinitionMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.Level, dto => dto.Level); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.Path, dto => dto.Path); + CacheMap(src => src.SortOrder, dto => dto.SortOrder); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Trashed, dto => dto.Trashed); + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.UserId, dto => dto.UserId); + CacheMap(src => src.ControlId, dto => dto.ControlId); + CacheMap(src => src.DatabaseType, dto => dto.DbType); + + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/DictionaryMapper.cs b/src/Umbraco.Core/Persistence/Mappers/DictionaryMapper.cs new file mode 100644 index 0000000000..a4ccc5a25b --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/DictionaryMapper.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class DictionaryMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static DictionaryMapper Instance = new DictionaryMapper(); + + private DictionaryMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.ItemKey, dto => dto.Key); + CacheMap(src => src.ParentId, dto => dto.Parent); + + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.Language.Id, dto => dto.LanguageId); + CacheMap(src => src.Value, dto => dto.Value); + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs b/src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs new file mode 100644 index 0000000000..b897e53895 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/DtoMapModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Reflection; + +namespace Umbraco.Core.Persistence.Mappers +{ + internal class DtoMapModel + { + public DtoMapModel(Type type, PropertyInfo propertyInfo, string sourcePropertyName) + { + Type = type; + PropertyInfo = propertyInfo; + SourcePropertyName = sourcePropertyName; + } + + public string SourcePropertyName { get; set; } + public Type Type { get; set; } + public PropertyInfo PropertyInfo { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/LanguageMapper.cs b/src/Umbraco.Core/Persistence/Mappers/LanguageMapper.cs new file mode 100644 index 0000000000..3f2dcf10ad --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/LanguageMapper.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class LanguageMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static LanguageMapper Instance = new LanguageMapper(); + + private LanguageMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.IsoCode, dto => dto.IsoCode); + CacheMap(src => src.CultureName, dto => dto.CultureName); + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs new file mode 100644 index 0000000000..0e55ebe7c9 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class MediaMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static MediaMapper Instance = new MediaMapper(); + + private MediaMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + if (PropertyInfoCache.IsEmpty) + { + CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); + CacheMap(src => src.Level, dto => dto.Level); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.Path, dto => dto.Path); + CacheMap(src => src.SortOrder, dto => dto.SortOrder); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.Trashed, dto => dto.Trashed); + CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.UserId, dto => dto.UserId); + } + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs index 6f43b8e304..3b27b88277 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs @@ -4,6 +4,9 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Mappers { + /// + /// Public api models to DTO mapper used by PetaPoco to map Properties to Columns + /// internal class ModelDtoMapper : IMapper { public void GetTableInfo(Type t, TableInfo ti) @@ -13,61 +16,44 @@ namespace Umbraco.Core.Persistence.Mappers { if (pi.DeclaringType == typeof(Content) || pi.DeclaringType == typeof(IContent)) { - switch (pi.Name) - { - case "Trashed": - columnName = "[umbracoNode].[trashed]"; - return true; - case "ParentId": - columnName = "[umbracoNode].[parentID]"; - return true; - case "UserId": - columnName = "[umbracoNode].[nodeUser]"; - return true; - case "Level": - columnName = "[umbracoNode].[level]"; - return true; - case "Path": - columnName = "[umbracoNode].[path]"; - return true; - case "SortOrder": - columnName = "[umbracoNode].[sortOrder]"; - return true; - case "NodeId": - columnName = "[umbracoNode].[id]"; - return true; - case "Published": - columnName = "[cmsDocument].[published]"; - return true; - case "Key": - columnName = "[umbracoNode].[uniqueID]"; - return true; - case "CreateDate": - columnName = "[umbracoNode].[createDate]"; - return true; - case "Name": - columnName = "[umbracoNode].[text]"; - return true; - } + columnName = ContentMapper.Instance.Map(pi.Name); + return true; } - if (pi.DeclaringType == typeof(ContentType) || pi.DeclaringType == typeof(IContentType)) + if (pi.DeclaringType == typeof(Models.Media) || pi.DeclaringType == typeof(IMedia)) { - switch (pi.Name) - { - case "Alias": - columnName = "[cmsContentType].[alias]"; - return true; - case "Icon": - columnName = "[cmsContentType].[icon]"; - return true; - case "Thumbnail": - columnName = "[cmsContentType].[thumbnail]"; - return true; - case "Description": - columnName = "[cmsContentType].[description]"; - return true; - } + columnName = MediaMapper.Instance.Map(pi.Name); + return true; + } + + if (pi.DeclaringType == typeof(ContentType) || pi.DeclaringType == typeof(IContentType) || pi.DeclaringType == typeof(IMediaType)) + { + columnName = ContentTypeMapper.Instance.Map(pi.Name); + } + + if (pi.DeclaringType == typeof(DataTypeDefinition)) + { + columnName = DataTypeDefinitionMapper.Instance.Map(pi.Name); + } + + if (pi.DeclaringType == typeof(DictionaryItem)) + { + columnName = DictionaryMapper.Instance.Map(pi.Name); + } + + if (pi.DeclaringType == typeof(Language)) + { + columnName = LanguageMapper.Instance.Map(pi.Name); + } + + if (pi.DeclaringType == typeof(Relation)) + { + columnName = RelationMapper.Instance.Map(pi.Name); + } + + if (pi.DeclaringType == typeof(RelationType)) + { + columnName = RelationTypeMapper.Instance.Map(pi.Name); } return true; diff --git a/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs b/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs new file mode 100644 index 0000000000..a7a6ff9c3b --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/RelationMapper.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class RelationMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static RelationMapper Instance = new RelationMapper(); + + private RelationMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.ChildId, dto => dto.ChildId); + CacheMap(src => src.Comment, dto => dto.Comment); + CacheMap(src => src.CreateDate, dto => dto.Datetime); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.RelationType.Id, dto => dto.RelationType); + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs new file mode 100644 index 0000000000..c9c9cdea84 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/RelationTypeMapper.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + /// + /// Represents a to DTO mapper used to translate the properties of the public api + /// implementation to that of the database's DTO as sql: [tableName].[columnName]. + /// + internal sealed class RelationTypeMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static RelationTypeMapper Instance = new RelationTypeMapper(); + + private RelationTypeMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.Alias, dto => dto.Alias); + CacheMap(src => src.ChildObjectType, dto => dto.ChildObjectType); + CacheMap(src => src.IsBidirectional, dto => dto.Dual); + CacheMap(src => src.Name, dto => dto.Name); + CacheMap(src => src.ParentObjectType, dto => dto.ParentObjectType); + } + + internal override string Map(string propertyName) + { + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 985c79d396..87eb953737 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -79,6 +79,7 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); + //NOTE: This doesn't allow properties to be part of the query var dtos = Database.Fetch(sql); foreach (var dto in dtos) diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs index 12216857cb..4aa183a2ca 100644 --- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs @@ -10,6 +10,9 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { + /// + /// Represents a repository for doing CRUD operations for + /// internal class LanguageRepository : PetaPocoRepositoryBase, ILanguageRepository { public LanguageRepository(IUnitOfWork work) : base(work) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 80e5919204..dfd31624fa 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -130,7 +130,17 @@ + + + + + + + + + +