PropertyValueConverter - cleanup WIP

This commit is contained in:
Stephan
2017-07-31 11:18:53 +02:00
parent a0fe56ee7c
commit 9029d8dbd9
6 changed files with 5 additions and 337 deletions

View File

@@ -16,15 +16,6 @@ namespace Umbraco.Tests.PublishedContent
[TestFixture]
public class ModelsAndConvertersTests
{
// fixme
// naming: IPublishedProperty is IPropertySetProperty or IFacadeProperty of some sort
// naming: general naming sucks at the moment
// caching: re-think how properties are cached
// - for true NuCache content it probably is OK (but needs explanation)
// - for true Xml cache content it probably is OK (but needs explanation)
// - for pure sets, I have no idea - should at least cache at content level?
// hold on - PropertySetPropertyBase probably handles it - need to check
#region ModelType
[Test]
@@ -417,7 +408,9 @@ namespace Umbraco.Tests.PublishedContent
.Setup(x => x.CreateSetProperty(It.IsAny<PublishedPropertyType>(), It.IsAny<IPropertySet>(), It.IsAny<bool>(), It.IsAny<PropertyCacheLevel>(), It.IsAny<object>()))
.Returns<PublishedPropertyType, IPropertySet, bool, PropertyCacheLevel, object>((propertyType, set, previewing, refCacheLevel, value) =>
{
// ReSharper disable AccessToModifiedClosure
return new TestPropertySetProperty(propertyType, set, previewing, refCacheLevel, value, () => snapshotCache, () => facadeCache);
// ReSharper restore AccessToModifiedClosure
});
var facadeService = facadeServiceMock.Object;
@@ -449,6 +442,7 @@ namespace Umbraco.Tests.PublishedContent
Assert.AreEqual(snapshotCount2, snapshotCache.Count);
Assert.AreEqual(facadeCount2, facadeCache.Count);
Assert.AreEqual(facadeCount2, oldFacadeCache.Count);
Assert.AreEqual((interConverts == 1 ? 1 : 3) + facadeCache.Count, converter.InterConverts);
@@ -459,6 +453,7 @@ namespace Umbraco.Tests.PublishedContent
Assert.AreEqual(1, converter.SourceConverts);
Assert.AreEqual(snapshotCount2, snapshotCache.Count);
Assert.AreEqual(snapshotCount2, oldSnapshotCache.Count);
Assert.AreEqual(facadeCount2, facadeCache.Count);
Assert.AreEqual((interConverts == 1 ? 1 : 4) + facadeCache.Count + snapshotCache.Count, converter.InterConverts);
@@ -481,7 +476,7 @@ namespace Umbraco.Tests.PublishedContent
Assert.Throws<Exception>(() =>
{
var set1 = new PropertySet(setType1, Guid.NewGuid(), new Dictionary<string, object> { { "prop1", "1234" } }, false);
var unused = new PropertySet(setType1, Guid.NewGuid(), new Dictionary<string, object> { { "prop1", "1234" } }, false);
});
}

View File

@@ -1,51 +0,0 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using NUnit.Framework;
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
{
[TestFixture]
public class CallingMethodTests
{
private readonly Func<MethodBase> _myProperty = MethodBase.GetCurrentMethod;
public string AField
{
// that attribute is REQUIRED for the test to work in RELEASE mode
// and then it kills performance, probably, so that whole way of
// doing things is probably a Bad Thing.
[MethodImpl(MethodImplOptions.NoInlining)]
get { return Resolve(_myProperty()); }
}
public string AField2
{
get { return Resolve2(); }
}
private string Resolve(MethodBase m)
{
return m.Name.Replace("get_", "");
}
// that would be the correct way of doing it, works in RELEASE mode
// as well as DEBUG and is optimized, etc
public string Resolve2([CallerMemberName] string memberName = null)
{
return memberName;
}
[Test]
public void GetMyName()
{
Assert.AreEqual("AField", AField);
}
[Test]
public void GetMyName2()
{
Assert.AreEqual("AField2", AField2);
}
}
}

View File

