Welcome to mirror list, hosted at ThFree Co, Russian Federation.

EntityWrapperFactory.cs « Internal « Objects « Data « System « System.Data.Entity « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a337615dea5057f3ce09d57e07e64d9cf84174b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
//---------------------------------------------------------------------
// <copyright file="EntityWrapperFactory.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;
using System.Reflection;
using System.Linq.Expressions;
using System.Security.Permissions;
using System.Threading;
using System.Data.Common.Utils;
using System.Runtime.CompilerServices;

namespace System.Data.Objects.Internal
{
    /// <summary>
    /// Factory class for creating IEntityWrapper instances.
    /// </summary>
    internal static class EntityWrapperFactory
    {
        // A cache of functions used to create IEntityWrapper instances for a given type
        private static readonly Memoizer<Type, Func<object, IEntityWrapper>> _delegateCache = new Memoizer<Type, Func<object, IEntityWrapper>>(CreateWrapperDelegate, null);

        /// <summary>
        /// The single instance of the NullEntityWrapper.
        /// </summary>
        internal static IEntityWrapper NullWrapper
        {
            get { return NullEntityWrapper.NullWrapper; }
        }

        /// <summary>
        /// Called to create a new wrapper outside of the normal materialization process.
        /// This method is typically used when a new entity is created outside the context and then is
        /// added or attached.  The materializer bypasses this method and calls wrapper constructors
        /// directory for performance reasons.
        /// This method does not check whether or not the wrapper already exists in the context.
        /// </summary>
        /// <param name="entity">The entity for which a wrapper will be created</param>
        /// <param name="key">The key associated with that entity, or null</param>
        /// <returns>The new wrapper instance</returns>
        internal static IEntityWrapper CreateNewWrapper(object entity, EntityKey key)
        {
            Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
            if (entity == null)
            {
                return NullEntityWrapper.NullWrapper;
            }
            // We used a cache of functions based on the actual type of entity that we need to wrap.
            // Creatung these functions is slow, but once they are created they are relatively fast.
            IEntityWrapper wrappedEntity = _delegateCache.Evaluate(entity.GetType())(entity);
            wrappedEntity.RelationshipManager.SetWrappedOwner(wrappedEntity, entity);
            // We cast to object here to avoid calling the overridden != operator on EntityKey.
            // This creates a very small perf gain, which is none-the-less significant for lean no-tracking cases.
            if ((object)key != null && (object)wrappedEntity.EntityKey == null)
            {
                wrappedEntity.EntityKey = key;
            }

            // If the entity is a proxy, set the wrapper to match
            EntityProxyTypeInfo proxyTypeInfo;
            if (EntityProxyFactory.TryGetProxyType(entity.GetType(), out proxyTypeInfo))
            {
                proxyTypeInfo.SetEntityWrapper(wrappedEntity);
            }

            return wrappedEntity;
        }

        // Creates a delegate that can then be used to create wrappers for a given type.
        // This is slow which is why we only create the delegate once and then cache it.
        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
        private static Func<object, IEntityWrapper> CreateWrapperDelegate(Type entityType)
        {
            // For entities that implement all our interfaces we create a special lightweight wrapper that is both
            // smaller and faster than the strategy-based wrapper.
            // Otherwise, the wrapper is provided with different delegates depending on which interfaces are implemented.
            bool isIEntityWithRelationships = typeof(IEntityWithRelationships).IsAssignableFrom(entityType);
            bool isIEntityWithChangeTracker = typeof(IEntityWithChangeTracker).IsAssignableFrom(entityType);
            bool isIEntityWithKey = typeof(IEntityWithKey).IsAssignableFrom(entityType);
            bool isProxy = EntityProxyFactory.IsProxyType(entityType);
            MethodInfo createDelegate;
            if (isIEntityWithRelationships && isIEntityWithChangeTracker && isIEntityWithKey && !isProxy)
            {
                createDelegate = typeof(EntityWrapperFactory).GetMethod("CreateWrapperDelegateTypedLightweight", BindingFlags.NonPublic | BindingFlags.Static);
            }
            else if (isIEntityWithRelationships)
            {
                // This type of strategy wrapper is used when the entity implements IEntityWithRelationships
                // In this case it is important that the entity itself is used to create the RelationshipManager
                createDelegate = typeof(EntityWrapperFactory).GetMethod("CreateWrapperDelegateTypedWithRelationships", BindingFlags.NonPublic | BindingFlags.Static);
            }
            else
            {
                createDelegate = typeof(EntityWrapperFactory).GetMethod("CreateWrapperDelegateTypedWithoutRelationships", BindingFlags.NonPublic | BindingFlags.Static);
            }
            createDelegate = createDelegate.MakeGenericMethod(entityType);
            return (Func<object, IEntityWrapper>)createDelegate.Invoke(null, new object[0]);
        }

