Now I know there are other solutions to this scenario already, notably http://code.msdn.microsoft.com/EFLazyLoading
However I wanted to work with the standard generated code, as it is an assumption on my part that this code will be better tested.
My Solution
Use PostSharp to weave aspects into the code to perform the appropriate IsLoaded and Load calls on the RelatedEnd of the accessed association.
Warning:
As with any other lazy load solution, just because you can doesn't mean that you should. Lazy loading can kill performance.
Please treat the ability to lazy load as a convenience and optimize your returned spans appropriately
Implementation
1) Install PostSharp V1.0RTM http://www.postsharp.org/ (Please consider donating 50 euros to them if you end up using their tool on an ongoing basis)
2) Add the following references to your project
PostSharp.Public;
PostSharp.Laos;
3) Create a new .cs file containing the following code (Refactor to mutiple files if you want)
using System;
using PostSharp.Extensibility;
using PostSharp.Laos;
using System.Reflection;
using System.Data.Objects.DataClasses;
using System.Collections;
namespace YourNamespace
{
[Serializable]
public abstract class LazyLoadBase : OnMethodInvocationAspect
{
//Load the related end if we can
protected void Load(RelatedEnd end)
{
if (end != null && !end.IsLoaded)
{
end.Load();
}
}
}
[Serializable]
public class LazyLoadSetAttribute : LazyLoadBase
{
//Only add this aspect to properties that return an enumerable, RelatedEnd, i.e. EntityCollection
public override bool CompileTimeValidate(MethodBase method)
{
MethodInfo info = method as MethodInfo;
if (info == null) return false;
bool isRelatedEnd = typeof(RelatedEnd).IsAssignableFrom(info.ReturnType);
bool isSet = typeof(IEnumerable).IsAssignableFrom(info.ReturnType);
return isSet && isRelatedEnd;
}
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
eventArgs.Proceed();
RelatedEnd end = eventArgs.ReturnValue as RelatedEnd;
Load(end);
}
}
[Serializable]
public class LazyLoadRefAttribute : LazyLoadBase
{
//Only add this aspect to properties that return an EntityObject
public override bool CompileTimeValidate(System.Reflection.MethodBase method)
{
MethodInfo info = method as MethodInfo;
return typeof(EntityObject).IsAssignableFrom(info.ReturnType);
}
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
RelatedEnd end = GetRelatedEnd(eventArgs.Delegate);
Load(end);
eventArgs.Proceed();
}
//Infer the name of the associated property that returns the EntityReference for the EntityObject, and force a load
private System.Data.Objects.DataClasses.RelatedEnd GetRelatedEnd(Delegate currentMethod)
{
string entityRefProperty = string.Format("{0}Reference", currentMethod.Method.Name);
if (entityRefProperty.StartsWith("~get_")) entityRefProperty = entityRefProperty.Substring(5);
PropertyInfo prop = currentMethod.Method.ReflectedType.GetProperty(entityRefProperty);
if (prop == null) return null;
return prop.GetValue(currentMethod.Target, null) as System.Data.Objects.DataClasses.RelatedEnd;
}
}
}
3) Add the following lines to your AssemblyInfo.cs
using YourNamespace;
[assembly: LazyLoadRefAttribute(AttributeTargetAssemblies = "YOUR_ASSEMBLY_NAME", AttributeTargetTypes = "EF_OBJECTCONTEXT_NAMESPACE.*")]
[assembly: LazyLoadSetAttribute(AttributeTargetAssemblies = "YOUR_ASSEMBLY_NAME", AttributeTargetTypes = "EF_OBJECTCONTEXT_NAMESPACE.*")]
Conclusion
And that's it, PostSharp will weave its magic when you build your project, leaving you free to focus on functionality first, optimization second.