Removing usage of IProfile on DataTypeDefinition.

Giving the Code First POC an extra kick in the conventions, so the default PropertyType convention can be overridden.
If a Definition doesn't exist for the selected DataType one will be created - with backing unit test.
This commit is contained in:
Morten Christensen
2012-11-24 19:07:06 -01:00
parent e32880f215
commit ff89383386
14 changed files with 240 additions and 121 deletions

View File

@@ -2,7 +2,6 @@
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
namespace Umbraco.Core.Models
{
@@ -22,7 +21,7 @@ namespace Umbraco.Core.Models
private int _sortOrder;
private int _level;
private string _path;
private IProfile _creator;
private int _creatorId;
private bool _trashed;
private Guid _controlId;
private DataTypeDatabaseType _databaseType;
@@ -38,7 +37,7 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, int>(x => x.SortOrder);
private static readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, int>(x => x.Level);
private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, string>(x => x.Path);
private static readonly PropertyInfo UserIdSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, IProfile>(x => x.Creator);
private static readonly PropertyInfo UserIdSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, int>(x => x.CreatorId);
private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, bool>(x => x.Trashed);
private static readonly PropertyInfo ControlIdSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, Guid>(x => x.ControlId);
private static readonly PropertyInfo DatabaseTypeSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, DataTypeDatabaseType>(x => x.DatabaseType);
@@ -118,12 +117,12 @@ namespace Umbraco.Core.Models
/// Id of the user who created this entity
/// </summary>
[DataMember]
public IProfile Creator
public int CreatorId
{
get { return _creator; }
get { return _creatorId; }
set
{
_creator = value;
_creatorId = value;
OnPropertyChanged(UserIdSelector);
}
}
@@ -169,5 +168,11 @@ namespace Umbraco.Core.Models
OnPropertyChanged(DatabaseTypeSelector);
}
}
internal override void AddingEntity()
{
base.AddingEntity();
Key = Guid.NewGuid();
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
namespace Umbraco.Core.Models
{
@@ -42,7 +41,7 @@ namespace Umbraco.Core.Models
/// Id of the user who created this entity
/// </summary>
[DataMember]
IProfile Creator { get; set; }
int CreatorId { get; set; }
/// <summary>
/// Boolean indicating whether this entity is Trashed or not.

View File

@@ -1,7 +1,6 @@
using System;
using System.Globalization;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
@@ -36,7 +35,7 @@ namespace Umbraco.Core.Persistence.Factories
Path = dto.NodeDto.Path,
SortOrder = dto.NodeDto.SortOrder,
Trashed = dto.NodeDto.Trashed,
Creator = new Profile(dto.NodeDto.UserId.Value, "")
CreatorId = dto.NodeDto.UserId.Value
};
return dataTypeDefinition;
@@ -81,7 +80,7 @@ namespace Umbraco.Core.Persistence.Factories
Text = entity.Name,
Trashed = entity.Trashed,
UniqueId = entity.Key,
UserId = entity.Creator.Id.SafeCast<int>()
UserId = entity.CreatorId
};
return nodeDto;

View File

@@ -34,7 +34,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap<DataTypeDefinition, NodeDto>(src => src.Name, dto => dto.Text);
CacheMap<DataTypeDefinition, NodeDto>(src => src.Trashed, dto => dto.Trashed);
CacheMap<DataTypeDefinition, NodeDto>(src => src.Key, dto => dto.UniqueId);
CacheMap<DataTypeDefinition, NodeDto>(src => src.Creator, dto => dto.UserId);
CacheMap<DataTypeDefinition, NodeDto>(src => src.CreatorId, dto => dto.UserId);
CacheMap<DataTypeDefinition, DataTypeDto>(src => src.ControlId, dto => dto.ControlId);
CacheMap<DataTypeDefinition, DataTypeDto>(src => src.DatabaseType, dto => dto.DbType);

View File

@@ -36,7 +36,7 @@ namespace Umbraco.Core.Services
/// </summary>
/// <param name="dataTypeDefinition"><see cref="IDataTypeDefinition"/> to save</param>
/// <param name="userId">Id of the user issueing the save</param>
void Save(IDataTypeDefinition dataTypeDefinition, int userId);
void Save(IDataTypeDefinition dataTypeDefinition, int userId = -1);
/// <summary>
/// Deletes an <see cref="IDataTypeDefinition"/>

