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