        // Returns a delegate that creates the fast LightweightEntityWrapper
        private static Func<object, IEntityWrapper> CreateWrapperDelegateTypedLightweight<TEntity>()
             where TEntity : IEntityWithRelationships, IEntityWithKey, IEntityWithChangeTracker
        {
            return (entity) => new LightweightEntityWrapper<TEntity>((TEntity)entity);
        }

        // Returns a delegate that creates a strategy-based wrapper for entities that implement IEntityWithRelationships
        private static Func<object, IEntityWrapper> CreateWrapperDelegateTypedWithRelationships<TEntity>()
            where TEntity : IEntityWithRelationships
        {
            Func<object, IPropertyAccessorStrategy> propertyAccessorStrategy;
            Func<object, IEntityKeyStrategy> keyStrategy;
            Func<object, IChangeTrackingStrategy> changeTrackingStrategy;
            CreateStrategies<TEntity>(out propertyAccessorStrategy, out changeTrackingStrategy, out keyStrategy);
            
            return (entity) => new EntityWrapperWithRelationships<TEntity>((TEntity)entity, propertyAccessorStrategy, changeTrackingStrategy, keyStrategy);
        }

        // Returns a delegate that creates a strategy-based wrapper for entities that do not implement IEntityWithRelationships
        private static Func<object, IEntityWrapper> CreateWrapperDelegateTypedWithoutRelationships<TEntity>()
        {
            Func<object, IPropertyAccessorStrategy> propertyAccessorStrategy;
            Func<object, IEntityKeyStrategy> keyStrategy;
            Func<object, IChangeTrackingStrategy> changeTrackingStrategy;
            CreateStrategies<TEntity>(out propertyAccessorStrategy, out changeTrackingStrategy, out keyStrategy);

            return (entity) => new EntityWrapperWithoutRelationships<TEntity>((TEntity)entity, propertyAccessorStrategy, changeTrackingStrategy, keyStrategy);
        }

        // Creates delegates that create strategy objects appropriate for the type of entity.
        private static void CreateStrategies<TEntity>(out Func<object, IPropertyAccessorStrategy> createPropertyAccessorStrategy,
                                                      out Func<object, IChangeTrackingStrategy> createChangeTrackingStrategy,
                                                      out Func<object, IEntityKeyStrategy> createKeyStrategy)
        {
            Type entityType = typeof(TEntity);
            bool isIEntityWithRelationships = typeof(IEntityWithRelationships).IsAssignableFrom(entityType);
            bool isIEntityWithChangeTracker = typeof(IEntityWithChangeTracker).IsAssignableFrom(entityType);
            bool isIEntityWithKey = typeof(IEntityWithKey).IsAssignableFrom(entityType);
            bool isProxy = EntityProxyFactory.IsProxyType(entityType);

            if (!isIEntityWithRelationships || isProxy)
            {
                createPropertyAccessorStrategy = GetPocoPropertyAccessorStrategyFunc();
            }
            else
            {
                createPropertyAccessorStrategy = GetNullPropertyAccessorStrategyFunc();
            }

            if (isIEntityWithChangeTracker)
            {
                createChangeTrackingStrategy = GetEntityWithChangeTrackerStrategyFunc();
            }
            else
            {
                createChangeTrackingStrategy = GetSnapshotChangeTrackingStrategyFunc();
            }

            if (isIEntityWithKey)
            {
                createKeyStrategy = GetEntityWithKeyStrategyStrategyFunc();
            }
            else
            {
                createKeyStrategy = GetPocoEntityKeyStrategyFunc();
            }
        }

