1 #region MIT license 2 // 3 // MIT license 4 // 5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 // 25 #endregion 26 27 using System; 28 using System.Collections.Generic; 29 using System.Linq.Expressions; 30 using System.Reflection; 31 using DbLinq.Util; 32 33 #if MONO_STRICT 34 namespace System.Data.Linq 35 #else 36 namespace DbLinq.Data.Linq 37 #endif 38 { 39 /// <summary> 40 /// Allows to specify 41 /// </summary> 42 public sealed class DataLoadOptions 43 { 44 /// <summary> 45 /// Criteria to restrict associations 46 /// </summary> 47 private readonly Dictionary<MemberInfo, bool> eagerLoading = new Dictionary<MemberInfo, bool>(); 48 private readonly Dictionary<MemberInfo, LambdaExpression> criteria = new Dictionary<MemberInfo, LambdaExpression>(); 49 50 /// <summary> 51 /// Filters objects retrieved for a particular relationship. 52 /// </summary> 53 /// <typeparam name="T"></typeparam> 54 /// <param name="expression"></param> AssociateWith(Expression<Func<T, object>> expression)55 public void AssociateWith<T>(Expression<Func<T, object>> expression) 56 { 57 AssociateWith((LambdaExpression)expression); 58 } 59 60 /// <summary> 61 /// Filters the objects retrieved for a particular relationship. 62 /// </summary> 63 /// <param name="expression"></param> AssociateWith(LambdaExpression expression)64 public void AssociateWith(LambdaExpression expression) 65 { 66 // TODO: ensure we have an EntitySet<> 67 var memberInfo = ReflectionUtility.GetMemberCallInfo(expression); 68 if (memberInfo == null) 69 throw new InvalidOperationException("The argument expression must be a property access or a field access where the target object is the parameter"); 70 if (!criteria.ContainsKey(memberInfo)) 71 { 72 VerifyMemberAccessCycles(memberInfo); 73 criteria.Add(memberInfo, expression); 74 } 75 } 76 77 /// <summary> 78 /// Gets the restrictive criteria related to an association 79 /// </summary> 80 /// <param name="memberInfo"></param> 81 /// <returns></returns> GetAssociationCriteria(MemberInfo memberInfo, out LambdaExpression associationCriteria)82 public bool GetAssociationCriteria(MemberInfo memberInfo, out LambdaExpression associationCriteria) 83 { 84 return criteria.TryGetValue(memberInfo, out associationCriteria); 85 } 86 87 /// <summary> 88 /// Specifies which sub-objects to retrieve when a query is submitted for an object of type T. 89 /// </summary> 90 /// <typeparam name="T"></typeparam> 91 /// <param name="expression"></param> LoadWith(Expression<Func<T, Object>> expression)92 public void LoadWith<T>(Expression<Func<T, Object>> expression) 93 { 94 LoadWith((LambdaExpression)expression); 95 } 96 97 /// <summary> 98 /// Retrieves specified data related to the main target by using a lambda expression. 99 /// </summary> 100 /// <param name="expression"></param> LoadWith(LambdaExpression expression)101 public void LoadWith(LambdaExpression expression) 102 { 103 // TODO: ensure we have an EntitySet<> 104 var memberInfo = ReflectionUtility.GetMemberInfo(expression); 105 if (memberInfo == null) 106 throw new InvalidOperationException("The argument expression must be a property access or a field access where the target object is the parameter"); 107 if (!eagerLoading.ContainsKey(memberInfo)) 108 { 109 VerifyMemberAccessCycles(memberInfo); 110 eagerLoading.Add(memberInfo, true); 111 } 112 } 113 VerifyMemberAccessCycles(MemberInfo member)114 private void VerifyMemberAccessCycles(MemberInfo member) 115 { 116 var mt = GetMemberEntityType (member); 117 var d = member.DeclaringType; 118 foreach (KeyValuePair<MemberInfo, bool> m in eagerLoading) 119 { 120 if (m.Key.DeclaringType == mt && GetMemberEntityType (m.Key) == d) 121 throw new InvalidOperationException("Illegal cycles are detected in the argument expression among other eager-loading expressions"); 122 } 123 } 124 GetMemberEntityType(MemberInfo member)125 private Type GetMemberEntityType(MemberInfo member) 126 { 127 var mt = member.GetMemberType(); 128 if (mt.IsGenericType) 129 { 130 if (mt.GetGenericTypeDefinition() == typeof(System.Data.Linq.EntitySet<>)) 131 mt = mt.GetGenericArguments()[0]; 132 else if (mt.GetGenericTypeDefinition() == typeof(System.Data.Linq.EntityRef<>)) 133 mt = mt.GetGenericArguments()[0]; 134 } 135 return mt; 136 } 137 138 /// <summary> 139 /// Tells if we do eager or lazy loading 140 /// </summary> 141 /// <param name="memberInfo"></param> 142 /// <returns>True on eager (immediate) logging</returns> IsImmediate(MemberInfo memberInfo)143 public bool IsImmediate(MemberInfo memberInfo) 144 { 145 return eagerLoading.ContainsKey(memberInfo); 146 } 147 } 148 } 149