Committing some POC code in the test project for strongly typed models/queries
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
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 Textpage Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return Parent<Textpage>();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Subpage> Subpages
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children<Subpage>(ContentTypeAlias());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
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 Textpage Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return Parent<Textpage>();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Textpage> Textpages
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children<Textpage>(ContentTypeAlias());
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Subpage> Subpages
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children<Subpage>(ContentTypeAlias());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
{
|
||||
public class TextpageModel : TypedModelBase
|
||||
{
|
||||
public TextpageModel(IPublishedContent publishedContent) : base(publishedContent)
|
||||
{
|
||||
}
|
||||
|
||||
public string Title { get { return Resolve(Property(), DefaultString); } }
|
||||
|
||||
public string BodyText { get { return Resolve(Property(), DefaultString); } }
|
||||
|
||||
public string AuthorName { get { return Resolve<string>(Property()); } }
|
||||
|
||||
public DateTime Date { get { return Resolve(Property(), DefaultDateTime); } }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,34 @@
|
||||
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;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
{
|
||||
public abstract class TypedModelBase
|
||||
/// <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 : IPublishedContent
|
||||
{
|
||||
private readonly IPublishedContent _publishedContent;
|
||||
|
||||
@@ -15,56 +37,168 @@ namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
_publishedContent = publishedContent;
|
||||
}
|
||||
|
||||
public readonly Func<MethodBase> Property = MethodBase.GetCurrentMethod;
|
||||
public static string DefaultString = default(string);
|
||||
public static int DefaultInteger = default(int);
|
||||
public static bool DefaultBool = default(bool);
|
||||
public static DateTime DefaultDateTime = default(DateTime);
|
||||
protected readonly Func<MethodBase> Property = MethodBase.GetCurrentMethod;
|
||||
protected readonly Func<MethodBase> ContentTypeAlias = MethodBase.GetCurrentMethod;
|
||||
|
||||
public T Resolve<T>(MethodBase methodBase)
|
||||
#region Properties
|
||||
|
||||
protected T Resolve<T>(MethodBase methodBase)
|
||||
{
|
||||
var propertyTypeAlias = methodBase.ToUmbracoAlias();
|
||||
return Resolve<T>(propertyTypeAlias);
|
||||
}
|
||||
|
||||
protected T Resolve<T>(string propertyTypeAlias)
|
||||
{
|
||||
return _publishedContent.GetPropertyValue<T>(propertyTypeAlias);
|
||||
}
|
||||
|
||||
public T Resolve<T>(MethodBase methodBase, T ifCannotConvert)
|
||||
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 _publishedContent.GetPropertyValue<T>(propertyTypeAlias, false, ifCannotConvert);
|
||||
}
|
||||
|
||||
public T Resolve<T>(MethodBase methodBase, bool recursive, T 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 _publishedContent.GetPropertyValue<T>(propertyTypeAlias, recursive, ifCannotConvert);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public string ResolveString(MethodBase methodBase)
|
||||
#region Querying
|
||||
protected T Parent<T>() where T : TypedModelBase
|
||||
{
|
||||
return Resolve<string>(methodBase);
|
||||
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
|
||||
if (constructorInfo == null)
|
||||
throw new Exception("No valid constructor found");
|
||||
|
||||
return (T) constructorInfo.Invoke(new object[] {_publishedContent.Parent});
|
||||
}
|
||||
|
||||
public int ResolveInt(MethodBase methodBase)
|
||||
protected IEnumerable<T> Children<T>(MethodBase methodBase) where T : TypedModelBase
|
||||
{
|
||||
return Resolve<int>(methodBase);
|
||||
var docTypeAlias = methodBase.CleanCallingMethodName();
|
||||
return Children<T>(docTypeAlias);
|
||||
}
|
||||
|
||||
public bool ResolveBool(MethodBase methodBase)
|
||||
protected IEnumerable<T> Children<T>(string docTypeAlias) where T : TypedModelBase
|
||||
{
|
||||
return Resolve<bool>(methodBase);
|
||||
var constructorInfo = typeof(T).GetConstructor(new[] { typeof(IPublishedContent) });
|
||||
if(constructorInfo == null)
|
||||
throw new Exception("No valid constructor found");
|
||||
|
||||
string singularizedDocTypeAlias = docTypeAlias.ToSingular();
|
||||
|
||||
return _publishedContent.Children.Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
|
||||
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
|
||||
}
|
||||
|
||||
public DateTime ResolveDate(MethodBase methodBase)
|
||||
protected IEnumerable<T> Ancestors<T>(MethodBase methodBase) where T : TypedModelBase
|
||||
{
|
||||
return Resolve<DateTime>(methodBase);
|
||||
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 _publishedContent.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 _publishedContent.Descendants().Where(x => x.DocumentTypeAlias == singularizedDocTypeAlias)
|
||||
.Select(x => (T)constructorInfo.Invoke(new object[] { x }));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IPublishedContent
|
||||
int IPublishedContent.Id { get { return _publishedContent.Id; } }
|
||||
int IPublishedContent.TemplateId { get { return _publishedContent.TemplateId; } }
|
||||
int IPublishedContent.SortOrder { get { return _publishedContent.SortOrder; } }
|
||||
string IPublishedContent.Name { get { return _publishedContent.Name; } }
|
||||
string IPublishedContent.UrlName { get { return _publishedContent.UrlName; } }
|
||||
string IPublishedContent.DocumentTypeAlias { get { return _publishedContent.DocumentTypeAlias; } }
|
||||
int IPublishedContent.DocumentTypeId { get { return _publishedContent.DocumentTypeId; } }
|
||||
string IPublishedContent.WriterName { get { return _publishedContent.WriterName; } }
|
||||
string IPublishedContent.CreatorName { get { return _publishedContent.CreatorName; } }
|
||||
int IPublishedContent.WriterId { get { return _publishedContent.WriterId; } }
|
||||
int IPublishedContent.CreatorId { get { return _publishedContent.CreatorId; } }
|
||||
string IPublishedContent.Path { get { return _publishedContent.Path; } }
|
||||
DateTime IPublishedContent.CreateDate { get { return _publishedContent.CreateDate; } }
|
||||
DateTime IPublishedContent.UpdateDate { get { return _publishedContent.UpdateDate; } }
|
||||
Guid IPublishedContent.Version { get { return _publishedContent.Version; } }
|
||||
int IPublishedContent.Level { get { return _publishedContent.Level; } }
|
||||
string IPublishedContent.Url { get { return _publishedContent.Url; } }
|
||||
PublishedItemType IPublishedContent.ItemType { get { return _publishedContent.ItemType; } }
|
||||
IPublishedContent IPublishedContent.Parent { get { return _publishedContent.Parent; } }
|
||||
IEnumerable<IPublishedContent> IPublishedContent.Children { get { return _publishedContent.Children; } }
|
||||
|
||||
ICollection<IPublishedContentProperty> IPublishedContent.Properties { get { return _publishedContent.Properties; } }
|
||||
|
||||
object IPublishedContent.this[string propertyAlias] { get { return _publishedContent[propertyAlias]; } }
|
||||
|
||||
IPublishedContentProperty IPublishedContent.GetProperty(string alias)
|
||||
{
|
||||
return _publishedContent.GetProperty(alias);
|
||||
}
|
||||
#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.Name.Replace("get_", "").ToUmbracoAlias();
|
||||
return methodBase.CleanCallingMethodName().ToUmbracoAlias();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,12 @@ using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent.StronglyTypedModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a basic extension of the UmbracoTemplatePage, which allows you to specify
|
||||
/// the type of a strongly typed model that inherits from TypedModelBase.
|
||||
/// The model is exposed as TypedModel.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the model to create/expose</typeparam>
|
||||
public abstract class UmbracoTemplatePage<T> : UmbracoTemplatePage where T : TypedModelBase
|
||||
{
|
||||
protected override void InitializePage()
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.Entity.Design" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.0\lib\System.Data.SqlServerCe.dll</HintPath>
|
||||
@@ -194,7 +195,8 @@
|
||||
<Compile Include="Persistence\Repositories\UserRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\UserTypeRepositoryTest.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\CallingMethodTests.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\TextpageModel.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\Subpage.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\Textpage.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\TypedModelBase.cs" />
|
||||
<Compile Include="PublishedContent\StronglyTypedModels\UmbracoTemplatePage`T.cs" />
|
||||
<Compile Include="Services\LocalizationServiceTests.cs" />
|
||||
|
||||
Reference in New Issue
Block a user