tag:blogger.com,1999:blog-74458901026337629982024-03-08T11:26:51.263-08:00Pablo BlamirezUK based .Net dudePablo Blamirez AKA PaulBlamirehttp://www.blogger.com/profile/09019386514425522091noreply@blogger.comBlogger1125tag:blogger.com,1999:blog-7445890102633762998.post-27911347531554241442008-10-10T15:35:00.002-07:002015-07-05T12:52:35.990-07:00EntityFramework Transparent Lazy Load<br />Now I know there are other solutions to this scenario already, notably <a href="http://code.msdn.microsoft.com/EFLazyLoading" title="http://code.msdn.microsoft.com/EFLazyLoading">http://code.msdn.microsoft.com/EFLazyLoading</a><br />
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.<br />
<h4>
My Solution</h4>
Use PostSharp to weave aspects into the code to perform the appropriate IsLoaded and Load calls on the RelatedEnd of the accessed association.<br />
Warning:<br />
As with any other lazy load solution, just because you can doesn't mean that you should. Lazy loading can kill performance.<br />Please treat the ability to lazy load as a convenience and optimize your returned spans appropriately<br />
<h4>
Implementation</h4>
1) Install PostSharp V1.0RTM <a href="http://www.postsharp.org/">http://www.postsharp.org/</a> (Please consider donating 50 euros to them if you end up using their tool on an ongoing basis)<br />
2) Add the following references to your project<br />
PostSharp.Public;<br />
PostSharp.Laos;<br />
3) Create a new .cs file containing the following code (Refactor to mutiple files if you want)<br />
using System;<br />using PostSharp.Extensibility;<br />using PostSharp.Laos;<br />using System.Reflection;<br />using System.Data.Objects.DataClasses;<br />using System.Collections;<br />
namespace <strong><em>YourNamespace</em></strong><br />{<br /> [Serializable]<br /> public abstract class LazyLoadBase : OnMethodInvocationAspect<br /> {<br /> //Load the related end if we can<br /> protected void Load(RelatedEnd end)<br /> {<br /> if (end != null && !end.IsLoaded)<br /> {<br /> end.Load();<br /> }<br /> }<br /> }<br />
[Serializable]<br /> public class LazyLoadSetAttribute : LazyLoadBase<br /> {<br /> //Only add this aspect to properties that return an enumerable, RelatedEnd, i.e. EntityCollection<br /> public override bool CompileTimeValidate(MethodBase method)<br /> {<br /> MethodInfo info = method as MethodInfo;<br /> if (info == null) return false;<br /> bool isRelatedEnd = typeof(RelatedEnd).IsAssignableFrom(info.ReturnType);<br /> bool isSet = typeof(IEnumerable).IsAssignableFrom(info.ReturnType);<br /> return isSet && isRelatedEnd;<br /> }<br />
public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br /> {<br /> eventArgs.Proceed();<br /> RelatedEnd end = eventArgs.ReturnValue as RelatedEnd;<br /> Load(end);<br /> }<br /> }<br />
[Serializable]<br /> public class LazyLoadRefAttribute : LazyLoadBase<br /> {<br /> //Only add this aspect to properties that return an EntityObject<br /> public override bool CompileTimeValidate(System.Reflection.MethodBase method)<br /> {<br /> MethodInfo info = method as MethodInfo;<br /> return typeof(EntityObject).IsAssignableFrom(info.ReturnType);<br /> }<br />
public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br /> {<br /> RelatedEnd end = GetRelatedEnd(eventArgs.Delegate);<br /> Load(end);<br /> eventArgs.Proceed();<br /> }<br />
//Infer the name of the associated property that returns the EntityReference for the EntityObject, and force a load<br /> private System.Data.Objects.DataClasses.RelatedEnd GetRelatedEnd(Delegate currentMethod)<br /> { <br /> string entityRefProperty = string.Format("{0}Reference", currentMethod.Method.Name);<br /> if (entityRefProperty.StartsWith("~get_")) entityRefProperty = entityRefProperty.Substring(5);<br /> PropertyInfo prop = currentMethod.Method.ReflectedType.GetProperty(entityRefProperty);<br /> if (prop == null) return null;<br /> return prop.GetValue(currentMethod.Target, null) as System.Data.Objects.DataClasses.RelatedEnd;<br /> }<br /> }<br />
}<br />
3) Add the following lines to your AssemblyInfo.cs<br />
using <strong><em>YourNamespace</em></strong>;<br />[assembly: LazyLoadRefAttribute(AttributeTargetAssemblies = "<em>YOUR_ASSEMBLY_NAME</em>", AttributeTargetTypes = "<em>EF_OBJECTCONTEXT_NAMESPACE</em>.*")]<br />[assembly: LazyLoadSetAttribute(AttributeTargetAssemblies = "<em>YOUR_</em>ASSEMBLY_NAME", AttributeTargetTypes = "<em>EF_OBJECTCONTEXT_NAMESPACE</em>.*")]<br />
<h4>
Conclusion</h4>
And that's it, PostSharp will weave its magic when you build your project, leaving you free to focus on functionality first, optimization second.Pablo Blamirez AKA PaulBlamirehttp://www.blogger.com/profile/09019386514425522091noreply@blogger.com0