@@ -1,42 +0,0 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
{
/// <summary>
/// Represents a Subpage which acts as the strongly typed model for a Doc Type
/// with alias "Subpage" and "Subpage" as the allowed child type.
///
/// Similar to the Textpage this model could also be generated, but it could also
/// act as a Code-First model by using the attributes shown on the various properties,
/// which decorate the model with information about the Document Type, its
/// Property Groups and Property Types.
/// </summary>
public class Subpage : TypedModelBase
{
public Subpage(IPublishedContent publishedContent) : base(publishedContent)
{
}
public string Title { get { return Resolve<string>(Property()); } }
public string BodyText { get { return Resolve<string>(Property()); } }
public new Textpage Parent
{
get
{
return Parent<Textpage>();
}
}
public IEnumerable<Subpage> Subpages
{
get
{
return Children<Subpage>(ContentTypeAlias());
}
}
}
}

View File

@@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
{
/// <summary>
/// Represents a Textpage which acts as the strongly typed model for a Doc Type
/// with alias "Textpage" and "Subpage" as the allowed child type.
///
/// The basic properties are resolved by convention using the Resolve-Type-PropertyTypeAlias
/// convention available through the base class' protected Resolve-method and Property-delegate.
///
/// The Textpage allows the use of Subpage and Textpage as child doc types, which are exposed as a
/// collection using the Children-Type-ContentTypeAlias convention available through the
/// base class' protected Children-method and ContentTypeAlias-delegate.
/// </summary>
/// <remarks>
/// This code can easily be generated using simple conventions for the types and names
/// of the properties that this type of strongly typed model exposes.
/// </remarks>
public class Textpage : TypedModelBase
{
public Textpage(IPublishedContent publishedContent) : base(publishedContent)
{
}
public string Title { get { return Resolve<string>(Property()); } }
public string BodyText { get { return Resolve<string>(Property()); } }
public string AuthorName { get { return Resolve<string>(Property()); } }
public DateTime Date { get { return Resolve<DateTime>(Property()); } }
public new Textpage Parent
{
get
{
return Parent<Textpage>();
}
}
public IEnumerable<Textpage> Textpages
{
get
{
return Children<Textpage>(ContentTypeAlias());
}
}
public IEnumerable<Subpage> Subpages
{
get
{
return Children<Subpage>(ContentTypeAlias());
}
}
}
}

View File