        /// <summary>
        /// Convenience function that gets the ObjectStateManager from the context and calls
        /// WrapEntityUsingStateManager.
        /// </summary>
        /// <param name="entity">the entity to wrap</param>
        /// <param name="context">the context in which the entity may exist, or null</param>
        /// <returns>a new or existing wrapper</returns>
        internal static IEntityWrapper WrapEntityUsingContext(object entity, ObjectContext context)
        {
            EntityEntry existingEntry;
            return WrapEntityUsingStateManagerGettingEntry(entity, context == null ? null : context.ObjectStateManager, out existingEntry);
        }

        /// <summary>
        /// Convenience function that gets the ObjectStateManager from the context and calls
        /// WrapEntityUsingStateManager.
        /// </summary>
        /// <param name="entity">The entity to wrap</param>
        /// <param name="context">The context in which the entity may exist, or null</param>
        /// <param name="existingEntry">Set to the existing state entry if one is found, else null</param>
        /// <returns>a new or existing wrapper</returns>
        internal static IEntityWrapper WrapEntityUsingContextGettingEntry(object entity, ObjectContext context, out EntityEntry existingEntry)
        {
            return WrapEntityUsingStateManagerGettingEntry(entity, context == null ? null : context.ObjectStateManager, out existingEntry);
        }

        /// <summary>
        /// Wraps an entity and returns a new wrapper, or returns an existing wrapper if one
        /// already exists in the ObjectStateManager or in a RelationshipManager associated with
        /// the entity.
        /// </summary>
        /// <param name="entity">the entity to wrap</param>
        /// <param name="context">the state manager  in which the entity may exist, or null</param>
        /// <returns>a new or existing wrapper</returns>
        internal static IEntityWrapper WrapEntityUsingStateManager(object entity, ObjectStateManager stateManager)
        {
            EntityEntry existingEntry;
            return WrapEntityUsingStateManagerGettingEntry(entity, stateManager, out existingEntry);
        }

        /// <summary>
        /// Wraps an entity and returns a new wrapper, or returns an existing wrapper if one
        /// already exists in the ObjectStateManager or in a RelationshipManager associated with
        /// the entity.
        /// </summary>
        /// <param name="entity">The entity to wrap</param>
        /// <param name="context">The state manager  in which the entity may exist, or null</param>
        /// <param name="existingEntry">The existing state entry for the given entity if one exists, otherwise null</param>
        /// <returns>A new or existing wrapper</returns>
        internal static IEntityWrapper WrapEntityUsingStateManagerGettingEntry(object entity, ObjectStateManager stateManager, out EntityEntry existingEntry)
        {
            Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
            IEntityWrapper wrapper = null;
            existingEntry = null;

            if (entity == null)
            {
                return NullEntityWrapper.NullWrapper;
            }
            // First attempt to find an existing wrapper in the ObjectStateMager.
            if (stateManager != null)
            {
                existingEntry = stateManager.FindEntityEntry(entity);
                if (existingEntry != null)
                {
                    return existingEntry.WrappedEntity;
                }
                if (stateManager.TransactionManager.TrackProcessedEntities)
                {
                    if (stateManager.TransactionManager.WrappedEntities.TryGetValue(entity, out wrapper))
                    {
                        return wrapper;
                    }
                }
            }
            // If no entity was found in the OSM, then check if one exists on an associated
            // RelationshipManager.  This only works where the entity implements IEntityWithRelationshops.
            IEntityWithRelationships entityWithRelationships = entity as IEntityWithRelationships;
            if (entityWithRelationships != null)
            {
                RelationshipManager relManager = entityWithRelationships.RelationshipManager;
                if (relManager == null)
                {
                    throw EntityUtil.UnexpectedNullRelationshipManager();
                }
                IEntityWrapper wrappedEntity = relManager.WrappedOwner;
                if (!Object.ReferenceEquals(wrappedEntity.Entity, entity))
                {
                    // This means that the owner of the RelationshipManager must have been set
                    // incorrectly in the call to RelationshipManager.Create().
                    throw EntityUtil.InvalidRelationshipManagerOwner();
                }
                return wrappedEntity;
            }
            else
            {
                // Finally look to see if the instance is a proxy and get the wrapper from the proxy
                EntityProxyFactory.TryGetProxyWrapper(entity, out wrapper);
            }

            // If we could not find an existing wrapper, then go create a new one
            if (wrapper == null)
            {
                IEntityWithKey withKey = entity as IEntityWithKey;
                wrapper = CreateNewWrapper(entity, withKey == null ? null : withKey.EntityKey);
            }
            if (stateManager != null && stateManager.TransactionManager.TrackProcessedEntities)
            {
                stateManager.TransactionManager.WrappedEntities.Add(entity, wrapper);
            }
            return wrapper;
        }

