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();
}
}
}
|