@@ -1,169 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data.Entity.Design.PluralizationServices;
using System.Globalization;
using System.Reflection;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web;
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
{
/// <summary>
/// Represents the abstract base class for a 'TypedModel', which basically wraps IPublishedContent
/// underneath a strongly typed model like "Textpage" and "Subpage".
/// Because IPublishedContent is used under the hood there is no need for additional mapping, so the
/// only added cost should be the creation of the objects, which the IPublishedContent instance is
/// passed into.
///
/// This base class exposes a simple way to write property getters by convention without
/// using the string alias of a PropertyType (this is resolved by the use of the Property delegate).
///
/// This base class also exposes query options like Parent, Children, Ancestors and Descendants,
/// which can be used for collections of strongly typed child models/objects. These types of collections
/// typically corresponds to 'allowed child content types' on a Doc Type (at different levels).
///
/// The IPublishedContent properties are also exposed through this base class, but only
/// by casting the typed model to IPublishedContent, so the properties doesn't show up by default:
/// ie. ((IPublishedContent)textpage).Url
/// </summary>
public abstract class TypedModelBase : PublishedContentWrapped // IPublishedContent
{
protected TypedModelBase(IPublishedContent publishedContent)
: base(publishedContent)
{ }
protected readonly Func<MethodBase> Property = MethodBase.GetCurrentMethod;
protected readonly Func<MethodBase> ContentTypeAlias = MethodBase.GetCurrentMethod;
#region Properties
protected T Resolve<T>(MethodBase methodBase)
{
var propertyTypeAlias = methodBase.ToUmbracoAlias();
return Resolve<T>(propertyTypeAlias);
}
protected T Resolve<T>(string propertyTypeAlias)
{
return Content.Value<T>(propertyTypeAlias);
}
protected T Resolve<T>(MethodBase methodBase, T ifCannotConvert)
{
var propertyTypeAlias = methodBase.ToUmbracoAlias();
return Resolve<T>(propertyTypeAlias, ifCannotConvert);
}
protected T Resolve<T>(string propertyTypeAlias, T ifCannotConvert)
{
return Content.Value<T>(propertyTypeAlias, false, ifCannotConvert);
}
protected T Resolve<T>(MethodBase methodBase, bool recursive, T ifCannotConvert)
{
var propertyTypeAlias = methodBase.ToUmbracoAlias();
return Resolve<T>(propertyTypeAlias, recursive, ifCannotConvert);
}
protected T Resolve<T>(string propertyTypeAlias, bool recursive, T ifCannotConvert)
{
return Content.Value<T>(propertyTypeAlias, recursive, ifCannotConvert);
}
#endregion
#region Querying
protected T Parent<T>() where T : TypedModelBase
{
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
if (constructorInfo == null)
throw new Exception("No valid constructor found");
return (T) constructorInfo.Invoke(new object[] {Content.Parent});
}
protected IEnumerable<T> Children<T>(MethodBase methodBase) where T : TypedModelBase
{
var docTypeAlias = methodBase.CleanCallingMethodName();
return Children<T>(docTypeAlias);
}
protected IEnumerable<T> Children<T>(string docTypeAlias) where T : TypedModelBase
{
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
if(constructorInfo == null)
throw new Exception("No valid constructor found");
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
return Content.Children.Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
}
protected IEnumerable<T> Ancestors<T>(MethodBase methodBase) where T : TypedModelBase
{
var docTypeAlias = methodBase.CleanCallingMethodName();
return Ancestors<T>(docTypeAlias);
}
protected IEnumerable<T> Ancestors<T>(string docTypeAlias) where T : TypedModelBase
{
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
if (constructorInfo == null)
throw new Exception("No valid constructor found");
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
return Content.Ancestors().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
}
protected IEnumerable<T> Descendants<T>(MethodBase methodBase) where T : TypedModelBase
{
var docTypeAlias = methodBase.CleanCallingMethodName();
return Descendants<T>(docTypeAlias);
}
protected IEnumerable<T> Descendants<T>(string docTypeAlias) where T : TypedModelBase
{
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
if (constructorInfo == null)
throw new Exception("No valid constructor found");
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
return Content.Descendants().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
}
#endregion
}
/// <summary>
/// Extension methods for MethodBase, which are used to clean the name of the calling method "get_BodyText"
/// to "BodyText" and then make it camel case according to the UmbracoAlias convention "bodyText".
/// There is also a string extension for making plural words singular, which is used when going from
/// something like "Subpages" to "Subpage" for Children/Ancestors/Descendants Doc Type aliases.
/// </summary>
public static class TypeExtensions
{
public static string CleanCallingMethodName(this MethodBase methodBase)
{
return methodBase.Name.Replace("get_", "");
}
public static string ToUmbracoAlias(this MethodBase methodBase)
{
return methodBase.CleanCallingMethodName().ToSafeAlias();
}
public static string ToSingular(this string pluralWord)
{
var service = PluralizationService.CreateService(new CultureInfo("en-US"));
if (service.IsPlural(pluralWord))
return service.Singularize(pluralWord);
return pluralWord;
}
}
}

View File

@@ -374,10 +374,6 @@
<Compile Include="PropertyEditors\PropertyEditorValueEditorTests.cs" />
<Compile Include="PublishedContent\PublishedContentRequestEngineTests.cs" />
<Compile Include="PublishedContent\RootNodeTests.cs" />
<Compile Include="PublishedContent\StronglyTypedModels\CallingMethodTests.cs" />
<Compile Include="PublishedContent\StronglyTypedModels\Subpage.cs" />
<Compile Include="PublishedContent\StronglyTypedModels\Textpage.cs" />
<Compile Include="PublishedContent\StronglyTypedModels\TypedModelBase.cs" />
<Compile Include="Scheduling\BackgroundTaskRunnerTests.cs" />
<Compile Include="Misc\ApplicationUrlHelperTests.cs" />
<Compile Include="Services\FileServiceTests.cs" />