        /// <summary>
        /// When an entity enters Object Services that was retreived with NoTracking, it may not have certain fields set that are in many cases
        /// assumed to be present. This method updates the wrapper with a key and a context.
        /// </summary>
        /// <param name="wrapper">The wrapped entity</param>
        /// <param name="context">The context that will be using this wrapper</param>
        /// <param name="entitySet">The entity set this wrapped entity belongs to</param>
        internal static void UpdateNoTrackingWrapper(IEntityWrapper wrapper, ObjectContext context, EntitySet entitySet)
        {
            if (wrapper.EntityKey == null)
            {
                wrapper.EntityKey = context.ObjectStateManager.CreateEntityKey(entitySet, wrapper.Entity);
            }
            if (wrapper.Context == null)
            {
                wrapper.AttachContext(context, entitySet, MergeOption.NoTracking);
            }
        }

        /// <summary>
        /// Returns a func that will create a PocoPropertyAccessorStrategy object for a given entity.
        /// </summary>
        /// <returns>The func to be used to create the strategy object.</returns>
        internal static Func<object, IPropertyAccessorStrategy> GetPocoPropertyAccessorStrategyFunc()
        {
            return (object entity) => new PocoPropertyAccessorStrategy(entity);
        }

        /// <summary>
        /// Returns a func that will create a null IPropertyAccessorStrategy strategy object for a given entity.
        /// </summary>
        /// <returns>The func to be used to create the strategy object.</returns>
        internal static Func<object, IPropertyAccessorStrategy> GetNullPropertyAccessorStrategyFunc()
        {
            return (object entity) => null;
        }

        /// <summary>
        /// Returns a func that will create a EntityWithChangeTrackerStrategy object for a given entity.
        /// </summary>
        /// <returns>The func to be used to create the strategy object.</returns>
        internal static Func<object, IChangeTrackingStrategy> GetEntityWithChangeTrackerStrategyFunc()
        {
            return (object entity) => new EntityWithChangeTrackerStrategy((IEntityWithChangeTracker)entity);
        }

        /// <summary>
        /// Returns a func that will create a SnapshotChangeTrackingStrategy object for a given entity.
        /// </summary>
        /// <returns>The func to be used to create the strategy object.</returns>
        internal static Func<object, IChangeTrackingStrategy> GetSnapshotChangeTrackingStrategyFunc()
        {
            return (object entity) => SnapshotChangeTrackingStrategy.Instance;
        }

        /// <summary>
        /// Returns a func that will create a EntityWithKeyStrategy object for a given entity.
        /// </summary>
        /// <returns>The func to be used to create the strategy object.</returns>
        internal static Func<object, IEntityKeyStrategy> GetEntityWithKeyStrategyStrategyFunc()
        {
            return (object entity) => new EntityWithKeyStrategy((IEntityWithKey)entity);
        }

        /// <summary>
        /// Returns a func that will create a GetPocoEntityKeyStrategyFunc object for a given entity.
        /// </summary>
        /// <returns>The func to be used to create the strategy object.</returns>
        internal static Func<object, IEntityKeyStrategy> GetPocoEntityKeyStrategyFunc()
        {
            return (object entity) => new PocoEntityKeyStrategy();
        }
    }
}