The following code is intended to implement a LinFu DynamicProxy interceptor to lazy load given virtual properties from an Umbraco datastore.
My concerns:
- Thread safety, Have I covered all the bases, Am I storing variables correctly?
- Duplication - Getting the child type, am I safe to store a variable via my Lazy to use later?
- Efficiency, If I have everything else correct, am I being wasteful?
The code:
/// <summary>
/// The content interceptor for intercepting virtual content properties.
/// </summary>
internal class ContentInterceptor : IInvokeWrapper
{
/// <summary>
/// The reader writer lock slim.
/// </summary>
private static readonly ReaderWriterLockSlim ReaderWriterLockSlim
= new ReaderWriterLockSlim();
/// <summary>
/// The lazy content.
/// </summary>
private static readonly Lazy<IContent> LazyParent = new Lazy<IContent>(
() =>
{
IContent content = contentService.GetById(id);
IContent parent = content.Parent();
return parent;
});
/// <summary>
/// The lazy list content.
/// </summary>
private static readonly Lazy<IEnumerable<IContent>> LazyChildren =
new Lazy<IEnumerable<IContent>>(
() =>
{
IContent content = contentService.GetById(id);
Type childType = returnType.GetGenericArguments().Single();
AliasAttribute aliasAttribute = childType.FirstAttribute<AliasAttribute>();
string alias = Conventions
.GetPropertyTypeAlias(aliasAttribute, childType.Name);
IEnumerable<IContent> umbracoChildren = contentService
.GetChildrenByName(content.Id, alias);
return umbracoChildren;
});
/// <summary>
/// The id.
/// </summary>
private static int id;
/// <summary>
/// The return type.
/// </summary>
private static Type returnType;
/// <summary>
/// The content service.
/// </summary>
private static IContentService contentService;
/// <summary>
/// The target to intercept.
/// </summary>
private readonly object target;
/// <summary>
/// Initializes a new instance of the <see cref="ContentInterceptor"/> class.
/// </summary>
/// <param name="target">
/// The target to intercept.
/// </param>
/// <param name="id">
/// The id.
/// </param>
public ContentInterceptor(object target, int id)
{
ContentInterceptor.id = id;
this.target = target;
contentService = GlobalServices.Current.ContentService;
}
/// <summary>
/// The event that runs after invocation.
/// </summary>
/// <param name="info">
/// The information.
/// </param>
/// <param name="returnValue">
/// The return Value.
/// </param>
public void AfterInvoke(InvocationInfo info, object returnValue)
{
Debug.WriteLine("AfterInvoke() called");
}
/// <summary>
/// The event that runs before invocation.
/// </summary>
/// <param name="info">The information.</param>
public void BeforeInvoke(InvocationInfo info)
{
Debug.WriteLine("BeforeInvoke() called");
}
/// <summary>
/// Performs the proxy invocation.
/// </summary>
/// <param name="info">The <see cref="InvocationInfo"/>.</param>
/// <returns>The object to replace the original value.</returns>
public object DoInvoke(InvocationInfo info)
{
lock (ReaderWriterLockSlim)
{
returnType = info.TargetMethod.ReturnType;
// Skip by any ignored properties.
IgnoreAttribute ignoreAttribute = returnType
.FirstAttribute<IgnoreAttribute>();
if (ignoreAttribute != null)
{
// Call the original implementation.
return info.TargetMethod.Invoke(this.target, info.Arguments);
}
// Check whether we are looking for children or not.
if (returnType.IsCollectionType())
{
IEnumerable<IContent> children = LazyChildren.Value;
if (children != null)
{
// TODO: Would rather not be getting this twice.
Type childType = returnType.GetGenericArguments().Single();
MethodInfo getModelList = this.GetType()
.GetMethod("GetModelList")
.MakeGenericMethod(childType);
return getModelList.Invoke(this, new object[] { children });
}
}
else
{
IContent parent = LazyParent.Value;
MethodInfo getModel = this.GetType()
.GetMethod("GetModel")
.MakeGenericMethod(returnType);
return getModel.Invoke(this, new object[] { parent });
}
return null;
}
}
/// <summary>
/// Gets a lazily invoked converted list of type from the content service.
/// </summary>
/// <param name="content">
/// The list of children to convert from.
/// </param>
/// <typeparam name="T">
/// The type to convert to.
/// </typeparam>
/// <returns>
/// The <see cref="T:System.Lazy{List{T}}"/> representing the content.
/// </returns>
private T GetModel<T>(IContent content) where T : class
{
return content.ToType<T>();
}
/// <summary>
/// Gets a list of the given type from an <see cref="IEnumerable{IContent}"/>.
/// </summary>
/// <param name="collection">
/// The list of children to convert from.
/// </param>
/// <typeparam name="T">
/// The type to convert to.
/// </typeparam>
/// <returns>
/// The <see cref="T:System.Lazy{List{T}}"/> representing the content.
/// </returns>
// ReSharper disable once UnusedMember.Local
private List<T> GetModelList<T>(IEnumerable<IContent> collection) where T : class
{
List<T> typedCollection = new List<T>();
typedCollection.AddRange(collection.Select(this.GetModel<T>));
return typedCollection;
}
}