View File

@@ -1,9 +1,11 @@
using System;
using Umbraco.Core.Models;
using Umbraco.Tests.CodeFirst.Definitions;
namespace Umbraco.Tests.CodeFirst.Attributes
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class PropertyTypeAttribute : Attribute
public class PropertyTypeAttribute : PropertyTypeConventionAttribute
{
public PropertyTypeAttribute(Type type)
{
@@ -11,6 +13,7 @@ namespace Umbraco.Tests.CodeFirst.Attributes
PropertyGroup = "Generic Properties";
Mandatory = false;
ValidationRegExp = string.Empty;
DatabaseType = DataTypeDatabaseType.Nvarchar;
}
/// <summary>
@@ -18,6 +21,15 @@ namespace Umbraco.Tests.CodeFirst.Attributes
/// </summary>
public Type Type { get; private set; }
/// <summary>
/// Gets or sets the Database Type to use for the chosen DataType.
/// </summary>
/// <remarks>
/// Please note that the DatabaseType only needs to be set when
/// creating a new DataType definition.
/// </remarks>
public DataTypeDatabaseType DatabaseType { get; set; }
/// <summary>
/// Gets or sets the Name of the PropertyGroup that this PropertyType belongs to
/// </summary>
@@ -32,5 +44,17 @@ namespace Umbraco.Tests.CodeFirst.Attributes
/// Regular Expression for validating PropertyType's values
/// </summary>
public string ValidationRegExp { get; set; }
public override PropertyDefinition GetPropertyConvention()
{
var definition = new PropertyDefinition();
definition.Mandatory = Mandatory;
definition.ValidationRegExp = string.IsNullOrEmpty(ValidationRegExp) ? ValidationRegExp : string.Empty;
definition.PropertyGroup = string.IsNullOrEmpty(PropertyGroup) ? "Generic Properties" : PropertyGroup;
definition.DataTypeDefinition = Conventions.DataTypeConvention(this, Type);
return definition;
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
using Umbraco.Tests.CodeFirst.Definitions;
namespace Umbraco.Tests.CodeFirst.Attributes
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public abstract class PropertyTypeConventionAttribute : Attribute
{
public abstract PropertyDefinition GetPropertyConvention();
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration;
@@ -9,6 +10,7 @@ using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Serialization;
using Umbraco.Tests.CodeFirst.Attributes;
using Umbraco.Tests.CodeFirst.Definitions;
using Umbraco.Tests.CodeFirst.TestModels;
using Umbraco.Tests.TestHelpers;
@@ -45,6 +47,21 @@ namespace Umbraco.Tests.CodeFirst
SerializationService = new SerializationService(serviceStackSerializer);
}
[Test]
public void Can_Create_Model_With_NonExisting_DataTypeDefinition()
{
ContentTypeDefinitionFactory.ClearContentTypeCache();
var modelType = typeof(ModelWithNewDataType);
var contentType = ContentTypeDefinitionFactory.GetContentTypeDefinition(modelType);
var mappedContentTypes = ContentTypeDefinitionFactory.RetrieveMappedContentTypes();
ServiceContext.ContentTypeService.Save(mappedContentTypes);
var model = ServiceContext.ContentTypeService.GetContentType(1046);
Assert.That(model, Is.Not.Null);
}
[Test]
public void Can_Resolve_ContentType_From_Decorated_Home_Model()
{

View File

@@ -8,7 +8,6 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Tests.CodeFirst.Attributes;
using umbraco.interfaces;
namespace Umbraco.Tests.CodeFirst.Definitions
{
@@ -43,29 +42,44 @@ namespace Umbraco.Tests.CodeFirst.Definitions
var objProperties = modelType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).ToList();
foreach (var propertyInfo in objProperties)
{
var definition = new PropertyDefinition();
var propertyTypeAttribute = propertyInfo.FirstAttribute<PropertyTypeConventionAttribute>();
var definition = propertyTypeAttribute == null
? new PropertyDefinition()
: propertyTypeAttribute.GetPropertyConvention();
var aliasAttribute = propertyInfo.FirstAttribute<AliasAttribute>();
definition.Alias = PropertyTypeAliasConvention(aliasAttribute, propertyInfo.Name);
definition.Name = PropertyTypeNameConvention(aliasAttribute, propertyInfo.Name);
var descriptionAttribute = propertyInfo.FirstAttribute<DescriptionAttribute>();
definition.Description = descriptionAttribute != null ? descriptionAttribute.Description : string.Empty;
var sortOrderAttribute = propertyInfo.FirstAttribute<SortOrderAttribute>();
definition.Order = sortOrderAttribute != null ? sortOrderAttribute.Order : order;
var propertyTypeAttribute = propertyInfo.FirstAttribute<PropertyTypeAttribute>();
definition.Mandatory = propertyTypeAttribute != null && propertyTypeAttribute.Mandatory;
definition.ValidationRegExp = propertyTypeAttribute == null ? string.Empty : propertyTypeAttribute.ValidationRegExp;
definition.PropertyGroup = propertyTypeAttribute == null ? "Generic Properties" : propertyTypeAttribute.PropertyGroup;
definition.DataTypeDefinition = DataTypeConvention(propertyTypeAttribute, propertyInfo.PropertyType);
//RichtextAttribute convention
var richtextAttribute = propertyInfo.FirstAttribute<RichtextAttribute>();
if(richtextAttribute != null)
//DataTypeDefinition fallback
if(definition.DataTypeDefinition == null)
{
definition.DataTypeDefinition = DataTypeConvention(richtextAttribute, propertyInfo.PropertyType);
definition.DataTypeDefinition = Conventions.DataTypeConvention(null, propertyInfo.PropertyType);
}
if(string.IsNullOrEmpty(definition.PropertyGroup))
{
definition.PropertyGroup = "Generic Properties";
}
//Alias fallback
if (string.IsNullOrEmpty(definition.Alias))
{
var aliasAttribute = propertyInfo.FirstAttribute<AliasAttribute>();
definition.Alias = Conventions.PropertyTypeAliasConvention(aliasAttribute, propertyInfo.Name);
definition.Name = Conventions.PropertyTypeNameConvention(aliasAttribute, propertyInfo.Name);
}
//Description fallback
if (string.IsNullOrEmpty(definition.Description))
{
var descriptionAttribute = propertyInfo.FirstAttribute<DescriptionAttribute>();
definition.Description = descriptionAttribute != null
? descriptionAttribute.Description
: string.Empty;
}
//SortOrder fallback
if (definition.Order == default(int))
{
var sortOrderAttribute = propertyInfo.FirstAttribute<SortOrderAttribute>();
definition.Order = sortOrderAttribute != null ? sortOrderAttribute.Order : order;
}
definitions.Add(definition);
@@ -177,84 +191,6 @@ namespace Umbraco.Tests.CodeFirst.Definitions
_contentTypeCache.Clear();
}
/// <summary>
/// Convention to get a DataTypeDefinition from the PropertyTypeAttribute or the type of the property itself
/// </summary>
/// <param name="attribute"></param>
/// <param name="type"></param>
/// <returns></returns>
private static IDataTypeDefinition DataTypeConvention(PropertyTypeAttribute attribute, Type type)
{
if(attribute != null)
{
var instance = Activator.CreateInstance(attribute.Type);
var dataType = instance as IDataType;
return GetDataTypeByControlId(dataType.Id);
}
return TypeToPredefinedDataTypeConvention(type);
}
/// <summary>
/// Convention to get predefined DataTypeDefinitions based on the Type of the property
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static IDataTypeDefinition TypeToPredefinedDataTypeConvention(Type type)
{
if(type == typeof(bool))
{
return GetDataTypeByControlId(new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a"));// Yes/No DataType
}
if(type == typeof(int))
{
return GetDataTypeByControlId(new Guid("1413afcb-d19a-4173-8e9a-68288d2a73b8"));// Number DataType
}
if(type == typeof(DateTime))
{
return GetDataTypeByControlId(new Guid("23e93522-3200-44e2-9f29-e61a6fcbb79a"));// Date Picker DataType
}
return GetDataTypeByControlId(new Guid("ec15c1e5-9d90-422a-aa52-4f7622c63bea"));// Standard textfield
}
/// <summary>
/// Gets the <see cref="IDataTypeDefinition"/> from the DataTypeService by its control Id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private static IDataTypeDefinition GetDataTypeByControlId(Guid id)
{
//TODO Create Definition if none exists
var definition = ServiceFactory.DataTypeService.GetDataTypeDefinitionByControlId(id);
return definition.FirstOrDefault();
}
/// <summary>
/// Convention to get the Alias of the PropertyType from the AliasAttribute or the property itself
/// </summary>
/// <param name="attribute"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
private static string PropertyTypeAliasConvention(AliasAttribute attribute, string propertyName)
{
return attribute == null ? propertyName.ToUmbracoAlias() : attribute.Alias;
}
/// <summary>
/// Convention to get the Name of the PropertyType from the AliasAttribute or the property itself
/// </summary>
/// <param name="attribute"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
private static string PropertyTypeNameConvention(AliasAttribute attribute, string propertyName)
{
if (attribute == null)
return propertyName.SplitPascalCasing();
return string.IsNullOrEmpty(attribute.Name) ? propertyName.SplitPascalCasing() : attribute.Name;
}
/// <summary>
/// Convention that converts a class decorated with the ContentTypeAttribute to an initial ContentType
/// </summary>
@@ -314,13 +250,14 @@ namespace Umbraco.Tests.CodeFirst.Definitions
var @alias = engine == RenderingEngine.Mvc
? templateName.Replace(".cshtml", "").Replace(".vbhtml", "")
: templateName.Replace(".masterpage", "");
var name = engine == RenderingEngine.Mvc
? string.Concat(@alias, ".cshtml")
: string.Concat(@alias, ".masterpage");
var template = ServiceFactory.FileService.GetTemplateByAlias(@alias);
if(template == null)
{
var name = engine == RenderingEngine.Mvc
? string.Concat(@alias, ".cshtml")
: string.Concat(@alias, ".masterpage");
template = new Template(string.Empty, name, @alias) { CreatorId = 0, Content = string.Empty};
ServiceFactory.FileService.SaveTemplate(template);
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Tests.CodeFirst.Attributes;
using umbraco.interfaces;
namespace Umbraco.Tests.CodeFirst.Definitions
{
public static class Conventions
{
/// <summary>
/// Convention to get a DataTypeDefinition from the PropertyTypeAttribute or the type of the property itself
/// </summary>
/// <param name="attribute"></param>
/// <param name="type"></param>
/// <returns></returns>
public static IDataTypeDefinition DataTypeConvention(PropertyTypeAttribute attribute, Type type)
{
if (attribute != null)
{
var instance = Activator.CreateInstance(attribute.Type);
var dataType = instance as IDataType;
var definition = GetDataTypeByControlId(dataType.Id);
//If the DataTypeDefinition doesn't exist we create a new one
if (definition == null)
{
definition = new DataTypeDefinition(-1, dataType.Id)
{
DatabaseType = attribute.DatabaseType,
Name = dataType.DataTypeName
};
ServiceFactory.DataTypeService.Save(definition, 0);
}
return definition;
}
return TypeToPredefinedDataTypeConvention(type);
}
/// <summary>
/// Convention to get predefined DataTypeDefinitions based on the Type of the property
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IDataTypeDefinition TypeToPredefinedDataTypeConvention(Type type)
{
if (type == typeof(bool))
{
return GetDataTypeByControlId(new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a"));// Yes/No DataType
}
if (type == typeof(int))
{
return GetDataTypeByControlId(new Guid("1413afcb-d19a-4173-8e9a-68288d2a73b8"));// Number DataType
}
if (type == typeof(DateTime))
{
return GetDataTypeByControlId(new Guid("23e93522-3200-44e2-9f29-e61a6fcbb79a"));// Date Picker DataType
}
return GetDataTypeByControlId(new Guid("ec15c1e5-9d90-422a-aa52-4f7622c63bea"));// Standard textfield
}
/// <summary>
/// Gets the <see cref="IDataTypeDefinition"/> from the DataTypeService by its control Id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static IDataTypeDefinition GetDataTypeByControlId(Guid id)
{
var definitions = ServiceFactory.DataTypeService.GetDataTypeDefinitionByControlId(id);
return definitions.FirstOrDefault();
}
/// <summary>
/// Convention to get the Alias of the PropertyType from the AliasAttribute or the property itself
/// </summary>
/// <param name="attribute"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public static string PropertyTypeAliasConvention(AliasAttribute attribute, string propertyName)
{
return attribute == null ? propertyName.ToUmbracoAlias() : attribute.Alias;
}
/// <summary>
/// Convention to get the Name of the PropertyType from the AliasAttribute or the property itself
/// </summary>
/// <param name="attribute"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public static string PropertyTypeNameConvention(AliasAttribute attribute, string propertyName)
{
if (attribute == null)
return propertyName.SplitPascalCasing();
return string.IsNullOrEmpty(attribute.Name) ? propertyName.SplitPascalCasing() : attribute.Name;
}
}
}

View File

@@ -4,18 +4,26 @@ using umbraco.editorControls.textfield;
namespace Umbraco.Tests.CodeFirst.TestModels
{
[ContentType("modelPage", AllowedChildContentTypes = new[] { typeof(ContentPage) }, AllowedTemplates = new[]{"umbMaster"})]
[ContentType("modelPage",
AllowedChildContentTypes = new[] { typeof(ContentPage) },
AllowedTemplates = new[]{"umbMaster"})]
public class DecoratedModelPage
{
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
[SortOrder(0)]
public string Author { get; set; }
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
[SortOrder(1)]
public string Title { get; set; }
[Richtext(PropertyGroup = "Content")]
[SortOrder(2)]
[Description("Richtext field to enter the main content of the page")]
public string BodyContent { get; set; }
[SortOrder(3)]
[Alias("publishedDate", Name = "Publish Date")]
public DateTime PublishDate { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using Umbraco.Tests.CodeFirst.Attributes;
using umbraco.editorControls.textfield;
using umbraco.editorControls.tinymce;
namespace Umbraco.Tests.CodeFirst.TestModels
{
public class ModelWithNewDataType
{
[PropertyType(typeof(TextFieldDataType), PropertyGroup = "Content")]
public string Title { get; set; }
[PropertyType(typeof(TinyMCEDataType), PropertyGroup = "Content")]
public string BodyContent { get; set; }
}
}

View File

@@ -133,7 +133,7 @@ namespace Umbraco.Tests.Persistence.Repositories
{
DatabaseType = DataTypeDatabaseType.Integer,
Name = "AgeDataType",
Creator = new Profile(0, "Administrator")
CreatorId = 0
};
// Act
@@ -158,7 +158,7 @@ namespace Umbraco.Tests.Persistence.Repositories
{
DatabaseType = DataTypeDatabaseType.Integer,
Name = "AgeDataType",
Creator = new Profile(0, "Administrator")
CreatorId = 0
};
repository.AddOrUpdate(dataTypeDefinition);
unitOfWork.Commit();
@@ -188,7 +188,7 @@ namespace Umbraco.Tests.Persistence.Repositories
{
DatabaseType = DataTypeDatabaseType.Integer,
Name = "AgeDataType",
Creator = new Profile(0, "Administrator")
CreatorId = 0
};
// Act

View File

@@ -93,17 +93,20 @@
<Compile Include="CodeFirst\Attributes\ContentTypeAttribute.cs" />
<Compile Include="CodeFirst\Attributes\DescriptionAttribute.cs" />
<Compile Include="CodeFirst\Attributes\PropertyTypeAttribute.cs" />
<Compile Include="CodeFirst\Attributes\PropertyTypeConventionAttribute.cs" />
<Compile Include="CodeFirst\Attributes\RichtextAttribute.cs" />
<Compile Include="CodeFirst\Attributes\SortOrderAttribute.cs" />
<Compile Include="CodeFirst\CodeFirstTests.cs" />
<Compile Include="CodeFirst\ContentTypeBase.cs" />
<Compile Include="CodeFirst\Definitions\ContentTypeDefinitionFactory.cs" />
<Compile Include="CodeFirst\Definitions\Conventions.cs" />
<Compile Include="CodeFirst\Definitions\DependencyField.cs" />
<Compile Include="CodeFirst\Definitions\PropertyDefinition.cs" />
<Compile Include="CodeFirst\TestModels\AdvancedContentPage.cs" />
<Compile Include="CodeFirst\TestModels\ContentPage.cs" />
<Compile Include="CodeFirst\TestModels\DecoratedModelPage.cs" />
<Compile Include="CodeFirst\TestModels\Home.cs" />
<Compile Include="CodeFirst\TestModels\ModelWithNewDataType.cs" />
<Compile Include="CodeFirst\TestModels\PlainPocoType.cs" />
<Compile Include="CodeFirst\TestModels\SimpleContentPage.cs" />
<Compile Include="CodeFirst\TestModels\TextPage.cs" />