Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / ObjectContext.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectContext.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.Objects
11 {
12     using System;
13     using System.Collections;
14     using System.Collections.Generic;
15     using System.ComponentModel;
16     using System.Configuration;
17     using System.Data.Common;
18     using System.Data.Common.CommandTrees;
19     using System.Data.Common.CommandTrees.ExpressionBuilder;
20     using System.Data.Common.Internal.Materialization;
21     using System.Data.Common.Utils;
22     using System.Data.Entity;
23     using System.Data.EntityClient;
24     using System.Data.Metadata.Edm;
25     using System.Data.Objects.DataClasses;
26     using System.Data.Objects.ELinq;
27     using System.Data.Objects.Internal;
28     using System.Data.Query.InternalTrees;
29     using System.Diagnostics;
30     using System.Diagnostics.CodeAnalysis;
31     using System.Globalization;
32     using System.Linq;
33     using System.Linq.Expressions;
34     using System.Runtime.Versioning;
35     using System.Text;
36     using System.Transactions;
37
38     /// <summary>
39     /// Defines options that affect the behavior of the ObjectContext.
40     /// </summary>
41     public sealed class ObjectContextOptions
42     {
43         private bool _lazyLoadingEnabled;
44         private bool _proxyCreationEnabled = true;
45         private bool _useLegacyPreserveChangesBehavior = false;
46         private bool _useConsistentNullReferenceBehavior;
47         private bool _useCSharpNullComparisonBehavior = false;
48
49         internal ObjectContextOptions()
50         {
51         }
52
53         /// <summary>
54         /// Get or set boolean that determines if related ends can be loaded on demand 
55         /// when they are accessed through a navigation property.
56         /// </summary>
57         /// <value>
58         /// True if related ends can be loaded on demand; otherwise false.
59         /// </value>
60         public bool LazyLoadingEnabled
61         {
62             get { return _lazyLoadingEnabled; }
63             set { _lazyLoadingEnabled = value; }
64         }
65
66         /// <summary>
67         /// Get or set boolean that determines whether proxy instances will be create
68         /// for CLR types with a corresponding proxy type.
69         /// </summary>
70         /// <value>
71         /// True if proxy instances should be created; otherwise false to create "normal" instances of the type.
72         /// </value>
73         public bool ProxyCreationEnabled
74         {
75             get { return _proxyCreationEnabled; }
76             set { _proxyCreationEnabled = value; }
77         }
78
79         /// <summary>
80         /// Get or set a boolean that determines whether to use the legacy MergeOption.PreserveChanges behavior
81         /// when querying for entities using MergeOption.PreserveChanges
82         /// </summary>
83         /// <value>
84         /// True if the legacy MergeOption.PreserveChanges behavior should be used; otherwise false.
85         /// </value>
86         public bool UseLegacyPreserveChangesBehavior
87         {
88             get { return _useLegacyPreserveChangesBehavior; }
89             set { _useLegacyPreserveChangesBehavior = value; }
90         }
91
92         /// <summary>
93         /// If this flag is set to false then setting the Value property of the <see cref="EntityReference{T}"/> for an
94         /// FK relationship to null when it is already null will have no effect. When this flag is set to true, then
95         /// setting the value to null will always cause the FK to be nulled and the relationship to be deleted
96         /// even if the value is currently null. The default value is false when using ObjectContext and true
97         /// when using DbContext.
98         /// </summary>
99         public bool UseConsistentNullReferenceBehavior
100         {
101             get { return _useConsistentNullReferenceBehavior; }
102             set { _useConsistentNullReferenceBehavior = value; }
103         }
104
105         /// <summary>
106         /// This flag determines whether C# behavior should be exhibited when comparing null values in LinqToEntities. 
107         /// If this flag is set, then any equality comparison between two operands, both of which are potentially 
108         /// nullable, will be rewritten to show C# null comparison semantics. As an example: 
109         /// (operand1 = operand2) will be rewritten as 
110         /// (((operand1 = operand2) AND NOT (operand1 IS NULL OR operand2 IS NULL)) || (operand1 IS NULL && operand2 IS NULL))
111         /// The default value is false when using <see cref="ObjectContext"/>.
112         /// </summary>
113         public bool UseCSharpNullComparisonBehavior
114         {
115             get { return _useCSharpNullComparisonBehavior; }
116             set { _useCSharpNullComparisonBehavior = value; }
117         }
118     }
119
120     /// <summary>
121     /// ObjectContext is the top-level object that encapsulates a connection between the CLR and the database,
122     /// serving as a gateway for Create, Read, Update, and Delete operations.
123     /// </summary>
124     public class ObjectContext : IDisposable
125     {
126         #region Fields
127         private IEntityAdapter _adapter;
128
129         // Connection may be null if used by ObjectMaterializer for detached ObjectContext,
130         // but those code paths should not touch the connection.
131         //
132         // If the connection is null, this indicates that this object has been disposed.
133         // Disposal for this class doesn't mean complete disposal, 
134         // but rather the disposal of the underlying connection object if the ObjectContext owns the connection,
135         // or the separation of the underlying connection object from the ObjectContext if the ObjectContext does not own the connection.
136         //
137         // Operations that require a connection should throw an ObjectDiposedException if the connection is null.
138         // Other operations that do not need a connection should continue to work after disposal.
139         private EntityConnection _connection;
140
141         private readonly MetadataWorkspace _workspace;
142         private ObjectStateManager _cache;
143         private ClrPerspective _perspective;
144         private readonly bool _createdConnection;
145         private bool _openedConnection;             // whether or not the context opened the connection to do an operation
146         private int _connectionRequestCount;        // the number of active requests for an open connection
147         private int? _queryTimeout;
148         private Transaction _lastTransaction;
149
150         private bool _disallowSettingDefaultContainerName;
151
152         private EventHandler _onSavingChanges;
153
154         private ObjectMaterializedEventHandler _onObjectMaterialized;
155
156         private ObjectQueryProvider _queryProvider;
157
158         private readonly ObjectContextOptions _options = new ObjectContextOptions();
159
160         private readonly string s_UseLegacyPreserveChangesBehavior = "EntityFramework_UseLegacyPreserveChangesBehavior";
161
162         #endregion Fields
163
164         #region Constructors
165         /// <summary>
166         /// Creates an ObjectContext with the given connection and metadata workspace.
167         /// </summary>
168         /// <param name="connection">connection to the store</param>
169         public ObjectContext(EntityConnection connection)
170             : this(EntityUtil.CheckArgumentNull(connection, "connection"), true)
171         {
172         }
173
174         /// <summary>
175         /// Creates an ObjectContext with the given connection string and
176         /// default entity container name.  This constructor
177         /// creates and initializes an EntityConnection so that the context is
178         /// ready to use; no other initialization is necessary.  The given
179         /// connection string must be valid for an EntityConnection; connection
180         /// strings for other connection types are not supported.
181         /// </summary>
182         /// <param name="connectionString">the connection string to use in the underlying EntityConnection to the store</param>
183         /// <exception cref="ArgumentNullException">connectionString is null</exception>
184         /// <exception cref="ArgumentException">if connectionString is invalid</exception>
185         [ResourceExposure(ResourceScope.Machine)] //Exposes the file names as part of ConnectionString which are a Machine resource
186         [ResourceConsumption(ResourceScope.Machine)] //For CreateEntityConnection method. But the paths are not created in this method.
187         public ObjectContext(string connectionString)
188             : this(CreateEntityConnection(connectionString), false)
189         {
190             _createdConnection = true;
191         }
192
193
194         /// <summary>
195         /// Creates an ObjectContext with the given connection string and
196         /// default entity container name.  This protected constructor creates and initializes an EntityConnection so that the context 
197         /// is ready to use; no other initialization is necessary.  The given connection string must be valid for an EntityConnection; 
198         /// connection strings for other connection types are not supported.
199         /// </summary>
200         /// <param name="connectionString">the connection string to use in the underlying EntityConnection to the store</param>
201         /// <param name="defaultContainerName">the name of the default entity container</param>
202         /// <exception cref="ArgumentNullException">connectionString is null</exception>
203         /// <exception cref="ArgumentException">either connectionString or defaultContainerName is invalid</exception>
204         [ResourceExposure(ResourceScope.Machine)] //Exposes the file names as part of ConnectionString which are a Machine resource
205         [ResourceConsumption(ResourceScope.Machine)] //For ObjectContext method. But the paths are not created in this method.
206         protected ObjectContext(string connectionString, string defaultContainerName)
207             : this(connectionString)
208         {
209             DefaultContainerName = defaultContainerName;
210             if (!string.IsNullOrEmpty(defaultContainerName))
211             {
212                 _disallowSettingDefaultContainerName = true;
213             }
214         }
215
216         /// <summary>
217         /// Creates an ObjectContext with the given connection and metadata workspace.
218         /// </summary>
219         /// <param name="connection">connection to the store</param>
220         /// <param name="defaultContainerName">the name of the default entity container</param>
221         protected ObjectContext(EntityConnection connection, string defaultContainerName)
222             : this(connection)
223         {
224             DefaultContainerName = defaultContainerName;
225             if (!string.IsNullOrEmpty(defaultContainerName))
226             {
227                 _disallowSettingDefaultContainerName = true;
228             }
229         }
230
231         private ObjectContext(EntityConnection connection, bool isConnectionConstructor)
232         {
233             Debug.Assert(null != connection, "null connection");
234             _connection = connection;
235
236             _connection.StateChange += ConnectionStateChange;
237
238             // Ensure a valid connection
239             string connectionString = connection.ConnectionString;
240             if (connectionString == null || connectionString.Trim().Length == 0)
241             {
242                 throw EntityUtil.InvalidConnection(isConnectionConstructor, null);
243             }
244
245             try
246             {
247                 _workspace = RetrieveMetadataWorkspaceFromConnection();
248             }
249             catch (InvalidOperationException e)
250             {
251                 // Intercept exceptions retrieving workspace, and wrap exception in appropriate
252                 // message based on which constructor pattern is being used.
253                 throw EntityUtil.InvalidConnection(isConnectionConstructor, e);
254             }
255
256             // Register the O and OC metadata
257             if (null != _workspace)
258             {
259                 // register the O-Loader
260                 if (!_workspace.IsItemCollectionAlreadyRegistered(DataSpace.OSpace))
261                 {
262                     ObjectItemCollection itemCollection = new ObjectItemCollection();
263                     _workspace.RegisterItemCollection(itemCollection);
264                 }
265
266                 // have the OC-Loader registered by asking for it
267                 _workspace.GetItemCollection(DataSpace.OCSpace);
268             }
269
270             // load config file properties
271             string value = ConfigurationManager.AppSettings[s_UseLegacyPreserveChangesBehavior];
272             bool useV35Behavior = false;
273             if (Boolean.TryParse(value, out useV35Behavior))
274             {
275                 ContextOptions.UseLegacyPreserveChangesBehavior = useV35Behavior;
276             }
277         }
278
279         #endregion //Constructors
280
281         #region Properties
282         /// <summary>
283         /// Gets the connection to the store.
284         /// </summary>
285         /// <exception cref="ObjectDisposedException">If the <see cref="ObjectContext"/> instance has been disposed.</exception>
286         public DbConnection Connection
287         {
288             get
289             {
290                 if (_connection == null)
291                 {
292                     throw EntityUtil.ObjectContextDisposed();
293                 }
294
295                 return _connection;
296             }
297         }
298
299         /// <summary>
300         /// Gets or sets the default container name.
301         /// </summary>
302         public string DefaultContainerName
303         {
304             get
305             {
306                 EntityContainer container = Perspective.GetDefaultContainer();
307                 return ((null != container) ? container.Name : String.Empty);
308             }
309             set
310             {
311                 if (!_disallowSettingDefaultContainerName)
312                 {
313                     Perspective.SetDefaultContainer(value);
314                 }
315                 else
316                 {
317                     throw EntityUtil.CannotSetDefaultContainerName();
318                 }
319             }
320         }
321
322         /// <summary>
323         /// Gets the metadata workspace associated with this ObjectContext.
324         /// </summary>
325         [CLSCompliant(false)]
326         public MetadataWorkspace MetadataWorkspace
327         {
328             get
329             {
330                 return _workspace;
331             }
332         }
333
334         /// <summary>
335         /// Gets the ObjectStateManager used by this ObjectContext.
336         /// </summary>
337         public ObjectStateManager ObjectStateManager
338         {
339             get
340             {
341                 if (_cache == null)
342                 {
343                     _cache = new ObjectStateManager(_workspace);
344                 }
345                 return _cache;
346             }
347         }
348
349         /// <summary>
350         /// ClrPerspective based on the MetadataWorkspace.
351         /// </summary>
352         internal ClrPerspective Perspective
353         {
354             get
355             {
356                 if (_perspective == null)
357                 {
358                     _perspective = new ClrPerspective(_workspace);
359                 }
360                 return _perspective;
361             }
362         }
363
364         /// <summary>
365         /// Gets and sets the timeout value used for queries with this ObjectContext.
366         /// A null value indicates that the default value of the underlying provider
367         /// will be used.
368         /// </summary>
369         public int? CommandTimeout
370         {
371             get
372             {
373                 return _queryTimeout;
374             }
375             set
376             {
377                 if (value.HasValue && value < 0)
378                 {
379                     throw EntityUtil.InvalidCommandTimeout("value");
380                 }
381                 _queryTimeout = value;
382             }
383         }
384
385         /// <summary>
386         /// Gets the LINQ query provider associated with this object context.
387         /// </summary>
388         internal protected IQueryProvider QueryProvider
389         {
390             get
391             {
392                 if (null == _queryProvider)
393                 {
394                     _queryProvider = new ObjectQueryProvider(this);
395                 }
396                 return _queryProvider;
397             }
398         }
399
400         /// <summary>
401         /// Whether or not we are in the middle of materialization
402         /// Used to suppress operations such as lazy loading that are not allowed during materialization
403         /// </summary>
404         internal bool InMaterialization { get; set; }
405
406         /// <summary>
407         /// Get <see cref="ObjectContextOptions"/> instance that contains options 
408         /// that affect the behavior of the ObjectContext.
409         /// </summary>
410         /// <value>
411         /// Instance of <see cref="ObjectContextOptions"/> for the current ObjectContext.
412         /// This value will never be null.
413         /// </value>
414         public ObjectContextOptions ContextOptions
415         {
416             get { return _options; }
417         }
418
419         #endregion //Properties
420
421         #region Events
422         /// <summary>
423         /// Property for adding a delegate to the SavingChanges Event.
424         /// </summary>
425         public event EventHandler SavingChanges
426         {
427             add { _onSavingChanges += value; }
428             remove { _onSavingChanges -= value; }
429         }
430         /// <summary>
431         /// A private helper function for the _savingChanges/SavingChanges event.
432         /// </summary>
433         private void OnSavingChanges()
434         {
435             if (null != _onSavingChanges)
436             {
437                 _onSavingChanges(this, new EventArgs());
438             }
439         }
440
441         /// <summary>
442         /// Event raised when a new entity object is materialized.  That is, the event is raised when
443         /// a new entity object is created from data in the store as part of a query or load operation.
444         /// </summary>
445         /// <remarks>
446         /// Note that the event is raised after included (spanned) referenced objects are loaded, but
447         /// before included (spanned) collections are loaded.  Also, for independent associations,
448         /// any stub entities for related objects that have not been loaded will also be created before
449         /// the event is raised.
450         /// 
451         /// It is possible for an entity object to be created and then thrown away if it is determined
452         /// that an entity with the same ID already exists in the Context.  This event is not raised
453         /// in those cases.
454         /// </remarks>
455         public event ObjectMaterializedEventHandler ObjectMaterialized
456         {
457             add { _onObjectMaterialized += value; }
458             remove { _onObjectMaterialized -= value; }
459         }
460
461         internal void OnObjectMaterialized(object entity)
462         {
463             if (null != _onObjectMaterialized)
464             {
465                 _onObjectMaterialized(this, new ObjectMaterializedEventArgs(entity));
466             }
467         }
468
469         /// <summary>
470         /// Returns true if any handlers for the ObjectMaterialized event exist.  This is
471         /// used for perf reasons to avoid collecting the information needed for the event
472         /// if there is no point in firing it.
473         /// </summary>
474         internal bool OnMaterializedHasHandlers
475         {
476             get { return _onObjectMaterialized != null && _onObjectMaterialized.GetInvocationList().Length != 0; }
477         }
478
479         #endregion //Events
480
481         #region Methods
482         /// <summary>
483         /// AcceptChanges on all associated entries in the ObjectStateManager so their resultant state is either unchanged or detached.
484         /// </summary>
485         /// <returns></returns>
486         public void AcceptAllChanges()
487         {
488             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
489
490             if (ObjectStateManager.SomeEntryWithConceptualNullExists())
491             {
492                 throw new InvalidOperationException(Strings.ObjectContext_CommitWithConceptualNull);
493             }
494
495             // There are scenarios in which order of calling AcceptChanges does matter:
496             // in case there is an entity in Deleted state and another entity in Added state with the same ID -
497             // it is necessary to call AcceptChanges on Deleted entity before calling AcceptChanges on Added entity
498             // (doing this in the other order there is conflict of keys).
499             foreach (ObjectStateEntry entry in ObjectStateManager.GetObjectStateEntries(EntityState.Deleted))
500             {
501                 entry.AcceptChanges();
502             }
503
504             foreach (ObjectStateEntry entry in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
505             {
506                 entry.AcceptChanges();
507             }
508
509             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
510         }
511
512         private void VerifyRootForAdd(bool doAttach, string entitySetName, IEntityWrapper wrappedEntity, EntityEntry existingEntry, out EntitySet entitySet, out bool isNoOperation)
513         {
514             isNoOperation = false;
515
516             EntitySet entitySetFromName = null;
517
518             if (doAttach)
519             {
520                 // For AttachTo the entity set name is optional
521                 if (!String.IsNullOrEmpty(entitySetName))
522                 {
523                     entitySetFromName = this.GetEntitySetFromName(entitySetName);
524                 }
525             }
526             else
527             {
528                 // For AddObject the entity set name is obligatory
529                 entitySetFromName = this.GetEntitySetFromName(entitySetName);
530             }
531
532             // Find entity set using entity key
533             EntitySet entitySetFromKey = null;
534
535             EntityKey key = existingEntry != null ? existingEntry.EntityKey : wrappedEntity.GetEntityKeyFromEntity();
536             if (null != (object)key)
537             {
538                 entitySetFromKey = key.GetEntitySet(this.MetadataWorkspace);
539
540                 if (entitySetFromName != null)
541                 {
542                     // both entity sets are not null, compare them
543                     EntityUtil.ValidateEntitySetInKey(key, entitySetFromName, "entitySetName");
544                 }
545                 key.ValidateEntityKey(_workspace, entitySetFromKey);
546             }
547
548             entitySet = entitySetFromKey ?? entitySetFromName;
549
550             // Check if entity set was found
551             if (entitySet == null)
552             {
553                 throw EntityUtil.EntitySetNameOrEntityKeyRequired();
554             }
555
556             this.ValidateEntitySet(entitySet, wrappedEntity.IdentityType);
557
558             // If in the middle of Attach, try to find the entry by key
559             if (doAttach && existingEntry == null)
560             {
561                 // If we don't already have a key, create one now
562                 if (null == (object)key)
563                 {
564                     key = this.ObjectStateManager.CreateEntityKey(entitySet, wrappedEntity.Entity);
565                 }
566                 existingEntry = this.ObjectStateManager.FindEntityEntry(key);
567             }
568
569             if (null != existingEntry && !(doAttach && existingEntry.IsKeyEntry))
570             {
571                 if (!Object.ReferenceEquals(existingEntry.Entity, wrappedEntity.Entity))
572                 {
573                     throw EntityUtil.ObjectStateManagerContainsThisEntityKey();
574                 }
575                 else
576                 {
577                     EntityState exptectedState = doAttach ? EntityState.Unchanged : EntityState.Added;
578
579                     if (existingEntry.State != exptectedState)
580                     {
581                         throw doAttach ?
582                             EntityUtil.EntityAlreadyExistsInObjectStateManager() :
583                             EntityUtil.ObjectStateManagerDoesnotAllowToReAddUnchangedOrModifiedOrDeletedEntity(existingEntry.State);
584                     }
585                     else
586                     {
587                         // AttachTo:
588                         // Attach is no-op when the existing entry is not a KeyEntry
589                         // and it's entity is the same entity instance and it's state is Unchanged
590
591                         // AddObject:
592                         // AddObject is no-op when the existing entry's entity is the same entity 
593                         // instance and it's state is Added
594                         isNoOperation = true;
595                         return;
596                     }
597                 }
598             }
599         }
600
601         /// <summary>
602         /// Adds an object to the cache.  If it doesn't already have an entity key, the
603         /// entity set is determined based on the type and the O-C map.
604         /// If the object supports relationships (i.e. it implements IEntityWithRelationships),
605         /// this also sets the context onto its RelationshipManager object.
606         /// </summary>
607         /// <param name="entitySetName">entitySetName the Object to be added. It might be qualifed with container name </param>
608         /// <param name="entity">Object to be added.</param>
609         public void AddObject(string entitySetName, object entity)
610         {
611             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
612             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
613             EntityUtil.CheckArgumentNull(entity, "entity");
614
615             EntityEntry existingEntry;
616             IEntityWrapper wrappedEntity = EntityWrapperFactory.WrapEntityUsingContextGettingEntry(entity, this, out existingEntry);
617
618             if (existingEntry == null)
619             {
620                 // If the exact object being added is already in the context, there there is no way we need to
621                 // load the type for it, and since this is expensive, we only do the load if we have to.
622
623                 // SQLBUDT 480919: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
624                 // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
625                 // We will auto-load the entity type's assembly into the ObjectItemCollection.
626                 // We don't need the user's calling assembly for LoadAssemblyForType since entityType is sufficient.
627                 MetadataWorkspace.ImplicitLoadAssemblyForType(wrappedEntity.IdentityType, null);
628             }
629             else
630             {
631                 Debug.Assert((object)existingEntry.Entity == (object)entity, "FindEntityEntry should return null if existing entry contains a different object.");
632             }
633
634             EntitySet entitySet;
635             bool isNoOperation;
636
637             this.VerifyRootForAdd(false, entitySetName, wrappedEntity, existingEntry, out entitySet, out isNoOperation);
638             if (isNoOperation)
639             {
640                 return;
641             }
642
643             System.Data.Objects.Internal.TransactionManager transManager = ObjectStateManager.TransactionManager;
644             transManager.BeginAddTracking();
645
646             try
647             {
648                 RelationshipManager relationshipManager = wrappedEntity.RelationshipManager;
649                 Debug.Assert(relationshipManager != null, "Entity wrapper returned a null RelationshipManager");
650
651                 bool doCleanup = true;
652                 try
653                 {
654                     // Add the root of the graph to the cache.
655                     AddSingleObject(entitySet, wrappedEntity, "entity");
656                     doCleanup = false;
657                 }
658                 finally
659                 {
660                     // If we failed after adding the entry but before completely attaching the related ends to the context, we need to do some cleanup.
661                     // If the context is null, we didn't even get as far as trying to attach the RelationshipManager, so something failed before the entry
662                     // was even added, therefore there is nothing to clean up.
663                     if (doCleanup && wrappedEntity.Context == this)
664                     {
665                         // If the context is not null, it be because the failure happened after it was attached, or it
666                         // could mean that this entity was already attached, in which case we don't want to clean it up
667                         // If we find the entity in the context and its key is temporary, we must have just added it, so remove it now.
668                         EntityEntry entry = this.ObjectStateManager.FindEntityEntry(wrappedEntity.Entity);
669                         if (entry != null && entry.EntityKey.IsTemporary)
670                         {
671                             // devnote: relationshipManager is valid, so entity must be IEntityWithRelationships and casting is safe
672                             relationshipManager.NodeVisited = true;
673                             // devnote: even though we haven't added the rest of the graph yet, we need to go through the related ends and
674                             //          clean them up, because some of them could have been attached to the context before the failure occurred
675                             RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(wrappedEntity);
676                             RelatedEnd.RemoveEntityFromObjectStateManager(wrappedEntity);
677                         }
678                         // else entry was not added or the key is not temporary, so it must have already been in the cache before we tried to add this product, so don't remove anything
679                     }
680                 }
681                 relationshipManager.AddRelatedEntitiesToObjectStateManager(/*doAttach*/false);
682             }
683             finally
684             {
685                 transManager.EndAddTracking();
686                 ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
687             }
688         }
689         /// <summary>
690         /// Adds an object to the cache without adding its related
691         /// entities.
692         /// </summary>
693         /// <param name="entity">Object to be added.</param>
694         /// <param name="setName">EntitySet name for the Object to be added. It may be qualified with container name</param>
695         /// <param name="containerName">Container name for the Object to be added.</param>
696         /// <param name="argumentName">Name of the argument passed to a public method, for use in exceptions.</param>
697         internal void AddSingleObject(EntitySet entitySet, IEntityWrapper wrappedEntity, string argumentName)
698         {
699             Debug.Assert(entitySet != null, "The extent for an entity must be a non-null entity set.");
700             Debug.Assert(wrappedEntity != null, "The entity wrapper must not be null.");
701             Debug.Assert(wrappedEntity.Entity != null, "The entity must not be null.");
702
703             EntityKey key = wrappedEntity.GetEntityKeyFromEntity();
704             if (null != (object)key)
705             {
706                 EntityUtil.ValidateEntitySetInKey(key, entitySet);
707                 key.ValidateEntityKey(_workspace, entitySet);
708             }
709
710             VerifyContextForAddOrAttach(wrappedEntity);
711             wrappedEntity.Context = this;
712             EntityEntry entry = this.ObjectStateManager.AddEntry(wrappedEntity, (EntityKey)null, entitySet, argumentName, true);
713
714             // If the entity supports relationships, AttachContext on the
715             // RelationshipManager object - with load option of
716             // AppendOnly (if adding a new object to a context, set
717             // the relationships up to cache by default -- load option
718             // is only set to other values when AttachContext is
719             // called by the materializer). Also add all related entitites to
720             // cache.
721             //
722             // NOTE: AttachContext must be called after adding the object to
723             // the cache--otherwise the object might not have a key
724             // when the EntityCollections expect it to.            
725             Debug.Assert(this.ObjectStateManager.TransactionManager.TrackProcessedEntities, "Expected tracking processed entities to be true when adding.");
726             Debug.Assert(this.ObjectStateManager.TransactionManager.ProcessedEntities != null, "Expected non-null collection when flag set.");
727
728             this.ObjectStateManager.TransactionManager.ProcessedEntities.Add(wrappedEntity);
729
730             wrappedEntity.AttachContext(this, entitySet, MergeOption.AppendOnly);
731
732             // Find PK values in referenced principals and use these to set FK values
733             entry.FixupFKValuesFromNonAddedReferences();
734
735             _cache.FixupReferencesByForeignKeys(entry);
736             wrappedEntity.TakeSnapshotOfRelationships(entry);
737         }
738
739         /// <summary>
740         /// Explicitly loads a referenced entity or collection of entities into the given entity.
741         /// </summary>
742         /// <remarks>
743         /// After loading, the referenced entity or collection can be accessed through the properties
744         /// of the source entity.
745         /// </remarks>
746         /// <param name="entity">The source entity on which the relationship is defined</param>
747         /// <param name="navigationProperty">The name of the property to load</param>
748         public void LoadProperty(object entity, string navigationProperty)
749         {
750             IEntityWrapper wrappedEntity = WrapEntityAndCheckContext(entity, "property");
751             wrappedEntity.RelationshipManager.GetRelatedEnd(navigationProperty).Load();
752         }
753
754         /// <summary>
755         /// Explicitly loads a referenced entity or collection of entities into the given entity.
756         /// </summary>
757         /// <remarks>
758         /// After loading, the referenced entity or collection can be accessed through the properties
759         /// of the source entity.
760         /// </remarks>
761         /// <param name="entity">The source entity on which the relationship is defined</param>
762         /// <param name="navigationProperty">The name of the property to load</param>
763         /// <param name="mergeOption">The merge option to use for the load</param>
764         public void LoadProperty(object entity, string navigationProperty, MergeOption mergeOption)
765         {
766             IEntityWrapper wrappedEntity = WrapEntityAndCheckContext(entity, "property");
767             wrappedEntity.RelationshipManager.GetRelatedEnd(navigationProperty).Load(mergeOption);
768         }
769
770         /// <summary>
771         /// Explicitly loads a referenced entity or collection of entities into the given entity.
772         /// </summary>
773         /// <remarks>
774         /// After loading, the referenced entity or collection can be accessed through the properties
775         /// of the source entity.
776         /// The property to load is specified by a LINQ expression which must be in the form of
777         /// a simple property member access.  For example, <code>(entity) => entity.PropertyName</code>
778         /// where PropertyName is the navigation property to be loaded.  Other expression forms will
779         /// be rejected at runtime.
780         /// </remarks>
781         /// <param name="entity">The source entity on which the relationship is defined</param>
782         /// <param name="selector">A LINQ expression specifying the property to load</param>
783         public void LoadProperty<TEntity>(TEntity entity, Expression<Func<TEntity, object>> selector)
784         {
785             // We used to throw an ArgumentException if the expression contained a Convert.  Now we remove the convert,
786             // but if we still need to throw, then we should still throw an ArgumentException to avoid a breaking change.
787             // Therefore, we keep track of whether or not we removed the convert.
788             bool removedConvert;
789             var navProp = ParsePropertySelectorExpression<TEntity>(selector, out removedConvert);
790             IEntityWrapper wrappedEntity = WrapEntityAndCheckContext(entity, "property");
791             wrappedEntity.RelationshipManager.GetRelatedEnd(navProp, throwArgumentException: removedConvert).Load();
792         }
793
794         /// <summary>
795         /// Explicitly loads a referenced entity or collection of entities into the given entity.
796         /// </summary>
797         /// <remarks>
798         /// After loading, the referenced entity or collection can be accessed through the properties
799         /// of the source entity.
800         /// The property to load is specified by a LINQ expression which must be in the form of
801         /// a simple property member access.  For example, <code>(entity) => entity.PropertyName</code>
802         /// where PropertyName is the navigation property to be loaded.  Other expression forms will
803         /// be rejected at runtime.
804         /// </remarks>
805         /// <param name="entity">The source entity on which the relationship is defined</param>
806         /// <param name="selector">A LINQ expression specifying the property to load</param>
807         /// <param name="mergeOption">The merge option to use for the load</param>
808         public void LoadProperty<TEntity>(TEntity entity, Expression<Func<TEntity, object>> selector, MergeOption mergeOption)
809         {
810             // We used to throw an ArgumentException if the expression contained a Convert.  Now we remove the convert,
811             // but if we still need to throw, then we should still throw an ArgumentException to avoid a breaking change.
812             // Therefore, we keep track of whether or not we removed the convert.
813             bool removedConvert;
814             var navProp = ParsePropertySelectorExpression<TEntity>(selector, out removedConvert);
815             IEntityWrapper wrappedEntity = WrapEntityAndCheckContext(entity, "property");
816             wrappedEntity.RelationshipManager.GetRelatedEnd(navProp, throwArgumentException: removedConvert).Load(mergeOption);
817         }
818
819         // Wraps the given entity and checks that it has a non-null context (i.e. that is is not detached).
820         private IEntityWrapper WrapEntityAndCheckContext(object entity, string refType)
821         {
822             IEntityWrapper wrappedEntity = EntityWrapperFactory.WrapEntityUsingContext(entity, this);
823             if (wrappedEntity.Context == null)
824             {
825                 throw new InvalidOperationException(System.Data.Entity.Strings.ObjectContext_CannotExplicitlyLoadDetachedRelationships(refType));
826             }
827             if (wrappedEntity.Context != this)
828             {
829                 throw new InvalidOperationException(System.Data.Entity.Strings.ObjectContext_CannotLoadReferencesUsingDifferentContext(refType));
830             }
831             return wrappedEntity;
832         }
833
834         // Validates that the given property selector may represent a navigation property and returns the nav prop string.
835         // The actual check that the navigation property is valid is performed by the
836         // RelationshipManager while loading the RelatedEnd.
837         internal static string ParsePropertySelectorExpression<TEntity>(Expression<Func<TEntity, object>> selector, out bool removedConvert)
838         {
839             EntityUtil.CheckArgumentNull(selector, "selector");
840
841             // We used to throw an ArgumentException if the expression contained a Convert.  Now we remove the convert,
842             // but if we still need to throw, then we should still throw an ArgumentException to avoid a breaking change.
843             // Therefore, we keep track of whether or not we removed the convert.
844             removedConvert = false;
845             var body = selector.Body;
846             while (body.NodeType == ExpressionType.Convert || body.NodeType == ExpressionType.ConvertChecked)
847             {
848                 removedConvert = true;
849                 body = ((UnaryExpression)body).Operand;
850             }
851
852             var bodyAsMember = body as MemberExpression;
853             if (bodyAsMember == null ||
854                 !bodyAsMember.Member.DeclaringType.IsAssignableFrom(typeof(TEntity)) ||
855                 bodyAsMember.Expression.NodeType != ExpressionType.Parameter)
856             {
857                 throw new ArgumentException(System.Data.Entity.Strings.ObjectContext_SelectorExpressionMustBeMemberAccess);
858             }
859             return bodyAsMember.Member.Name;
860         }
861
862         /// <summary>
863         /// Apply modified properties to the original object.
864         /// This API is obsolete.  Please use ApplyCurrentValues instead.
865         /// </summary>
866         /// <param name="entitySetName">name of EntitySet of entity to be updated</param>
867         /// <param name="changed">object with modified properties</param>
868         [EditorBrowsable(EditorBrowsableState.Never)]
869         [Browsable(false)]
870         [Obsolete("Use ApplyCurrentValues instead")]
871         public void ApplyPropertyChanges(string entitySetName, object changed)
872         {
873             EntityUtil.CheckStringArgument(entitySetName, "entitySetName");
874             EntityUtil.CheckArgumentNull(changed, "changed");
875
876             this.ApplyCurrentValues(entitySetName, changed);
877         }
878
879         /// <summary>
880         /// Apply modified properties to the original object.
881         /// </summary>
882         /// <param name="entitySetName">name of EntitySet of entity to be updated</param>
883         /// <param name="currentEntity">object with modified properties</param>
884         public TEntity ApplyCurrentValues<TEntity>(string entitySetName, TEntity currentEntity) where TEntity : class
885         {
886             EntityUtil.CheckStringArgument(entitySetName, "entitySetName");
887             EntityUtil.CheckArgumentNull(currentEntity, "currentEntity");
888             IEntityWrapper wrappedEntity = EntityWrapperFactory.WrapEntityUsingContext(currentEntity, this);
889
890             // SQLBUDT 480919: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
891             // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
892             // We will auto-load the entity type's assembly into the ObjectItemCollection.
893             // We don't need the user's calling assembly for LoadAssemblyForType since entityType is sufficient.
894             MetadataWorkspace.ImplicitLoadAssemblyForType(wrappedEntity.IdentityType, null);
895
896             EntitySet entitySet = this.GetEntitySetFromName(entitySetName);
897
898             EntityKey key = wrappedEntity.EntityKey;
899             if (null != (object)key)
900             {
901                 EntityUtil.ValidateEntitySetInKey(key, entitySet, "entitySetName");
902                 key.ValidateEntityKey(_workspace, entitySet);
903             }
904             else
905             {
906                 key = this.ObjectStateManager.CreateEntityKey(entitySet, currentEntity);
907             }
908
909             // Check if entity is already in the cache
910             EntityEntry ose = this.ObjectStateManager.FindEntityEntry(key);
911             if (ose == null || ose.IsKeyEntry)
912             {
913                 throw EntityUtil.EntityNotTracked();
914             }
915
916             ose.ApplyCurrentValuesInternal(wrappedEntity);
917
918             return (TEntity)ose.Entity;
919         }
920
921         /// <summary>
922         /// Apply original values to the entity.
923         /// The entity to update is found based on key values of the <paramref name="originalEntity"/> entity and the given <paramref name="entitySetName"/>.
924         /// </summary>
925         /// <param name="entitySetName">name of EntitySet of entity to be updated</param>
926         /// <param name="originalEntity">object with original values</param>
927         /// <returns>updated entity</returns>
928         public TEntity ApplyOriginalValues<TEntity>(string entitySetName, TEntity originalEntity) where TEntity : class
929         {
930             EntityUtil.CheckStringArgument(entitySetName, "entitySetName");
931             EntityUtil.CheckArgumentNull(originalEntity, "originalEntity");
932
933             IEntityWrapper wrappedOriginalEntity = EntityWrapperFactory.WrapEntityUsingContext(originalEntity, this);
934
935             // SQLBUDT 480919: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
936             // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
937             // We will auto-load the entity type's assembly into the ObjectItemCollection.
938             // We don't need the user's calling assembly for LoadAssemblyForType since entityType is sufficient.
939             MetadataWorkspace.ImplicitLoadAssemblyForType(wrappedOriginalEntity.IdentityType, null);
940
941             EntitySet entitySet = this.GetEntitySetFromName(entitySetName);
942
943             EntityKey key = wrappedOriginalEntity.EntityKey;
944             if (null != (object)key)
945             {
946                 EntityUtil.ValidateEntitySetInKey(key, entitySet, "entitySetName");
947                 key.ValidateEntityKey(_workspace, entitySet);
948             }
949             else
950             {
951                 key = this.ObjectStateManager.CreateEntityKey(entitySet, originalEntity);
952             }
953
954             // Check if the entity is already in the cache
955             EntityEntry ose = this.ObjectStateManager.FindEntityEntry(key);
956             if (ose == null || ose.IsKeyEntry)
957             {
958                 throw EntityUtil.EntityNotTrackedOrHasTempKey();
959             }
960
961             if (ose.State != EntityState.Modified &&
962                 ose.State != EntityState.Unchanged &&
963                 ose.State != EntityState.Deleted)
964             {
965                 throw EntityUtil.EntityMustBeUnchangedOrModifiedOrDeleted(ose.State);
966             }
967
968             if (ose.WrappedEntity.IdentityType != wrappedOriginalEntity.IdentityType)
969             {
970                 throw EntityUtil.EntitiesHaveDifferentType(ose.Entity.GetType().FullName, originalEntity.GetType().FullName);
971             }
972
973             ose.CompareKeyProperties(originalEntity);
974
975             // The ObjectStateEntry.UpdateModifiedFields uses a variation of Shaper.UpdateRecord method 
976             // which additionaly marks properties as modified as necessary.
977             ose.UpdateOriginalValues(wrappedOriginalEntity.Entity);
978
979             // return the current entity
980             return (TEntity)ose.Entity;
981         }
982
983
984         /// <summary>
985         /// Attach entity graph into the context in the Unchanged state.
986         /// This version takes entity which doesn't have to have a Key.
987         /// </summary>
988         /// <param name="entitySetName">EntitySet name for the Object to be attached. It may be qualified with container name</param>        
989         /// <param name="entity"></param>
990         public void AttachTo(string entitySetName, object entity)
991         {
992             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
993             EntityUtil.CheckArgumentNull(entity, "entity");
994             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
995
996             EntityEntry existingEntry;
997             IEntityWrapper wrappedEntity = EntityWrapperFactory.WrapEntityUsingContextGettingEntry(entity, this, out existingEntry);
998
999             if (existingEntry == null)
1000             {
1001                 // If the exact object being added is already in the context, there there is no way we need to
1002                 // load the type for it, and since this is expensive, we only do the load if we have to.
1003
1004                 // SQLBUDT 480919: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
1005                 // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
1006                 // We will auto-load the entity type's assembly into the ObjectItemCollection.
1007                 // We don't need the user's calling assembly for LoadAssemblyForType since entityType is sufficient.
1008                 MetadataWorkspace.ImplicitLoadAssemblyForType(wrappedEntity.IdentityType, null);
1009             }
1010             else
1011             {
1012                 Debug.Assert((object)existingEntry.Entity == (object)entity, "FindEntityEntry should return null if existing entry contains a different object.");
1013             }
1014
1015             EntitySet entitySet;
1016             bool isNoOperation;
1017
1018             this.VerifyRootForAdd(true, entitySetName, wrappedEntity, existingEntry, out entitySet, out isNoOperation);
1019             if (isNoOperation)
1020             {
1021                 return;
1022             }
1023
1024             System.Data.Objects.Internal.TransactionManager transManager = ObjectStateManager.TransactionManager;
1025             transManager.BeginAttachTracking();
1026
1027             try
1028             {
1029                 this.ObjectStateManager.TransactionManager.OriginalMergeOption = wrappedEntity.MergeOption;
1030                 RelationshipManager relationshipManager = wrappedEntity.RelationshipManager;
1031                 Debug.Assert(relationshipManager != null, "Entity wrapper returned a null RelationshipManager");
1032
1033                 bool doCleanup = true;
1034                 try
1035                 {
1036                     // Attach the root of entity graph to the cache.
1037                     AttachSingleObject(wrappedEntity, entitySet, "entity");
1038                     doCleanup = false;
1039                 }
1040                 finally
1041                 {
1042                     // SQLBU 555615 Be sure that wrappedEntity.Context == this to not try to detach 
1043                     // entity from context if it was already attached to some other context.
1044                     // It's enough to check this only for the root of the graph since we can assume that all entities
1045                     // in the graph are attached to the same context (or none of them is attached).
1046                     if (doCleanup && wrappedEntity.Context == this)
1047                     {
1048                         // SQLBU 509900 RIConstraints: Entity still exists in cache after Attach fails
1049                         //
1050                         // Cleaning up is needed only when root of the graph violates some referential constraint.
1051                         // Normal cleaning is done in RelationshipManager.AddRelatedEntitiesToObjectStateManager()
1052                         // (referential constraints properties are checked in AttachSingleObject(), before
1053                         // AddRelatedEntitiesToObjectStateManager is called, that's why normal cleaning
1054                         // doesn't work in this case)
1055
1056                         relationshipManager.NodeVisited = true;
1057                         // devnote: even though we haven't attached the rest of the graph yet, we need to go through the related ends and
1058                         //          clean them up, because some of them could have been attached to the context.
1059                         RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(wrappedEntity);
1060                         RelatedEnd.RemoveEntityFromObjectStateManager(wrappedEntity);
1061                     }
1062                 }
1063                 relationshipManager.AddRelatedEntitiesToObjectStateManager(/*doAttach*/true);
1064             }
1065             finally
1066             {
1067                 transManager.EndAttachTracking();
1068                 ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1069             }
1070         }
1071         /// <summary>
1072         /// Attach entity graph into the context in the Unchanged state.
1073         /// This version takes entity which does have to have a non-temporary Key.
1074         /// </summary>
1075         /// <param name="entity"></param>        
1076         public void Attach(IEntityWithKey entity)
1077         {
1078             EntityUtil.CheckArgumentNull(entity, "entity");
1079
1080             if (null == (object)entity.EntityKey)
1081             {
1082                 throw EntityUtil.CannotAttachEntityWithoutKey();
1083             }
1084
1085             this.AttachTo(null, entity);
1086         }
1087         /// <summary>
1088         /// Attaches single object to the cache without adding its related entities.
1089         /// </summary>
1090         /// <param name="entity">Entity to be attached.</param>
1091         /// <param name="entitySet">"Computed" entity set.</param>
1092         /// <param name="argumentName">Name of the argument passed to a public method, for use in exceptions.</param>
1093         internal void AttachSingleObject(IEntityWrapper wrappedEntity, EntitySet entitySet, string argumentName)
1094         {
1095             Debug.Assert(wrappedEntity != null, "entity wrapper shouldn't be null");
1096             Debug.Assert(wrappedEntity.Entity != null, "entity shouldn't be null");
1097             Debug.Assert(entitySet != null, "entitySet shouldn't be null");
1098
1099             // Try to detect if the entity is invalid as soon as possible
1100             // (before adding the entity to the ObjectStateManager)
1101             RelationshipManager relationshipManager = wrappedEntity.RelationshipManager;
1102             Debug.Assert(relationshipManager != null, "Entity wrapper returned a null RelationshipManager");
1103
1104             EntityKey key = wrappedEntity.GetEntityKeyFromEntity();
1105             if (null != (object)key)
1106             {
1107                 EntityUtil.ValidateEntitySetInKey(key, entitySet);
1108                 key.ValidateEntityKey(_workspace, entitySet);
1109             }
1110             else
1111             {
1112                 key = this.ObjectStateManager.CreateEntityKey(entitySet, wrappedEntity.Entity);
1113             }
1114
1115             Debug.Assert(key != null, "GetEntityKey should have returned a non-null key");
1116
1117             // Temporary keys are not allowed
1118             if (key.IsTemporary)
1119             {
1120                 throw EntityUtil.CannotAttachEntityWithTemporaryKey();
1121             }
1122
1123             if (wrappedEntity.EntityKey != key)
1124             {
1125                 wrappedEntity.EntityKey = key;
1126             }
1127
1128             // Check if entity already exists in the cache.
1129             // NOTE: This check could be done earlier, but this way I avoid creating key twice.
1130             EntityEntry entry = ObjectStateManager.FindEntityEntry(key);
1131
1132             if (null != entry)
1133             {
1134                 if (entry.IsKeyEntry)
1135                 {
1136                     // devnote: SQLBU 555615. This method was extracted from PromoteKeyEntry to have consistent
1137                     // behavior of AttachTo in case of attaching entity which is already attached to some other context.
1138                     // We can not detect if entity is attached to another context until we call SetChangeTrackerOntoEntity
1139                     // which throws exception if the change tracker is already set.  
1140                     // SetChangeTrackerOntoEntity is now called from PromoteKeyEntryInitialization(). 
1141                     // Calling PromoteKeyEntryInitialization() before calling relationshipManager.AttachContext prevents
1142                     // overriding Context property on relationshipManager (and attaching relatedEnds to current context).
1143                     this.ObjectStateManager.PromoteKeyEntryInitialization(this, entry, wrappedEntity, /*shadowValues*/ null, /*replacingEntry*/ false);
1144
1145                     Debug.Assert(this.ObjectStateManager.TransactionManager.TrackProcessedEntities, "Expected tracking processed entities to be true when adding.");
1146                     Debug.Assert(this.ObjectStateManager.TransactionManager.ProcessedEntities != null, "Expected non-null collection when flag set.");
1147
1148                     this.ObjectStateManager.TransactionManager.ProcessedEntities.Add(wrappedEntity);
1149
1150                     wrappedEntity.TakeSnapshotOfRelationships(entry);
1151
1152                     this.ObjectStateManager.PromoteKeyEntry(entry,
1153                         wrappedEntity,
1154                         /*shadowValues*/ null,
1155                         /*replacingEntry*/ false,
1156                         /*setIsLoaded*/ false,
1157                         /*keyEntryInitialized*/ true,
1158                         "Attach");
1159
1160                     ObjectStateManager.FixupReferencesByForeignKeys(entry);
1161
1162                     relationshipManager.CheckReferentialConstraintProperties(entry);
1163                 }
1164                 else
1165                 {
1166                     Debug.Assert(!Object.ReferenceEquals(entry.Entity, wrappedEntity.Entity));
1167                     throw EntityUtil.ObjectStateManagerContainsThisEntityKey();
1168                 }
1169             }
1170             else
1171             {
1172                 VerifyContextForAddOrAttach(wrappedEntity);
1173                 wrappedEntity.Context = this;
1174                 entry = this.ObjectStateManager.AttachEntry(key, wrappedEntity, entitySet, argumentName);
1175
1176                 Debug.Assert(this.ObjectStateManager.TransactionManager.TrackProcessedEntities, "Expected tracking processed entities to be true when adding.");
1177                 Debug.Assert(this.ObjectStateManager.TransactionManager.ProcessedEntities != null, "Expected non-null collection when flag set.");
1178
1179                 this.ObjectStateManager.TransactionManager.ProcessedEntities.Add(wrappedEntity);
1180
1181                 wrappedEntity.AttachContext(this, entitySet, MergeOption.AppendOnly);
1182
1183                 ObjectStateManager.FixupReferencesByForeignKeys(entry);
1184                 wrappedEntity.TakeSnapshotOfRelationships(entry);
1185
1186                 relationshipManager.CheckReferentialConstraintProperties(entry);
1187             }
1188         }
1189
1190         /// <summary>
1191         /// When attaching we need to check that the entity is not already attached to a different context
1192         /// before we wipe away that context.
1193         /// </summary>
1194         private void VerifyContextForAddOrAttach(IEntityWrapper wrappedEntity)
1195         {
1196             if (wrappedEntity.Context != null &&
1197                 wrappedEntity.Context != this &&
1198                 !wrappedEntity.Context.ObjectStateManager.IsDisposed &&
1199                 wrappedEntity.MergeOption != MergeOption.NoTracking)
1200             {
1201                 throw EntityUtil.EntityCantHaveMultipleChangeTrackers();
1202             }
1203         }
1204
1205         /// <summary>
1206         /// Create entity key based on given entity set and values of given entity.
1207         /// </summary>
1208         /// <param name="entitySetName">entity set of the entity</param>
1209         /// <param name="entity">entity</param>
1210         /// <returns>new instance of entity key</returns>
1211         public EntityKey CreateEntityKey(string entitySetName, object entity)
1212         {
1213             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
1214             EntityUtil.CheckStringArgument(entitySetName, "entitySetName");
1215             EntityUtil.CheckArgumentNull(entity, "entity");
1216
1217             // SQLBUDT 480919: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
1218             // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
1219             // We will auto-load the entity type's assembly into the ObjectItemCollection.
1220             // We don't need the user's calling assembly for LoadAssemblyForType since entityType is sufficient.
1221             MetadataWorkspace.ImplicitLoadAssemblyForType(EntityUtil.GetEntityIdentityType(entity.GetType()), null);
1222
1223             EntitySet entitySet = this.GetEntitySetFromName(entitySetName);
1224
1225             return this.ObjectStateManager.CreateEntityKey(entitySet, entity);
1226         }
1227
1228         internal EntitySet GetEntitySetFromName(string entitySetName)
1229         {
1230             string setName;
1231             string containerName;
1232
1233             ObjectContext.GetEntitySetName(entitySetName, "entitySetName", this, out setName, out containerName);
1234
1235             // Find entity set using entitySetName and entityContainerName
1236             return this.GetEntitySet(setName, containerName);
1237         }
1238
1239         private void AddRefreshKey(object entityLike, Dictionary<EntityKey, EntityEntry> entities, Dictionary<EntitySet, List<EntityKey>> currentKeys)
1240         {
1241             Debug.Assert(!(entityLike is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
1242             if (null == entityLike)
1243             {
1244                 throw EntityUtil.NthElementIsNull(entities.Count);
1245             }
1246
1247             IEntityWrapper wrappedEntity = EntityWrapperFactory.WrapEntityUsingContext(entityLike, this);
1248             EntityKey key = wrappedEntity.EntityKey;
1249             RefreshCheck(entities, entityLike, key);
1250
1251             // Retrieve the EntitySet for the EntityKey and add an entry in the dictionary
1252             // that maps a set to the keys of entities that should be refreshed from that set.
1253             EntitySet entitySet = key.GetEntitySet(this.MetadataWorkspace);
1254
1255             List<EntityKey> setKeys = null;
1256             if (!currentKeys.TryGetValue(entitySet, out setKeys))
1257             {
1258                 setKeys = new List<EntityKey>();
1259                 currentKeys.Add(entitySet, setKeys);
1260             }
1261
1262             setKeys.Add(key);
1263         }
1264
1265         /// <summary>
1266         /// Creates an ObjectSet based on the EntitySet that is defined for TEntity.
1267         /// Requires that the DefaultContainerName is set for the context and that there is a
1268         /// single EntitySet for the specified type. Throws exception if more than one type is found.
1269         /// </summary>
1270         /// <typeparam name="TEntity">Entity type for the requested ObjectSet</typeparam>
1271         public ObjectSet<TEntity> CreateObjectSet<TEntity>()
1272             where TEntity : class
1273         {
1274             EntitySet entitySet = GetEntitySetForType(typeof(TEntity), "TEntity");
1275             return new ObjectSet<TEntity>(entitySet, this);
1276         }
1277
1278         /// <summary>
1279         /// Find the EntitySet in the default EntityContainer for the specified CLR type.
1280         /// Must be a valid mapped entity type and must be mapped to exactly one EntitySet across all of the EntityContainers in the metadata for this context.
1281         /// </summary>
1282         /// <param name="entityCLRType">CLR type to use for EntitySet lookup.</param>
1283         /// <returns></returns>
1284         private EntitySet GetEntitySetForType(Type entityCLRType, string exceptionParameterName)
1285         {
1286             EntitySet entitySetForType = null;
1287
1288             EntityContainer defaultContainer = this.Perspective.GetDefaultContainer();
1289             if (defaultContainer == null)
1290             {
1291                 // We don't have a default container, so look through all EntityContainers in metadata to see if
1292                 // we can find exactly one EntitySet that matches the specified CLR type.
1293                 System.Collections.ObjectModel.ReadOnlyCollection<EntityContainer> entityContainers = this.MetadataWorkspace.GetItems<EntityContainer>(DataSpace.CSpace);
1294                 foreach (EntityContainer entityContainer in entityContainers)
1295                 {
1296                     // See if this container has exactly one EntitySet for this type
1297                     EntitySet entitySetFromContainer = GetEntitySetFromContainer(entityContainer, entityCLRType, exceptionParameterName);
1298
1299                     if (entitySetFromContainer != null)
1300                     {
1301                         // Verify we haven't already found a matching EntitySet in some other container
1302                         if (entitySetForType != null)
1303                         {
1304                             // There is more than one EntitySet for this type across all containers in metadata, so we can't determine which one the user intended
1305                             throw EntityUtil.MultipleEntitySetsFoundInAllContainers(entityCLRType.FullName, exceptionParameterName);
1306                         }
1307
1308                         entitySetForType = entitySetFromContainer;
1309                     }
1310                 }
1311             }
1312             else
1313             {
1314                 // There is a default container, so restrict the search to EntitySets within it
1315                 entitySetForType = GetEntitySetFromContainer(defaultContainer, entityCLRType, exceptionParameterName);
1316             }
1317
1318             // We still may not have found a matching EntitySet for this type
1319             if (entitySetForType == null)
1320             {
1321                 throw EntityUtil.NoEntitySetFoundForType(entityCLRType.FullName, exceptionParameterName);
1322             }
1323
1324             return entitySetForType;
1325         }
1326
1327         private EntitySet GetEntitySetFromContainer(EntityContainer container, Type entityCLRType, string exceptionParameterName)
1328         {
1329             // Verify that we have an EdmType mapping for the specified CLR type
1330             EdmType entityEdmType = GetTypeUsage(entityCLRType).EdmType;
1331
1332             // Try to find a single EntitySet for the specified type
1333             EntitySet entitySet = null;
1334             foreach (EntitySetBase es in container.BaseEntitySets)
1335             {
1336                 // This is a match if the set is an EntitySet (not an AssociationSet) and the EntitySet
1337                 // is defined for the specified entity type. Must be an exact match, not a base type. 
1338                 if (es.BuiltInTypeKind == BuiltInTypeKind.EntitySet && es.ElementType == entityEdmType)
1339                 {
1340                     if (entitySet != null)
1341                     {
1342                         // There is more than one EntitySet for this type, so we can't determine which one the user intended
1343                         throw EntityUtil.MultipleEntitySetsFoundInSingleContainer(entityCLRType.FullName, container.Name, exceptionParameterName);
1344                     }
1345
1346                     entitySet = (EntitySet)es;
1347                 }
1348             }
1349
1350             return entitySet;
1351         }
1352
1353         /// <summary>
1354         /// Creates an ObjectSet based on the specified EntitySet name.
1355         /// </summary>
1356         /// <typeparam name="TEntity">Expected type of the EntitySet</typeparam>
1357         /// <param name="entitySetName">
1358         /// EntitySet to use for the ObjectSet. Can be fully-qualified or unqualified if the DefaultContainerName is set.
1359         /// </param>
1360         public ObjectSet<TEntity> CreateObjectSet<TEntity>(string entitySetName)
1361             where TEntity : class
1362         {
1363             EntitySet entitySet = GetEntitySetForNameAndType(entitySetName, typeof(TEntity), "TEntity");
1364             return new ObjectSet<TEntity>(entitySet, this);
1365         }
1366
1367         /// <summary>
1368         /// Finds an EntitySet with the specified name and verifies that its type matches the specified type.
1369         /// </summary>
1370         /// <param name="entitySetName">
1371         /// Name of the EntitySet to find. Can be fully-qualified or unqualified if the DefaultContainerName is set
1372         /// </param>
1373         /// <param name="entityCLRType">
1374         /// Expected CLR type of the EntitySet. Must exactly match the type for the EntitySet, base types are not valid.
1375         /// </param>
1376         /// <param name="exceptionParameterName">Argument name to use if an exception occurs.</param>
1377         /// <returns>EntitySet that was found in metadata with the specified parameters</returns>
1378         private EntitySet GetEntitySetForNameAndType(string entitySetName, Type entityCLRType, string exceptionParameterName)
1379         {
1380             // Verify that the specified entitySetName exists in metadata
1381             EntitySet entitySet = GetEntitySetFromName(entitySetName);
1382
1383             // Verify that the EntitySet type matches the specified type exactly (a base type is not valid)
1384             EdmType entityEdmType = GetTypeUsage(entityCLRType).EdmType;
1385             if (entitySet.ElementType != entityEdmType)
1386             {
1387                 throw EntityUtil.InvalidEntityTypeForObjectSet(entityCLRType.FullName, entitySet.ElementType.FullName, entitySetName, exceptionParameterName);
1388             }
1389
1390             return entitySet;
1391         }
1392
1393         #region Connection Management
1394
1395         /// <summary>
1396         /// Ensures that the connection is opened for an operation that requires an open connection to the store.
1397         /// Calls to EnsureConnection MUST be matched with a single call to ReleaseConnection.
1398         /// </summary>
1399         /// <exception cref="ObjectDisposedException">If the <see cref="ObjectContext"/> instance has been disposed.</exception>
1400         internal void EnsureConnection()
1401         {
1402             if (_connection == null)
1403             {
1404                 throw EntityUtil.ObjectContextDisposed();
1405             }
1406
1407             if (ConnectionState.Closed == Connection.State)
1408             {
1409                 Connection.Open();
1410                 _openedConnection = true;
1411             }
1412
1413             if (_openedConnection)
1414             {
1415                 _connectionRequestCount++;
1416             }
1417
1418             // Check the connection was opened correctly
1419             if ((_connection.State == ConnectionState.Closed) || (_connection.State == ConnectionState.Broken))
1420             {
1421                 string message = System.Data.Entity.Strings.EntityClient_ExecutingOnClosedConnection(
1422                        _connection.State == ConnectionState.Closed ? System.Data.Entity.Strings.EntityClient_ConnectionStateClosed : System.Data.Entity.Strings.EntityClient_ConnectionStateBroken);
1423                 throw EntityUtil.InvalidOperation(message);
1424             }
1425
1426             try
1427             {
1428                 // Make sure the necessary metadata is registered
1429                 EnsureMetadata();
1430
1431                 #region EnsureContextIsEnlistedInCurrentTransaction
1432
1433                 // The following conditions are no longer valid since Metadata Independence.
1434                 Debug.Assert(ConnectionState.Open == _connection.State, "Connection must be open.");
1435
1436                 // IF YOU MODIFIED THIS TABLE YOU MUST UPDATE TESTS IN SaveChangesTransactionTests SUITE ACCORDINGLY AS SOME CASES REFER TO NUMBERS IN THIS TABLE
1437                 //
1438                 // TABLE OF ACTIONS WE PERFORM HERE:
1439                 //
1440                 //  #  lastTransaction     currentTransaction         ConnectionState   WillClose      Action                                  Behavior when no explicit transaction (started with .ElistTransaction())     Behavior with explicit transaction (started with .ElistTransaction())
1441                 //  1   null                null                       Open              No             no-op;                                  implicit transaction will be created and used                                explicit transaction should be used
1442                 //  2   non-null tx1        non-null tx1               Open              No             no-op;                                  the last transaction will be used                                            N/A - it is not possible to EnlistTransaction if another transaction has already enlisted
1443                 //  3   null                non-null                   Closed            Yes            connection.Open();                      Opening connection will automatically enlist into Transaction.Current        N/A - cannot enlist in transaction on a closed connection
1444                 //  4   null                non-null                   Open              No             connection.Enlist(currentTransaction);  currentTransaction enlisted and used                                         N/A - it is not possible to EnlistTransaction if another transaction has already enlisted
1445                 //  5   non-null            null                       Open              No             no-op;                                  implicit transaction will be created and used                                explicit transaction should be used
1446                 //  6   non-null            null                       Closed            Yes            no-op;                                  implicit transaction will be created and used                                N/A - cannot enlist in transaction on a closed connection
1447                 //  7   non-null tx1        non-null tx2               Open              No             connection.Enlist(currentTransaction);  currentTransaction enlisted and used                                         N/A - it is not possible to EnlistTransaction if another transaction has already enlisted
1448                 //  8   non-null tx1        non-null tx2               Open              Yes            connection.Close(); connection.Open();  Re-opening connection will automatically enlist into Transaction.Current     N/A - only applies to TransactionScope - requires two transactions and CommitableTransaction and TransactionScope cannot be mixed
1449                 //  9   non-null tx1        non-null tx2               Closed            Yes            connection.Open();                      Opening connection will automatcially enlist into Transaction.Current        N/A - cannot enlist in transaction on a closed connection
1450
1451                 Transaction currentTransaction = Transaction.Current;
1452
1453                 bool transactionHasChanged = (null != currentTransaction && !currentTransaction.Equals(_lastTransaction))
1454                                           || (null != _lastTransaction && !_lastTransaction.Equals(currentTransaction));
1455
1456                 if (transactionHasChanged)
1457                 {
1458                     if (!_openedConnection)
1459                     {
1460                         // We didn't open the connection so, just try to enlist the connection in the current transaction. 
1461                         // Note that the connection can already be enlisted in a transaction (since the user opened 
1462                         // it s/he could enlist it manually using EntityConnection.EnlistTransaction() method). If the 
1463                         // transaction the connection is enlisted in has not completed (e.g. nested transaction) this call 
1464                         // will fail (throw). Also currentTransaction can be null here which means that the transaction
1465                         // used in the previous operation has completed. In this case we should not enlist the connection
1466                         // in "null" transaction as the user might have enlisted in a transaction manually between calls by 
1467                         // calling EntityConnection.EnlistTransaction() method. Enlisting with null would in this case mean "unenlist" 
1468                         // and would cause an exception (see above). Had the user not enlisted in a transaction between the calls
1469                         // enlisting with null would be a no-op - so again no reason to do it. 
1470                         if (currentTransaction != null)
1471                         {
1472                             _connection.EnlistTransaction(currentTransaction);
1473                         }
1474                     }
1475                     else if (_connectionRequestCount > 1)
1476                     {
1477                         // We opened the connection. In addition we are here because there are multiple
1478                         // active requests going on (read: enumerators that has not been disposed yet) 
1479                         // using the same connection. (If there is only one active request e.g. like SaveChanges
1480                         // or single enumerator there is no need for any specific transaction handling - either
1481                         // we use the implicit ambient transaction (Transaction.Current) if one exists or we 
1482                         // will create our own local transaction. Also if there is only one active request
1483                         // the user could not enlist it in a transaction using EntityConnection.EnlistTransaction()
1484                         // because we opened the connection).
1485                         // If there are multiple active requests the user might have "played" with transactions
1486                         // after the first transaction. This code tries to deal with this kind of changes.
1487
1488                         if (null == _lastTransaction)
1489                         {
1490                             Debug.Assert(currentTransaction != null, "transaction has changed and the lastTransaction was null");
1491
1492                             // Two cases here: 
1493                             // - the previous operation was not run inside a transaction created by the user while this one is - just
1494                             //   enlist the connection in the transaction
1495                             // - the previous operation ran withing explicit transaction started with EntityConnection.EnlistTransaction()
1496                             //   method - try enlisting the connection in the transaction. This may fail however if the transactions 
1497                             //   are nested as you cannot enlist the connection in the transaction until the previous transaction has
1498                             //   completed.
1499                             _connection.EnlistTransaction(currentTransaction);
1500                         }
1501                         else
1502                         {
1503                             // We'll close and reopen the connection to get the benefit of automatic transaction enlistment.
1504                             // Remarks: We get here only if there is more than one active query (e.g. nested foreach or two subsequent queries or SaveChanges
1505                             // inside a for each) and each of these queries are using a different transaction (note that using TransactionScopeOption.Required 
1506                             // will not create a new transaction if an ambient transaction already exists - the ambient transaction will be used and we will 
1507                             // not end up in this code path). If we get here we are already in a loss-loss situation - we cannot enlist to the second transaction
1508                             // as this would cause an exception saying that there is already an active transaction that needs to be committed or rolled back
1509                             // before we can enlist the connection to a new transaction. The other option (and this is what we do here) is to close and reopen
1510                             // the connection. This will enlist the newly opened connection to the second transaction but will also close the reader being used
1511                             // by the first active query. As a result when trying to continue reading results from the first query the user will get an exception
1512                             // saying that calling "Read" on a closed data reader is not a valid operation.
1513                             _connection.Close();
1514                             _connection.Open();
1515                             _openedConnection = true;
1516                             _connectionRequestCount++;
1517                         }
1518                     }
1519                 }
1520                 else
1521                 {
1522                     // we don't need to do anything, nothing has changed.
1523                 }
1524
1525                 // If we get here, we have an open connection, either enlisted in the current
1526                 // transaction (if it's non-null) or unenlisted from all transactions (if the
1527                 // current transaction is null)
1528                 _lastTransaction = currentTransaction;
1529
1530                 #endregion
1531             }
1532             catch (Exception)
1533             {
1534                 // when the connection is unable to enlist properly or another error occured, be sure to release this connection
1535                 ReleaseConnection();
1536                 throw;
1537             }
1538
1539         }
1540
1541         /// <summary>
1542         /// Resets the state of connection management when the connection becomes closed.
1543         /// </summary>
1544         /// <param name="sender"></param>
1545         /// <param name="e"></param>
1546         private void ConnectionStateChange(object sender, StateChangeEventArgs e)
1547         {
1548             if (e.CurrentState == ConnectionState.Closed)
1549             {
1550                 _connectionRequestCount = 0;
1551                 _openedConnection = false;
1552             }
1553         }
1554
1555         /// <summary>
1556         /// Releases the connection, potentially closing the connection if no active operations
1557         /// require the connection to be open. There should be a single ReleaseConnection call
1558         /// for each EnsureConnection call.
1559         /// </summary>
1560         /// <exception cref="ObjectDisposedException">If the <see cref="ObjectContext"/> instance has been disposed.</exception>
1561         internal void ReleaseConnection()
1562         {
1563             if (_connection == null)
1564             {
1565                 throw EntityUtil.ObjectContextDisposed();
1566             }
1567
1568             if (_openedConnection)
1569             {
1570                 Debug.Assert(_connectionRequestCount > 0, "_connectionRequestCount is zero or negative");
1571                 if (_connectionRequestCount > 0)
1572                 {
1573                     _connectionRequestCount--;
1574                 }
1575
1576                 // When no operation is using the connection and the context had opened the connection
1577                 // the connection can be closed
1578                 if (_connectionRequestCount == 0)
1579                 {
1580                     Connection.Close();
1581                     _openedConnection = false;
1582                 }
1583             }
1584         }
1585
1586         internal void EnsureMetadata()
1587         {
1588             if (!MetadataWorkspace.IsItemCollectionAlreadyRegistered(DataSpace.SSpace))
1589             {
1590                 Debug.Assert(!MetadataWorkspace.IsItemCollectionAlreadyRegistered(DataSpace.CSSpace), "ObjectContext has C-S metadata but not S?");
1591
1592                 // Only throw an ObjectDisposedException if an attempt is made to access the underlying connection object.
1593                 if (_connection == null)
1594                 {
1595                     throw EntityUtil.ObjectContextDisposed();
1596                 }
1597
1598                 MetadataWorkspace connectionWorkspace = _connection.GetMetadataWorkspace();
1599
1600                 Debug.Assert(connectionWorkspace.IsItemCollectionAlreadyRegistered(DataSpace.CSpace) &&
1601                              connectionWorkspace.IsItemCollectionAlreadyRegistered(DataSpace.SSpace) &&
1602                              connectionWorkspace.IsItemCollectionAlreadyRegistered(DataSpace.CSSpace),
1603                             "EntityConnection.GetMetadataWorkspace() did not return an initialized workspace?");
1604
1605                 // Validate that the context's MetadataWorkspace and the underlying connection's MetadataWorkspace
1606                 // have the same CSpace collection. Otherwise, an error will occur when trying to set the SSpace
1607                 // and CSSpace metadata
1608                 ItemCollection connectionCSpaceCollection = connectionWorkspace.GetItemCollection(DataSpace.CSpace);
1609                 ItemCollection contextCSpaceCollection = MetadataWorkspace.GetItemCollection(DataSpace.CSpace);
1610                 if (!object.ReferenceEquals(connectionCSpaceCollection, contextCSpaceCollection))
1611                 {
1612                     throw EntityUtil.ContextMetadataHasChanged();
1613                 }
1614                 MetadataWorkspace.RegisterItemCollection(connectionWorkspace.GetItemCollection(DataSpace.SSpace));
1615                 MetadataWorkspace.RegisterItemCollection(connectionWorkspace.GetItemCollection(DataSpace.CSSpace));
1616             }
1617         }
1618
1619         #endregion
1620
1621         /// <summary>
1622         /// Creates an ObjectQuery<typeparamref name="T"/> over the store, ready to be executed.
1623         /// </summary>
1624         /// <typeparam name="T">type of the query result</typeparam>
1625         /// <param name="queryString">the query string to be executed</param>
1626         /// <param name="parameters">parameters to pass to the query</param>
1627         /// <returns>an ObjectQuery instance, ready to be executed</returns>
1628         public ObjectQuery<T> CreateQuery<T>(string queryString, params ObjectParameter[] parameters)
1629         {
1630             EntityUtil.CheckArgumentNull(queryString, "queryString");
1631             EntityUtil.CheckArgumentNull(parameters, "parameters");
1632
1633             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
1634             // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
1635             // We either auto-load <T>'s assembly into the ObjectItemCollection or we auto-load the user's calling assembly and its referenced assemblies.
1636             // If the entities in the user's result spans multiple assemblies, the user must manually call LoadFromAssembly.
1637             // *GetCallingAssembly returns the assembly of the method that invoked the currently executing method.
1638             MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(T), System.Reflection.Assembly.GetCallingAssembly());
1639
1640             // create a ObjectQuery<T> with default settings
1641             ObjectQuery<T> query = new ObjectQuery<T>(queryString, this, MergeOption.AppendOnly);
1642
1643             foreach (ObjectParameter parameter in parameters)
1644             {
1645                 query.Parameters.Add(parameter);
1646             }
1647
1648             return query;
1649         }
1650         /// <summary>
1651         /// Creates an EntityConnection from the given connection string.
1652         /// </summary>
1653         /// <param name="connectionString">the connection string</param>
1654         /// <returns>the newly created connection</returns>
1655         [ResourceExposure(ResourceScope.Machine)] //Exposes the file names as part of ConnectionString which are a Machine resource
1656         [ResourceConsumption(ResourceScope.Machine)] //For EntityConnection constructor. But the paths are not created in this method.
1657         private static EntityConnection CreateEntityConnection(string connectionString)
1658         {
1659             EntityUtil.CheckStringArgument(connectionString, "connectionString");
1660
1661             // create the connection
1662             EntityConnection connection = new EntityConnection(connectionString);
1663
1664             return connection;
1665         }
1666         /// <summary>
1667         /// Given an entity connection, returns a copy of its MetadataWorkspace. Ensure we get
1668         /// all of the metadata item collections by priming the entity connection.
1669         /// </summary>
1670         /// <returns></returns>
1671         /// <exception cref="ObjectDisposedException">If the <see cref="ObjectContext"/> instance has been disposed.</exception>
1672         private MetadataWorkspace RetrieveMetadataWorkspaceFromConnection()
1673         {
1674             if (_connection == null)
1675             {
1676                 throw EntityUtil.ObjectContextDisposed();
1677             }
1678
1679             MetadataWorkspace connectionWorkspace = _connection.GetMetadataWorkspace(false /* initializeAllConnections */);
1680             Debug.Assert(connectionWorkspace != null, "EntityConnection.MetadataWorkspace is null.");
1681
1682             // Create our own workspace
1683             MetadataWorkspace workspace = connectionWorkspace.ShallowCopy();
1684
1685             return workspace;
1686         }
1687         /// <summary>
1688         /// Marks an object for deletion from the cache.
1689         /// </summary>
1690         /// <param name="entity">Object to be deleted.</param>
1691         public void DeleteObject(object entity)
1692         {
1693             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1694             // This method and ObjectSet.DeleteObject are expected to have identical behavior except for the extra validation ObjectSet
1695             // requests by passing a non-null expectedEntitySetName. Any changes to this method are expected to be made in the common
1696             // internal overload below that ObjectSet also uses, unless there is a specific reason why a behavior is desired when the
1697             // call comes from ObjectContext only.
1698             DeleteObject(entity, null /*expectedEntitySetName*/);
1699             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1700         }
1701
1702         /// <summary>
1703         /// Common DeleteObject method that is used by both ObjectContext.DeleteObject and ObjectSet.DeleteObject.
1704         /// </summary>
1705         /// <param name="entity">Object to be deleted.</param>
1706         /// <param name="expectedEntitySet">
1707         /// EntitySet that the specified object is expected to be in. Null if the caller doesn't want to validate against a particular EntitySet.
1708         /// </param>
1709         internal void DeleteObject(object entity, EntitySet expectedEntitySet)
1710         {
1711             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
1712             EntityUtil.CheckArgumentNull(entity, "entity");
1713
1714             EntityEntry cacheEntry = this.ObjectStateManager.FindEntityEntry(entity);
1715             if (cacheEntry == null || !object.ReferenceEquals(cacheEntry.Entity, entity))
1716             {
1717                 throw EntityUtil.CannotDeleteEntityNotInObjectStateManager();
1718             }
1719
1720             if (expectedEntitySet != null)
1721             {
1722                 EntitySetBase actualEntitySet = cacheEntry.EntitySet;
1723                 if (actualEntitySet != expectedEntitySet)
1724                 {
1725                     throw EntityUtil.EntityNotInObjectSet_Delete(actualEntitySet.EntityContainer.Name, actualEntitySet.Name, expectedEntitySet.EntityContainer.Name, expectedEntitySet.Name);
1726                 }
1727             }
1728
1729             cacheEntry.Delete();
1730             // Detaching from the context happens when the object
1731             // actually detaches from the cache (not just when it is
1732             // marked for deletion).
1733         }
1734
1735         /// <summary>
1736         /// Detach entity from the cache.
1737         /// </summary>
1738         /// <param name="entity">Object to be detached.</param>
1739         public void Detach(object entity)
1740         {
1741             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1742             // This method and ObjectSet.DetachObject are expected to have identical behavior except for the extra validation ObjectSet
1743             // requests by passing a non-null expectedEntitySetName. Any changes to this method are expected to be made in the common
1744             // internal overload below that ObjectSet also uses, unless there is a specific reason why a behavior is desired when the
1745             // call comes from ObjectContext only.
1746             Detach(entity, null /*expectedEntitySet*/);
1747             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1748         }
1749
1750         /// <summary>
1751         /// Common Detach method that is used by both ObjectContext.Detach and ObjectSet.Detach.
1752         /// </summary>
1753         /// <param name="entity">Object to be detached.</param>
1754         /// <param name="expectedEntitySet">
1755         /// EntitySet that the specified object is expected to be in. Null if the caller doesn't want to validate against a particular EntitySet.
1756         /// </param>        
1757         internal void Detach(object entity, EntitySet expectedEntitySet)
1758         {
1759             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
1760             EntityUtil.CheckArgumentNull(entity, "entity");
1761
1762             EntityEntry cacheEntry = this.ObjectStateManager.FindEntityEntry(entity);
1763             if (cacheEntry == null ||
1764                 !object.ReferenceEquals(cacheEntry.Entity, entity) ||
1765                 cacheEntry.Entity == null) // this condition includes key entries and relationship entries
1766             {
1767                 throw EntityUtil.CannotDetachEntityNotInObjectStateManager();
1768             }
1769
1770             if (expectedEntitySet != null)
1771             {
1772                 EntitySetBase actualEntitySet = cacheEntry.EntitySet;
1773                 if (actualEntitySet != expectedEntitySet)
1774                 {
1775                     throw EntityUtil.EntityNotInObjectSet_Detach(actualEntitySet.EntityContainer.Name, actualEntitySet.Name, expectedEntitySet.EntityContainer.Name, expectedEntitySet.Name);
1776                 }
1777             }
1778
1779             cacheEntry.Detach();
1780         }
1781
1782         /// <summary>
1783         /// Disposes this ObjectContext.
1784         /// </summary>
1785         public void Dispose()
1786         {
1787             // Technically, calling GC.SuppressFinalize is not required because the class does not
1788             // have a finalizer, but it does no harm, protects against the case where a finalizer is added
1789             // in the future, and prevents an FxCop warning.
1790             GC.SuppressFinalize(this);
1791             Dispose(true);
1792         }
1793
1794         /// <summary>
1795         /// Disposes this ObjectContext.
1796         /// </summary>
1797         /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
1798         protected virtual void Dispose(bool disposing)
1799         {
1800             if (disposing)
1801             {
1802                 // Release managed resources here.
1803
1804                 if (_connection != null)
1805                 {
1806                     _connection.StateChange -= ConnectionStateChange;
1807
1808                     // Dispose the connection the ObjectContext created
1809                     if (_createdConnection)
1810                     {
1811                         _connection.Dispose();
1812                     }
1813                 }
1814                 _connection = null; // Marks this object as disposed.
1815                 _adapter = null;
1816                 if (_cache != null)
1817                 {
1818                     _cache.Dispose();
1819                 }
1820             }
1821             // Release unmanaged resources here (none for this class).
1822         }
1823
1824         #region GetEntitySet
1825
1826         /// <summary>
1827         /// Returns the EntitySet with the given name from given container.
1828         /// </summary>
1829         /// <param name="entitySetName">name of entity set</param>
1830         /// <param name="entityContainerName">name of container</param>
1831         /// <returns>the appropriate EntitySet</returns>
1832         /// <exception cref="InvalidOperationException">the entity set could not be found for the given name</exception>
1833         /// <exception cref="InvalidOperationException">the entity container could not be found for the given name</exception>
1834         internal EntitySet GetEntitySet(string entitySetName, string entityContainerName)
1835         {
1836             Debug.Assert(entitySetName != null, "entitySetName should be not null");
1837
1838             EntityContainer container = null;
1839
1840             if (String.IsNullOrEmpty(entityContainerName))
1841             {
1842                 container = this.Perspective.GetDefaultContainer();
1843                 Debug.Assert(container != null, "Problem with metadata - default container not found");
1844             }
1845             else
1846             {
1847                 if (!this.MetadataWorkspace.TryGetEntityContainer(entityContainerName, DataSpace.CSpace, out container))
1848                 {
1849                     throw EntityUtil.EntityContainterNotFoundForName(entityContainerName);
1850                 }
1851             }
1852
1853             EntitySet entitySet = null;
1854
1855             if (!container.TryGetEntitySetByName(entitySetName, false, out entitySet))
1856             {
1857                 throw EntityUtil.EntitySetNotFoundForName(TypeHelpers.GetFullName(container.Name, entitySetName));
1858             }
1859
1860             return entitySet;
1861         }
1862
1863         private static void GetEntitySetName(string qualifiedName, string parameterName, ObjectContext context, out  string entityset, out string container)
1864         {
1865             entityset = null;
1866             container = null;
1867             EntityUtil.CheckStringArgument(qualifiedName, parameterName);
1868
1869             string[] result = qualifiedName.Split('.');
1870             if (result.Length > 2)
1871             {
1872                 throw EntityUtil.QualfiedEntitySetName(parameterName);
1873             }
1874             if (result.Length == 1) // if not '.' at all
1875             {
1876                 entityset = result[0];
1877             }
1878             else
1879             {
1880                 container = result[0];
1881                 entityset = result[1];
1882                 if (container == null || container.Length == 0) // if it starts with '.'
1883                 {
1884                     throw EntityUtil.QualfiedEntitySetName(parameterName);
1885                 }
1886             }
1887             if (entityset == null || entityset.Length == 0) // if it's not in the form "ES name . containername"
1888             {
1889                 throw EntityUtil.QualfiedEntitySetName(parameterName);
1890             }
1891
1892             if (context != null && String.IsNullOrEmpty(container) && (context.Perspective.GetDefaultContainer() == null))
1893             {
1894                 throw EntityUtil.ContainerQualifiedEntitySetNameRequired(parameterName);
1895             }
1896         }
1897
1898         /// <summary>
1899         /// Validate that an EntitySet is compatible with a given entity instance's CLR type.
1900         /// </summary>
1901         /// <param name="entitySet">an EntitySet</param>
1902         /// <param name="entityType">The CLR type of an entity instance</param>
1903         private void ValidateEntitySet(EntitySet entitySet, Type entityType)
1904         {
1905             TypeUsage entityTypeUsage = GetTypeUsage(entityType);
1906             if (!entitySet.ElementType.IsAssignableFrom(entityTypeUsage.EdmType))
1907             {
1908                 throw EntityUtil.InvalidEntitySetOnEntity(entitySet.Name, entityType, "entity");
1909             }
1910         }
1911
1912         internal TypeUsage GetTypeUsage(Type entityCLRType)
1913         {
1914             // Register the assembly so the type information will be sure to be loaded in metadata
1915             this.MetadataWorkspace.ImplicitLoadAssemblyForType(entityCLRType, System.Reflection.Assembly.GetCallingAssembly());
1916
1917             TypeUsage entityTypeUsage = null;
1918             if (!this.Perspective.TryGetType(entityCLRType, out entityTypeUsage) ||
1919                 !TypeSemantics.IsEntityType(entityTypeUsage))
1920             {
1921                 throw EntityUtil.InvalidEntityType(entityCLRType);
1922             }
1923             Debug.Assert(entityTypeUsage != null, "entityTypeUsage is null");
1924             return entityTypeUsage;
1925         }
1926         #endregion
1927
1928         /// <summary>
1929         /// Retrieves an object from the cache if present or from the
1930         /// store if not.
1931         /// </summary>
1932         /// <param name="key">Key of the object to be found.</param>
1933         /// <returns>Entity object.</returns>
1934         public object GetObjectByKey(EntityKey key)
1935         {
1936             EntityUtil.CheckArgumentNull(key, "key");
1937
1938             EntitySet entitySet = key.GetEntitySet(this.MetadataWorkspace);
1939             Debug.Assert(entitySet != null, "Key's EntitySet should not be null in the MetadataWorkspace");
1940
1941             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
1942             // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
1943             // Either the entity type's assembly is already in the ObjectItemCollection or we auto-load the user's calling assembly and its referenced assemblies.
1944             // *GetCallingAssembly returns the assembly of the method that invoked the currently executing method.
1945             MetadataWorkspace.ImplicitLoadFromEntityType(entitySet.ElementType, System.Reflection.Assembly.GetCallingAssembly());
1946
1947             object entity;
1948             if (!TryGetObjectByKey(key, out entity))
1949             {
1950                 throw EntityUtil.ObjectNotFound();
1951             }
1952             return entity;
1953         }
1954
1955         #region Refresh
1956         /// <summary>
1957         /// Refreshing cache data with store data for specific entities.
1958         /// The order in which entites are refreshed is non-deterministic.
1959         /// </summary>
1960         /// <param name="refreshMode">Determines how the entity retrieved from the store is merged with the entity in the cache</param>
1961         /// <param name="collection">must not be null and all entities must be attached to this context. May be empty.</param>
1962         /// <exception cref="ArgumentOutOfRangeException">if refreshMode is not valid</exception>
1963         /// <exception cref="ArgumentNullException">collection is null</exception>
1964         /// <exception cref="ArgumentException">collection contains null or non entities or entities not attached to this context</exception>
1965         public void Refresh(RefreshMode refreshMode, IEnumerable collection)
1966         {
1967             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1968             try
1969             {
1970                 EntityUtil.CheckArgumentRefreshMode(refreshMode);
1971                 EntityUtil.CheckArgumentNull(collection, "collection");
1972
1973                 // collection may not contain any entities -- this is valid for this overload
1974                 RefreshEntities(refreshMode, collection);
1975             }
1976             finally
1977             {
1978                 ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1979             }
1980         }
1981         /// <summary>
1982         /// Refreshing cache data with store data for a specific entity.
1983         /// </summary>
1984         /// <param name="refreshMode">Determines how the entity retrieved from the store is merged with the entity in the cache</param>
1985         /// <param name="entity">The entity to refresh. This must be a non-null entity that is attached to this context</param>
1986         /// <exception cref="ArgumentOutOfRangeException">if refreshMode is not valid</exception>
1987         /// <exception cref="ArgumentNullException">entity is null</exception>
1988         /// <exception cref="ArgumentException">entity is not attached to this context</exception>
1989         public void Refresh(RefreshMode refreshMode, object entity)
1990         {
1991             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
1992             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
1993             try
1994             {
1995                 EntityUtil.CheckArgumentRefreshMode(refreshMode);
1996                 EntityUtil.CheckArgumentNull(entity, "entity");
1997
1998                 RefreshEntities(refreshMode, new object[] { entity });
1999             }
2000             finally
2001             {
2002                 ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
2003             }
2004         }
2005
2006         /// <summary>
2007         /// Validates that the given entity/key pair has an ObjectStateEntry
2008         /// and that entry is not in the added state.
2009         /// 
2010         /// The entity is added to the entities dictionary, and checked for duplicates.
2011         /// </summary>
2012         /// <param name="entities">on exit, entity is added to this dictionary.</param>
2013         /// <param name="entity">An object reference that is not "Added," has an ObjectStateEntry and is not in the entities list.</param>
2014         /// <param name="key"></param>
2015         private void RefreshCheck(
2016             Dictionary<EntityKey, EntityEntry> entities,
2017             object entity, EntityKey key)
2018         {
2019             Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
2020             Debug.Assert(entity != null, "The entity is null.");
2021
2022             EntityEntry entry = ObjectStateManager.FindEntityEntry(key);
2023             if (null == entry)
2024             {
2025                 throw EntityUtil.NthElementNotInObjectStateManager(entities.Count);
2026             }
2027             if (EntityState.Added == entry.State)
2028             {
2029                 throw EntityUtil.NthElementInAddedState(entities.Count);
2030             }
2031             Debug.Assert(EntityState.Added != entry.State, "not expecting added");
2032             Debug.Assert(EntityState.Detached != entry.State, "not expecting detached");
2033
2034             try
2035             {
2036                 entities.Add(key, entry); // don't ignore duplicates
2037             }
2038             catch (ArgumentException)
2039             {
2040                 throw EntityUtil.NthElementIsDuplicate(entities.Count);
2041             }
2042
2043             Debug.Assert(null != entity, "null entity");
2044             Debug.Assert(null != (object)key, "null entity.Key");
2045             Debug.Assert(null != key.EntitySetName, "null entity.Key.EntitySetName");
2046         }
2047
2048         private void RefreshEntities(RefreshMode refreshMode, IEnumerable collection)
2049         {
2050             // refreshMode and collection should already be validated prior to this call -- collection can be empty in one Refresh overload
2051             // but not in the other, so we need to do that check before we get to this common method
2052             Debug.Assert(collection != null, "collection may not contain any entities but should never be null");
2053
2054             bool openedConnection = false;
2055
2056             try
2057             {
2058                 Dictionary<EntityKey, EntityEntry> entities = new Dictionary<EntityKey, EntityEntry>(RefreshEntitiesSize(collection));
2059
2060                 #region 1) Validate and bucket the entities by entity set
2061                 Dictionary<EntitySet, List<EntityKey>> refreshKeys = new Dictionary<EntitySet, List<EntityKey>>();
2062                 foreach (object entity in collection) // anything other than object risks InvalidCastException
2063                 {
2064                     AddRefreshKey(entity, entities, refreshKeys);
2065                 }
2066
2067                 // The collection is no longer required at this point.
2068                 collection = null;
2069                 #endregion
2070
2071                 #region 2) build and execute the query for each set of entities
2072                 if (refreshKeys.Count > 0)
2073                 {
2074                     EnsureConnection();
2075                     openedConnection = true;
2076
2077                     // All entities from a single set can potentially be refreshed in the same query.
2078                     // However, the refresh operations are batched in an attempt to avoid the generation
2079                     // of query trees or provider SQL that exhaust available client or server resources.
2080                     foreach (EntitySet targetSet in refreshKeys.Keys)
2081                     {
2082                         List<EntityKey> setKeys = refreshKeys[targetSet];
2083                         int refreshedCount = 0;
2084                         while (refreshedCount < setKeys.Count)
2085                         {
2086                             refreshedCount = BatchRefreshEntitiesByKey(refreshMode, entities, targetSet, setKeys, refreshedCount);
2087                         }
2088                     }
2089                 }
2090
2091                 // The refreshKeys list is no longer required at this point.
2092                 refreshKeys = null;
2093                 #endregion
2094
2095                 #region 3) process the unrefreshed entities
2096                 if (RefreshMode.StoreWins == refreshMode)
2097                 {
2098                     // remove all entites that have been removed from the store, not added by client
2099                     foreach (KeyValuePair<EntityKey, EntityEntry> item in entities)
2100                     {
2101                         Debug.Assert(EntityState.Added != item.Value.State, "should not be possible");
2102                         if (EntityState.Detached != item.Value.State)
2103                         {
2104                             // We set the detaching flag here even though we are deleting because we are doing a
2105                             // Delete/AcceptChanges cycle to simulate a Detach, but we can't use Detach directly
2106                             // because legacy behavior around cascade deletes should be preserved.  However, we
2107                             // do want to prevent FK values in dependents from being nulled, which is why we
2108                             // need to set the detaching flag.
2109                             ObjectStateManager.TransactionManager.BeginDetaching();
2110                             try
2111                             {
2112                                 item.Value.Delete();
2113                             }
2114                             finally
2115                             {
2116                                 ObjectStateManager.TransactionManager.EndDetaching();
2117                             }
2118                             Debug.Assert(EntityState.Detached != item.Value.State, "not expecting detached");
2119
2120                             item.Value.AcceptChanges();
2121                         }
2122                     }
2123                 }
2124                 else if ((RefreshMode.ClientWins == refreshMode) && (0 < entities.Count))
2125                 {
2126                     // throw an exception with all appropriate entity keys in text
2127                     string prefix = String.Empty;
2128                     StringBuilder builder = new StringBuilder();
2129                     foreach (KeyValuePair<EntityKey, EntityEntry> item in entities)
2130                     {
2131                         Debug.Assert(EntityState.Added != item.Value.State, "should not be possible");
2132                         if (item.Value.State == EntityState.Deleted)
2133                         {
2134                             // Detach the deleted items because this is the client changes and the server
2135                             // does not have these items any more
2136                             item.Value.AcceptChanges();
2137                         }
2138                         else
2139                         {
2140                             builder.Append(prefix).Append(Environment.NewLine);
2141                             builder.Append('\'').Append(item.Key.ConcatKeyValue()).Append('\'');
2142                             prefix = ",";
2143                         }
2144                     }
2145                     // If there were items that could not be found, throw an exception
2146                     if (builder.Length > 0)
2147                     {
2148                         throw EntityUtil.ClientEntityRemovedFromStore(builder.ToString());
2149                     }
2150                 }
2151                 #endregion
2152             }
2153             finally
2154             {
2155                 if (openedConnection)
2156                 {
2157                     ReleaseConnection();
2158                 }
2159             }
2160         }
2161
2162         private int BatchRefreshEntitiesByKey(RefreshMode refreshMode, Dictionary<EntityKey, EntityEntry> trackedEntities, EntitySet targetSet, List<EntityKey> targetKeys, int startFrom)
2163         {
2164             //
2165             // A single refresh query can be built for all entities from the same set.
2166             // For each entity set, a DbFilterExpression is constructed that
2167             // expresses the equivalent of:
2168             //
2169             // SELECT VALUE e
2170             // FROM <entityset> AS e
2171             // WHERE
2172             // GetRefKey(GetEntityRef(e)) == <ref1>.KeyValues
2173             // [OR GetRefKey(GetEntityRef(e)) == <ref2>.KeyValues
2174             // [..OR GetRefKey(GetEntityRef(e)) == <refN>.KeyValues]]
2175             //
2176             // Note that a LambdaFunctionExpression is used so that instead
2177             // of repeating GetRefKey(GetEntityRef(e)) a VariableReferenceExpression
2178             // to a Lambda argument with the value GetRefKey(GetEntityRef(e)) is used instead.
2179             // The query is therefore logically equivalent to:
2180             //
2181             // SELECT VALUE e
2182             // FROM <entityset> AS e
2183             // WHERE
2184             //   LET(x = GetRefKey(GetEntityRef(e)) IN (
2185             //      x == <ref1>.KeyValues
2186             //     [OR x == <ref2>.KeyValues
2187             //     [..OR x == <refN>.KeyValues]]
2188             //   )
2189             //
2190
2191             // The batch size determines the maximum depth of the predicate OR tree and
2192             // also limits the size of the generated provider SQL that is sent to the server.
2193             const int maxBatch = 250;
2194
2195             // Bind the target EntitySet under the name "EntitySet".
2196             DbExpressionBinding entitySetBinding = targetSet.Scan().BindAs("EntitySet");
2197
2198             // Use the variable from the set binding as the 'e' in a new GetRefKey(GetEntityRef(e)) expression.
2199             DbExpression sourceEntityKey = entitySetBinding.Variable.GetEntityRef().GetRefKey();
2200
2201             // Build the where predicate as described above. A maximum of <batchsize> entity keys will be included
2202             // in the predicate, starting from position <startFrom> in the list of entity keys. As each key is
2203             // included, both <batchsize> and <startFrom> are incremented to ensure that the batch size is
2204             // correctly constrained and that the new starting position for the next call to this method is calculated.
2205             int batchSize = Math.Min(maxBatch, (targetKeys.Count - startFrom));
2206             DbExpression[] keyFilters = new DbExpression[batchSize];
2207             for (int idx = 0; idx < batchSize; idx++)
2208             {
2209                 // Create a row constructor expression based on the key values of the EntityKey.
2210                 KeyValuePair<string, DbExpression>[] keyValueColumns = targetKeys[startFrom++].GetKeyValueExpressions(targetSet);
2211                 DbExpression keyFilter = DbExpressionBuilder.NewRow(keyValueColumns);
2212
2213                 // Create an equality comparison between the row constructor and the lambda variable
2214                 // that refers to GetRefKey(GetEntityRef(e)), which also produces a row
2215                 // containing key values, but for the current entity from the entity set.
2216                 keyFilters[idx] = sourceEntityKey.Equal(keyFilter);
2217             }
2218
2219             // Sanity check that the batch includes at least one element.
2220             Debug.Assert(batchSize > 0, "Didn't create a refresh expression?");
2221
2222             // Build a balanced binary tree that OR's the key filters together.
2223             DbExpression entitySetFilter = Helpers.BuildBalancedTreeInPlace(keyFilters, DbExpressionBuilder.Or);
2224
2225             // Create a FilterExpression based on the EntitySet binding and the Lambda predicate.
2226             // This FilterExpression encapsulated the logic required for the refresh query as described above.
2227             DbExpression refreshQuery = entitySetBinding.Filter(entitySetFilter);
2228
2229             // Initialize the command tree used to issue the refresh query.
2230             DbQueryCommandTree tree = DbQueryCommandTree.FromValidExpression(this.MetadataWorkspace, DataSpace.CSpace, refreshQuery);
2231
2232             // Evaluate the refresh query using ObjectQuery<T> and process the results to update the ObjectStateManager.
2233             MergeOption mergeOption = (RefreshMode.StoreWins == refreshMode ?
2234                                        MergeOption.OverwriteChanges :
2235                                        MergeOption.PreserveChanges);
2236
2237             // The connection will be released by ObjectResult when enumeration is complete.
2238             this.EnsureConnection();
2239
2240             try
2241             {
2242                 ObjectResult<object> results = ObjectQueryExecutionPlan.ExecuteCommandTree<object>(this, tree, mergeOption);
2243
2244                 foreach (object entity in results)
2245                 {
2246                     // There is a risk that, during an event, the Entity removed itself from the cache.
2247                     EntityEntry entry = ObjectStateManager.FindEntityEntry(entity);
2248                     if ((null != entry) && (EntityState.Modified == entry.State))
2249                     {   // this is 'ForceChanges' - which is the same as PreserveChanges, except all properties are marked modified.
2250                         Debug.Assert(RefreshMode.ClientWins == refreshMode, "StoreWins always becomes unchanged");
2251                         entry.SetModifiedAll();
2252                     }
2253
2254                     IEntityWrapper wrappedEntity = EntityWrapperFactory.WrapEntityUsingContext(entity, this);
2255                     EntityKey key = wrappedEntity.EntityKey;
2256                     EntityUtil.CheckEntityKeyNull(key);
2257
2258                     // Dev10#673631 - An incorrectly returned entity should result in an exception to avoid further corruption to the OSM.
2259                     if (!trackedEntities.Remove(key))
2260                     {
2261                         throw EntityUtil.StoreEntityNotPresentInClient();
2262                     }
2263                 }
2264             }
2265             catch
2266             {
2267                 // Enumeration did not complete, so the connection must be explicitly released.
2268                 this.ReleaseConnection();
2269                 throw;
2270             }
2271
2272             // Return the position in the list from which the next refresh operation should start.
2273             // This will be equal to the list count if all remaining entities in the list were
2274             // refreshed during this call.
2275             return startFrom;
2276         }
2277
2278         private static int RefreshEntitiesSize(IEnumerable collection)
2279         {
2280             ICollection list = collection as ICollection;
2281             return ((null != list) ? list.Count : 0);
2282         }
2283         #endregion
2284
2285         #region SaveChanges
2286         /// <summary>
2287         /// Persists all updates to the store.
2288         /// </summary>
2289         /// <returns>
2290         ///   the number of dirty (i.e., Added, Modified, or Deleted) ObjectStateEntries
2291         ///   in the ObjectStateManager when SaveChanges was called.
2292         /// </returns>
2293         public Int32 SaveChanges()
2294         {
2295             return SaveChanges(SaveOptions.DetectChangesBeforeSave | SaveOptions.AcceptAllChangesAfterSave);
2296         }
2297
2298
2299         /// <summary>
2300         ///   Persists all updates to the store.
2301         ///   This API is obsolete.  Please use SaveChanges(SaveOptions options) instead.
2302         ///   SaveChanges(true) is equivalent to SaveChanges() -- That is it detects changes and
2303         ///      accepts all changes after save.
2304         ///   SaveChanges(false) detects changes but does not accept changes after save.
2305         /// </summary>
2306         /// <param name="acceptChangesDuringSave">if false, user must call AcceptAllChanges</param>/>
2307         /// <returns>
2308         ///   the number of dirty (i.e., Added, Modified, or Deleted) ObjectStateEntries
2309         ///   in the ObjectStateManager when SaveChanges was called.
2310         /// </returns>
2311         [EditorBrowsable(EditorBrowsableState.Never)]
2312         [Browsable(false)]
2313         [Obsolete("Use SaveChanges(SaveOptions options) instead.")]
2314         public Int32 SaveChanges(bool acceptChangesDuringSave)
2315         {
2316             return this.SaveChanges(acceptChangesDuringSave ? SaveOptions.DetectChangesBeforeSave | SaveOptions.AcceptAllChangesAfterSave
2317                                                             : SaveOptions.DetectChangesBeforeSave);
2318         }
2319
2320         /// <summary>
2321         /// Persists all updates to the store.
2322         /// </summary>
2323         /// <param name="options">describes behavior options of SaveChanges</param>
2324         /// <returns>
2325         ///   the number of dirty (i.e., Added, Modified, or Deleted) ObjectStateEntries
2326         ///   in the ObjectStateManager processed by SaveChanges.
2327         /// </returns>
2328         public virtual Int32 SaveChanges(SaveOptions options)
2329         {
2330             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
2331
2332             OnSavingChanges();
2333
2334             if ((SaveOptions.DetectChangesBeforeSave & options) != 0)
2335             {
2336                 this.ObjectStateManager.DetectChanges();
2337             }
2338
2339             if (ObjectStateManager.SomeEntryWithConceptualNullExists())
2340             {
2341                 throw new InvalidOperationException(Strings.ObjectContext_CommitWithConceptualNull);
2342             }
2343
2344             bool mustReleaseConnection = false;
2345             Int32 entriesAffected = ObjectStateManager.GetObjectStateEntriesCount(EntityState.Added | EntityState.Deleted | EntityState.Modified);
2346             EntityConnection connection = (EntityConnection)Connection;
2347
2348             if (0 < entriesAffected)
2349             {   // else fast exit if no changes to save to avoids interacting with or starting of new transactions
2350
2351                 // get data adapter
2352                 if (_adapter == null)
2353                 {
2354                     IServiceProvider sp = DbProviderFactories.GetFactory(connection) as IServiceProvider;
2355                     if (sp != null)
2356                     {
2357                         _adapter = sp.GetService(typeof(IEntityAdapter)) as IEntityAdapter;
2358                     }
2359                     if (_adapter == null)
2360                     {
2361                         throw EntityUtil.InvalidDataAdapter();
2362                     }
2363                 }
2364                 // only accept changes after the local transaction commits
2365                 _adapter.AcceptChangesDuringUpdate = false;
2366                 _adapter.Connection = connection;
2367                 _adapter.CommandTimeout = this.CommandTimeout;
2368
2369                 try
2370                 {
2371                     EnsureConnection();
2372                     mustReleaseConnection = true;
2373
2374                     // determine what transaction to enlist in
2375                     bool needLocalTransaction = false;
2376
2377                     if (null == connection.CurrentTransaction && !connection.EnlistedInUserTransaction)
2378                     {
2379                         // If there isn't a local transaction started by the user, we'll attempt to enlist 
2380                         // on the current SysTx transaction so we don't need to construct a local
2381                         // transaction.
2382
2383                         needLocalTransaction = (null == _lastTransaction);
2384                     }
2385                     // else the user already has his own local transaction going; user will do the abort or commit.
2386
2387                     DbTransaction localTransaction = null;
2388                     try
2389                     {
2390                         // EntityConnection tracks the CurrentTransaction we don't need to pass it around
2391                         if (needLocalTransaction)
2392                         {
2393                             localTransaction = connection.BeginTransaction();
2394                         }
2395                         entriesAffected = _adapter.Update(ObjectStateManager);
2396
2397                         if (null != localTransaction)
2398                         {   // we started the local transaction; so we also commit it
2399                             localTransaction.Commit();
2400                         }
2401                         // else on success with no exception is thrown, user generally commits the transaction
2402                     }
2403                     finally
2404                     {
2405                         if (null != localTransaction)
2406                         {   // we started the local transaction; so it requires disposal (rollback if not previously committed
2407                             localTransaction.Dispose();
2408                         }
2409                         // else on failure with an exception being thrown, user generally aborts (default action with transaction without an explict commit)
2410                     }
2411                 }
2412                 finally
2413                 {
2414                     if (mustReleaseConnection)
2415                     {
2416                         // Release the connection when we are done with the save
2417                         ReleaseConnection();
2418                     }
2419                 }
2420
2421                 if ((SaveOptions.AcceptAllChangesAfterSave & options) != 0)
2422                 {   // only accept changes after the local transaction commits
2423
2424                     try
2425                     {
2426                         AcceptAllChanges();
2427                     }
2428                     catch (Exception e)
2429                     {
2430                         // If AcceptAllChanges throw - let's inform user that changes in database were committed 
2431                         // and that Context and Database can be in inconsistent state.
2432
2433                         // We should not be wrapping all exceptions
2434                         if (EntityUtil.IsCatchableExceptionType(e))
2435                         {
2436                             throw EntityUtil.AcceptAllChangesFailure(e);
2437                         }
2438                         throw;
2439                     }
2440                 }
2441             }
2442             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
2443             return entriesAffected;
2444         }
2445         #endregion
2446
2447         /// <summary>
2448         /// For every tracked entity which doesn't implement IEntityWithChangeTracker detect changes in the entity's property values
2449         /// and marks appropriate ObjectStateEntry as Modified.
2450         /// For every tracked entity which doesn't implement IEntityWithRelationships detect changes in its relationships.
2451         /// 
2452         /// The method is used inter----ly by ObjectContext.SaveChanges() but can be also used if user wants to detect changes 
2453         /// and have ObjectStateEntries in appropriate state before the SaveChanges() method is called.
2454         /// </summary>
2455         public void DetectChanges()
2456         {
2457             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
2458             this.ObjectStateManager.DetectChanges();
2459             ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
2460         }
2461
2462         /// <summary>
2463         /// Attempts to retrieve an object from the cache or the store.
2464         /// </summary>
2465         /// <param name="key">Key of the object to be found.</param>
2466         /// <param name="value">Out param for the object.</param>
2467         /// <returns>True if the object was found, false otherwise.</returns>
2468         public bool TryGetObjectByKey(EntityKey key, out object value)
2469         {
2470             // try the cache first
2471             EntityEntry entry;
2472             ObjectStateManager.TryGetEntityEntry(key, out entry); // this will check key argument
2473             if (entry != null)
2474             {
2475                 // can't find keys
2476                 if (!entry.IsKeyEntry)
2477                 {
2478                     // SQLBUDT 511296 returning deleted object.
2479                     value = entry.Entity;
2480                     return value != null;
2481                 }
2482             }
2483
2484             if (key.IsTemporary)
2485             {
2486                 // If the key is temporary, we cannot find a corresponding object in the store.
2487                 value = null;
2488                 return false;
2489             }
2490
2491             EntitySet entitySet = key.GetEntitySet(this.MetadataWorkspace);
2492             Debug.Assert(entitySet != null, "Key's EntitySet should not be null in the MetadataWorkspace");
2493
2494             // Validate the EntityKey values against the EntitySet
2495             key.ValidateEntityKey(_workspace, entitySet, true /*isArgumentException*/, "key");
2496
2497             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type is loaded into the workspace.
2498             // If the schema types are not loaded: metadata, cache & query would be unable to reason about the type.
2499             // Either the entity type's assembly is already in the ObjectItemCollection or we auto-load the user's calling assembly and its referenced assemblies.
2500             // *GetCallingAssembly returns the assembly of the method that invoked the currently executing method.
2501             MetadataWorkspace.ImplicitLoadFromEntityType(entitySet.ElementType, System.Reflection.Assembly.GetCallingAssembly());
2502
2503             // Execute the query:
2504             // SELECT VALUE X FROM [EC].[ES] AS X
2505             // WHERE X.KeyProp0 = @p0 AND X.KeyProp1 = @p1 AND ... 
2506             // parameters are the key values 
2507
2508             // Build the Entity SQL query
2509             StringBuilder esql = new StringBuilder();
2510             esql.AppendFormat("SELECT VALUE X FROM {0}.{1} AS X WHERE ", EntityUtil.QuoteIdentifier(entitySet.EntityContainer.Name), EntityUtil.QuoteIdentifier(entitySet.Name));
2511             EntityKeyMember[] members = key.EntityKeyValues;
2512             ReadOnlyMetadataCollection<EdmMember> keyMembers = entitySet.ElementType.KeyMembers;
2513             ObjectParameter[] parameters = new ObjectParameter[members.Length];
2514
2515             for (int i = 0; i < members.Length; i++)
2516             {
2517                 if (i > 0)
2518                 {
2519                     esql.Append(" AND ");
2520                 }
2521                 string parameterName = string.Format(CultureInfo.InvariantCulture, "p{0}", i.ToString(CultureInfo.InvariantCulture));
2522                 esql.AppendFormat("X.{0} = @{1}", EntityUtil.QuoteIdentifier(members[i].Key), parameterName);
2523                 parameters[i] = new ObjectParameter(parameterName, members[i].Value);
2524
2525                 // Try to set the TypeUsage on the ObjectParameter
2526                 EdmMember keyMember = null;
2527                 if (keyMembers.TryGetValue(members[i].Key, true, out keyMember))
2528                 {
2529                     parameters[i].TypeUsage = keyMember.TypeUsage;
2530                 }
2531             }
2532
2533             // Execute the query
2534             object entity = null;
2535             ObjectResult<object> results = CreateQuery<object>(esql.ToString(), parameters).Execute(MergeOption.AppendOnly);
2536             foreach (object queriedEntity in results)
2537             {
2538                 Debug.Assert(entity == null, "Query for a key returned more than one entity!");
2539                 entity = queriedEntity;
2540             }
2541
2542             value = entity;
2543             return value != null;
2544         }
2545
2546
2547         /// <summary>
2548         /// Executes the given function on the default container. 
2549         /// </summary>
2550         /// <typeparam name="TElement">Element type for function results.</typeparam>
2551         /// <param name="functionName">Name of function. May include container (e.g. ContainerName.FunctionName)
2552         /// or just function name when DefaultContainerName is known.</param>
2553         /// <param name="parameters"></param>
2554         /// <exception cref="ArgumentException">If function is null or empty</exception>
2555         /// <exception cref="InvalidOperationException">If function is invalid (syntax,
2556         /// does not exist, refers to a function with return type incompatible with T)</exception>
2557         public ObjectResult<TElement> ExecuteFunction<TElement>(string functionName, params ObjectParameter[] parameters)
2558         {
2559             return ExecuteFunction<TElement>(functionName, MergeOption.AppendOnly, parameters);
2560         }
2561
2562         /// <summary>
2563         /// Executes the given function on the default container. 
2564         /// </summary>
2565         /// <typeparam name="TElement">Element type for function results.</typeparam>
2566         /// <param name="functionName">Name of function. May include container (e.g. ContainerName.FunctionName)
2567         /// or just function name when DefaultContainerName is known.</param>
2568         /// <param name="mergeOption"></param>
2569         /// <param name="parameters"></param>
2570         /// <exception cref="ArgumentException">If function is null or empty</exception>
2571         /// <exception cref="InvalidOperationException">If function is invalid (syntax,
2572         /// does not exist, refers to a function with return type incompatible with T)</exception>
2573         public ObjectResult<TElement> ExecuteFunction<TElement>(string functionName, MergeOption mergeOption, params ObjectParameter[] parameters)
2574         {
2575             EntityUtil.CheckStringArgument(functionName, "function");
2576             EntityUtil.CheckArgumentNull(parameters, "parameters");
2577
2578             EdmFunction functionImport;
2579             EntityCommand entityCommand = CreateEntityCommandForFunctionImport(functionName, out functionImport, parameters);
2580             int returnTypeCount = Math.Max(1, functionImport.ReturnParameters.Count);
2581             EdmType[] expectedEdmTypes = new EdmType[returnTypeCount];
2582             expectedEdmTypes[0] = MetadataHelper.GetAndCheckFunctionImportReturnType<TElement>(functionImport, 0, this.MetadataWorkspace);
2583             for (int i = 1; i < returnTypeCount; i++)
2584             {
2585                 if (!MetadataHelper.TryGetFunctionImportReturnType<EdmType>(functionImport, i, out expectedEdmTypes[i]))
2586                 {
2587                     throw EntityUtil.ExecuteFunctionCalledWithNonReaderFunction(functionImport);
2588                 }
2589             }
2590
2591             return CreateFunctionObjectResult<TElement>(entityCommand, functionImport.EntitySets, expectedEdmTypes, mergeOption);
2592         }
2593
2594         /// <summary>
2595         /// Executes the given function on the default container and discard any results returned from the function.
2596         /// </summary>
2597         /// <param name="functionName">Name of function. May include container (e.g. ContainerName.FunctionName)
2598         /// or just function name when DefaultContainerName is known.</param>
2599         /// <param name="parameters"></param>
2600         /// <returns>Number of rows affected</returns>
2601         /// <exception cref="ArgumentException">If function is null or empty</exception>
2602         /// <exception cref="InvalidOperationException">If function is invalid (syntax,
2603         /// does not exist, refers to a function with return type incompatible with T)</exception>
2604         public int ExecuteFunction(string functionName, params ObjectParameter[] parameters)
2605         {
2606             EntityUtil.CheckStringArgument(functionName, "function");
2607             EntityUtil.CheckArgumentNull(parameters, "parameters");
2608
2609             EdmFunction functionImport;
2610             EntityCommand entityCommand = CreateEntityCommandForFunctionImport(functionName, out functionImport, parameters);
2611
2612             EnsureConnection();
2613
2614             // Prepare the command before calling ExecuteNonQuery, so that exceptions thrown during preparation are not wrapped in CommandCompilationException
2615             entityCommand.Prepare();
2616
2617             try
2618             {
2619                 return entityCommand.ExecuteNonQuery();
2620             }
2621             catch (Exception e)
2622             {
2623                 if (EntityUtil.IsCatchableEntityExceptionType(e))
2624                 {
2625                     throw EntityUtil.CommandExecution(System.Data.Entity.Strings.EntityClient_CommandExecutionFailed, e);
2626                 }
2627                 throw;
2628             }
2629             finally
2630             {
2631                 this.ReleaseConnection();
2632             }
2633         }
2634
2635         private EntityCommand CreateEntityCommandForFunctionImport(string functionName, out EdmFunction functionImport, params ObjectParameter[] parameters)
2636         {
2637             for (int i = 0; i < parameters.Length; i++)
2638             {
2639                 ObjectParameter parameter = parameters[i];
2640                 if (null == parameter)
2641                 {
2642                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectContext_ExecuteFunctionCalledWithNullParameter(i));
2643                 }
2644             }
2645
2646             string containerName;
2647             string functionImportName;
2648
2649             functionImport =
2650                 MetadataHelper.GetFunctionImport(
2651                 functionName, this.DefaultContainerName, this.MetadataWorkspace,
2652                 out containerName, out functionImportName);
2653
2654
2655             EntityConnection connection = (EntityConnection)this.Connection;
2656
2657             // create query
2658             EntityCommand entityCommand = new EntityCommand();
2659             entityCommand.CommandType = CommandType.StoredProcedure;
2660             entityCommand.CommandText = containerName + "." + functionImportName;
2661             entityCommand.Connection = connection;
2662             if (this.CommandTimeout.HasValue)
2663             {
2664                 entityCommand.CommandTimeout = this.CommandTimeout.Value;
2665             }
2666
2667             PopulateFunctionImportEntityCommandParameters(parameters, functionImport, entityCommand);
2668
2669             return entityCommand;
2670         }
2671
2672         private ObjectResult<TElement> CreateFunctionObjectResult<TElement>(EntityCommand entityCommand, ReadOnlyMetadataCollection<EntitySet> entitySets, EdmType[] edmTypes, MergeOption mergeOption)
2673         {
2674             Debug.Assert(edmTypes != null && edmTypes.Length > 0);
2675             EnsureConnection();
2676
2677             EntityCommandDefinition commandDefinition = entityCommand.GetCommandDefinition();
2678
2679             // get store data reader
2680             DbDataReader storeReader;
2681             try
2682             {
2683                 storeReader = commandDefinition.ExecuteStoreCommands(entityCommand, CommandBehavior.Default);
2684             }
2685             catch (Exception e)
2686             {
2687                 this.ReleaseConnection();
2688                 if (EntityUtil.IsCatchableEntityExceptionType(e))
2689                 {
2690                     throw EntityUtil.CommandExecution(System.Data.Entity.Strings.EntityClient_CommandExecutionFailed, e);
2691                 }
2692                 throw;
2693             }
2694
2695             return MaterializedDataRecord<TElement>(entityCommand, storeReader, 0, entitySets, edmTypes, mergeOption);
2696         }
2697
2698         /// <summary>
2699         ///  Get the materializer for the resultSetIndexth result set of storeReader.
2700         /// </summary>
2701         internal ObjectResult<TElement> MaterializedDataRecord<TElement>(EntityCommand entityCommand, DbDataReader storeReader, int resultSetIndex, ReadOnlyMetadataCollection<EntitySet> entitySets, EdmType[] edmTypes, MergeOption mergeOption)
2702         {
2703             EntityCommandDefinition commandDefinition = entityCommand.GetCommandDefinition();
2704             try
2705             {
2706                 // We want the shaper to close the reader if it is the last result set.
2707                 bool shaperOwnsReader = edmTypes.Length <= resultSetIndex + 1;
2708                 EdmType edmType = edmTypes[resultSetIndex];
2709
2710                 //Note: Defensive check for historic reasons, we expect entitySets.Count > resultSetIndex 
2711                 EntitySet entitySet = entitySets.Count > resultSetIndex ? entitySets[resultSetIndex] : null;
2712
2713                 // create the shaper
2714                 System.Data.Common.QueryCache.QueryCacheManager cacheManager = this.Perspective.MetadataWorkspace.GetQueryCacheManager();
2715                 ShaperFactory<TElement> shaperFactory = Translator.TranslateColumnMap<TElement>(cacheManager, commandDefinition.CreateColumnMap(storeReader, resultSetIndex), this.MetadataWorkspace, null, mergeOption, false);
2716                 Shaper<TElement> shaper = shaperFactory.Create(storeReader, this, this.MetadataWorkspace, mergeOption, shaperOwnsReader);
2717
2718                 NextResultGenerator nextResultGenerator;
2719
2720                 // We need to run notifications when the data reader is closed in order to propagate any out parameters.
2721                 // We do this whenever the last (declared) result set's enumerator is disposed (this calls Finally on the shaper)
2722                 // or when the underlying reader is closed as a result of the ObjectResult itself getting disposed.   
2723                 // We use onReaderDisposeHasRun to ensure that this notification is only called once.   
2724                 // the alternative approach of not making the final ObjectResult's disposal result do cleanup doesn't work in the case where
2725                 // its GetEnumerator is called explicitly, and the resulting enumerator is never disposed.
2726                 bool onReaderDisposeHasRun = false; 
2727                 Action<object, EventArgs> onReaderDispose = (object sender, EventArgs e) =>
2728                     {
2729                         if (!onReaderDisposeHasRun)
2730                         {
2731                             onReaderDisposeHasRun = true;
2732                             // consume the store reader
2733                             CommandHelper.ConsumeReader(storeReader);
2734                             // trigger event callback
2735                             entityCommand.NotifyDataReaderClosing();
2736                         }
2737                     };
2738
2739                 if (shaperOwnsReader)
2740                 {
2741                     shaper.OnDone += new EventHandler(onReaderDispose);
2742                     nextResultGenerator = null;
2743                 }
2744                 else
2745                 {
2746                     nextResultGenerator = new NextResultGenerator(this, entityCommand, edmTypes, entitySets, mergeOption, resultSetIndex + 1);
2747                 }
2748
2749                 // We want the ObjectResult to close the reader in its Dispose method, even if it is not the last result set.
2750                 // This is to allow users to cancel reading results without the unnecessary iteration thru all the result sets.
2751                 return new ObjectResult<TElement>(shaper, entitySet, TypeUsage.Create(edmTypes[resultSetIndex]), true, nextResultGenerator, onReaderDispose);
2752             }
2753             catch
2754             {
2755                 this.ReleaseConnection();
2756                 storeReader.Dispose();
2757                 throw;
2758             }
2759         }
2760
2761
2762         private void PopulateFunctionImportEntityCommandParameters(ObjectParameter[] parameters, EdmFunction functionImport, EntityCommand command)
2763         {
2764             // attach entity parameters
2765             for (int i = 0; i < parameters.Length; i++)
2766             {
2767                 ObjectParameter objectParameter = parameters[i];
2768                 EntityParameter entityParameter = new EntityParameter();
2769
2770                 FunctionParameter functionParameter = FindParameterMetadata(functionImport, parameters, i);
2771
2772                 if (null != functionParameter)
2773                 {
2774                     entityParameter.Direction = MetadataHelper.ParameterModeToParameterDirection(
2775                        functionParameter.Mode);
2776                     entityParameter.ParameterName = functionParameter.Name;
2777                 }
2778                 else
2779                 {
2780                     entityParameter.ParameterName = objectParameter.Name;
2781                 }
2782
2783                 entityParameter.Value = objectParameter.Value ?? DBNull.Value;
2784
2785                 if (DBNull.Value == entityParameter.Value ||
2786                     entityParameter.Direction != ParameterDirection.Input)
2787                 {
2788                     TypeUsage typeUsage;
2789                     if (functionParameter != null)
2790                     {
2791                         // give precedence to the statically declared type usage
2792                         typeUsage = functionParameter.TypeUsage;
2793                     }
2794                     else if (null == objectParameter.TypeUsage)
2795                     {
2796                         Debug.Assert(objectParameter.MappableType != null, "MappableType must not be null");
2797                         Debug.Assert(Nullable.GetUnderlyingType(objectParameter.MappableType) == null, "Nullable types not expected here.");
2798
2799                         // since ObjectParameters do not allow users to especify 'facets', make 
2800                         // sure that the parameter typeusage is not populated with the provider
2801                         // dafault facet values.
2802                         // Try getting the type from the workspace. This may fail however for one of the following reasons:
2803                         // - the type is not a model type
2804                         // - the types were not loaded into the workspace yet
2805                         // If the types were not loaded into the workspace we try loading types from the assembly the type lives in and re-try
2806                         // loading the type. We don't care if the type still cannot be loaded - in this case the result TypeUsage will be null
2807                         // which we handle later.
2808                         if (!this.Perspective.TryGetTypeByName(objectParameter.MappableType.FullName, /*ignoreCase */ false, out typeUsage))
2809                         {
2810                             this.MetadataWorkspace.ImplicitLoadAssemblyForType(objectParameter.MappableType, null);
2811                             this.Perspective.TryGetTypeByName(objectParameter.MappableType.FullName, /*ignoreCase */ false, out typeUsage);
2812                         }
2813                     }
2814                     else
2815                     {
2816                         typeUsage = objectParameter.TypeUsage;
2817                     }
2818
2819                     // set type information (if the provider cannot determine it from the actual value)
2820                     EntityCommandDefinition.PopulateParameterFromTypeUsage(entityParameter, typeUsage, entityParameter.Direction != ParameterDirection.Input);
2821                 }
2822
2823                 if (entityParameter.Direction != ParameterDirection.Input)
2824                 {
2825                     ParameterBinder binder = new ParameterBinder(entityParameter, objectParameter);
2826                     command.OnDataReaderClosing += new EventHandler(binder.OnDataReaderClosingHandler);
2827                 }
2828                 command.Parameters.Add(entityParameter);
2829             }
2830         }
2831
2832         private static FunctionParameter FindParameterMetadata(EdmFunction functionImport, ObjectParameter[] parameters, int ordinal)
2833         {
2834             // Retrieve parameter information from functionImport.
2835             // We first attempt to resolve by case-sensitive name. If there is no exact match,
2836             // check if there is a case-insensitive match. Case insensitive matches are only permitted
2837             // when a single parameter would match.
2838             FunctionParameter functionParameter;
2839             string parameterName = parameters[ordinal].Name;
2840             if (!functionImport.Parameters.TryGetValue(parameterName, false, out functionParameter))
2841             {
2842                 // if only one parameter has this name, try a case-insensitive lookup
2843                 int matchCount = 0;
2844                 for (int i = 0; i < parameters.Length && matchCount < 2; i++)
2845                 {
2846                     if (StringComparer.OrdinalIgnoreCase.Equals(parameters[i].Name, parameterName))
2847                     {
2848                         matchCount++;
2849                     }
2850                 }
2851                 if (matchCount == 1)
2852                 {
2853                     functionImport.Parameters.TryGetValue(parameterName, true, out functionParameter);
2854                 }
2855             }
2856             return functionParameter;
2857         }
2858
2859         /// <summary>
2860         /// Attempt to generate a proxy type for each type in the supplied enumeration.
2861         /// </summary>
2862         /// <param name="types">
2863         /// Enumeration of Type objects that should correspond to O-Space types.
2864         /// </param>
2865         /// <remarks>
2866         /// Types in the enumeration that do not map to an O-Space type are ignored.
2867         /// Also, there is no guarantee that a proxy type will be created for a given type,
2868         /// only that if a proxy can be generated, then it will be generated.
2869         /// 
2870         /// See <see cref="EntityProxyFactory"/> class for more information about proxy type generation.
2871         /// </remarks>
2872
2873         // Use one of the following methods to retrieve an enumeration of all CLR types mapped to O-Space EntityType objects:
2874         // 
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885         // 
2886
2887         public void CreateProxyTypes(IEnumerable<Type> types)
2888         {
2889             ObjectItemCollection ospaceItems = (ObjectItemCollection)MetadataWorkspace.GetItemCollection(DataSpace.OSpace);
2890
2891             // Ensure metadata is loaded for each type,
2892             // and attempt to create proxy type only for types that have a mapping to an O-Space EntityType.
2893             EntityProxyFactory.TryCreateProxyTypes(
2894                 types.Select(type =>
2895                 {
2896                     // Ensure the assembly containing the entity's CLR type is loaded into the workspace.
2897                     MetadataWorkspace.ImplicitLoadAssemblyForType(type, null);
2898
2899                     EntityType entityType;
2900                     ospaceItems.TryGetItem<EntityType>(type.FullName, out entityType);
2901                     return entityType;
2902                 }).Where(entityType => entityType != null)
2903             );
2904         }
2905
2906         /// <summary>
2907         /// Return an enumerable of the current set of CLR proxy types.
2908         /// </summary>
2909         /// <returns>
2910         /// Enumerable of the current set of CLR proxy types.
2911         /// This will never be null.
2912         /// </returns>
2913         public static IEnumerable<Type> GetKnownProxyTypes()
2914         {
2915             return EntityProxyFactory.GetKnownProxyTypes();
2916         }
2917
2918         /// <summary>
2919         /// Given a type that may represent a known proxy type, 
2920         /// return the corresponding type being proxied.
2921         /// </summary>
2922         /// <param name="type">Type that may represent a proxy type.</param>
2923         /// <returns>
2924         /// Non-proxy type that corresponds to the supplied proxy type,
2925         /// or the supplied type if it is not a known proxy type.
2926         /// </returns>
2927         /// <exception cref="ArgumentNullException">
2928         /// If the value of the type parameter is null.
2929         /// </exception
2930         public static Type GetObjectType(Type type)
2931         {
2932             EntityUtil.CheckArgumentNull(type, "type");
2933
2934             return EntityProxyFactory.IsProxyType(type) ? type.BaseType : type;
2935         }
2936
2937         /// <summary>
2938         /// Create an appropriate instance of the type <typeparamref name="T"/>.
2939         /// </summary>
2940         /// <typeparam name="T">
2941         /// Type of object to be returned.
2942         /// </typeparam>
2943         /// <returns>
2944         /// An instance of an object of type <typeparamref name="T"/>.
2945         /// The object will either be an instance of the exact type <typeparamref name="T"/>,
2946         /// or possibly an instance of the proxy type that corresponds to <typeparamref name="T"/>.
2947         /// </returns>
2948         /// <remarks>
2949         /// The type <typeparamref name="T"/> must have an OSpace EntityType representation.
2950         /// </remarks>
2951         public T CreateObject<T>()
2952             where T : class
2953         {
2954             T instance = null;
2955             Type clrType = typeof(T);
2956
2957             // Ensure the assembly containing the entity's CLR type is loaded into the workspace.
2958             MetadataWorkspace.ImplicitLoadAssemblyForType(clrType, null);
2959
2960             // Retrieve the OSpace EntityType that corresponds to the supplied CLR type.
2961             // This call ensure that this mapping exists.
2962             ClrEntityType entityType = MetadataWorkspace.GetItem<ClrEntityType>(clrType.FullName, DataSpace.OSpace);
2963             EntityProxyTypeInfo proxyTypeInfo = null;
2964
2965             if (ContextOptions.ProxyCreationEnabled && ((proxyTypeInfo = EntityProxyFactory.GetProxyType(entityType)) != null))
2966             {
2967                 instance = (T)proxyTypeInfo.CreateProxyObject();
2968
2969                 // After creating the proxy we need to add additional state to the proxy such
2970                 // that it is able to function correctly when returned.  In particular, it needs
2971                 // an initialized set of RelatedEnd objects because it will not be possible to
2972                 // create these for convention based mapping once the metadata in the context has
2973                 // been lost.
2974                 IEntityWrapper wrappedEntity = EntityWrapperFactory.CreateNewWrapper(instance, null);
2975                 wrappedEntity.InitializingProxyRelatedEnds = true;
2976                 try
2977                 {
2978                     // We're setting the context temporarily here so that we can go through the process
2979                     // of creating RelatedEnds even with convention-based mapping.
2980                     // However, we also need to tell the wrapper that we're doing this so that we don't
2981                     // try to do things that we normally do when we have a context, such as adding the
2982                     // context to the RelatedEnds.  We can't do these things since they require an
2983                     // EntitySet, and, because of MEST, we don't have one.
2984                     wrappedEntity.AttachContext(this, null, MergeOption.NoTracking);
2985                     proxyTypeInfo.SetEntityWrapper(wrappedEntity);
2986                     if (proxyTypeInfo.InitializeEntityCollections != null)
2987                     {
2988                         proxyTypeInfo.InitializeEntityCollections.Invoke(null, new object[] { wrappedEntity });
2989                     }
2990                 }
2991                 finally
2992                 {
2993                     wrappedEntity.InitializingProxyRelatedEnds = false;
2994                     wrappedEntity.DetachContext();
2995                 }
2996             }
2997             else
2998             {
2999                 Func<object> ctor = LightweightCodeGenerator.GetConstructorDelegateForType(entityType) as Func<object>;
3000                 Debug.Assert(ctor != null, "Could not find entity constructor");
3001                 instance = ctor() as T;
3002             }
3003
3004             return instance;
3005         }
3006
3007         /// <summary>
3008         /// Execute a command against the database server that does not return a sequence of objects.
3009         /// The command is specified using the server's native query language, such as SQL.
3010         /// </summary>
3011         /// <param name="command">The command specified in the server's native query language.</param>
3012         /// <param name="parameters">The parameter values to use for the query.</param>
3013         /// <returns>A single integer return value</returns>
3014         public int ExecuteStoreCommand(string commandText, params object[] parameters)
3015         {
3016             this.EnsureConnection();
3017
3018             try
3019             {
3020                 DbCommand command = CreateStoreCommand(commandText, parameters);
3021                 return command.ExecuteNonQuery();
3022             }
3023             finally
3024             {
3025                 this.ReleaseConnection();
3026             }
3027         }
3028
3029         /// <summary>
3030         /// Execute the sequence returning query against the database server. 
3031         /// The query is specified using the server's native query language, such as SQL.
3032         /// </summary>
3033         /// <typeparam name="TElement">The element type of the result sequence.</typeparam>
3034         /// <param name="query">The query specified in the server's native query language.</param>
3035         /// <param name="parameters">The parameter values to use for the query.</param>
3036         /// <returns>An IEnumerable sequence of objects.</returns>
3037         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
3038         public ObjectResult<TElement> ExecuteStoreQuery<TElement>(string commandText, params object[] parameters)
3039         {
3040             return ExecuteStoreQueryInternal<TElement>(commandText, null /*entitySetName*/, MergeOption.AppendOnly, parameters);
3041         }
3042
3043         /// <summary>
3044         /// Execute the sequence returning query against the database server. 
3045         /// The query is specified using the server's native query language, such as SQL.
3046         /// </summary>
3047         /// <typeparam name="TEntity">The element type of the resulting sequence</typeparam>
3048         /// <param name="reader">The DbDataReader to translate</param>
3049         /// <param name="entitySetName">The entity set in which results should be tracked. Null indicates there is no entity set.</param>
3050         /// <param name="mergeOption">Merge option to use for entity results.</param>
3051         /// <returns>The translated sequence of objects</returns>
3052         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
3053         public ObjectResult<TEntity> ExecuteStoreQuery<TEntity>(string commandText, string entitySetName, MergeOption mergeOption, params object[] parameters)
3054         {
3055             EntityUtil.CheckStringArgument(entitySetName, "entitySetName");
3056             return ExecuteStoreQueryInternal<TEntity>(commandText, entitySetName, mergeOption, parameters);
3057         }
3058
3059         /// <summary>
3060         /// See ExecuteStoreQuery method.
3061         /// </summary>
3062         private ObjectResult<TElement> ExecuteStoreQueryInternal<TElement>(string commandText, string entitySetName, MergeOption mergeOption, params object[] parameters)
3063         {
3064             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type
3065             // is loaded into the workspace. If the schema types are not loaded
3066             // metadata, cache & query would be unable to reason about the type. We
3067             // either auto-load <TElement>'s assembly into the ObjectItemCollection or we
3068             // auto-load the user's calling assembly and its referenced assemblies.
3069             // If the entities in the user's result spans multiple assemblies, the
3070             // user must manually call LoadFromAssembly. *GetCallingAssembly returns
3071             // the assembly of the method that invoked the currently executing method.
3072             this.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(TElement), System.Reflection.Assembly.GetCallingAssembly());
3073
3074             this.EnsureConnection();
3075             DbDataReader reader = null;
3076
3077             try
3078             {
3079                 DbCommand command = CreateStoreCommand(commandText, parameters);
3080                 reader = command.ExecuteReader();
3081             }
3082             catch
3083             {
3084                 // We only release the connection when there is an exception. Otherwise, the ObjectResult is
3085                 // in charge of releasing it.
3086                 this.ReleaseConnection();
3087                 throw;
3088             }
3089
3090             try
3091             {
3092                 return InternalTranslate<TElement>(reader, entitySetName, mergeOption, true);
3093             }
3094             catch
3095             {
3096                 reader.Dispose();
3097                 this.ReleaseConnection();
3098                 throw;
3099             }
3100         }
3101
3102         /// <summary>
3103         /// Translates the data from a DbDataReader into sequence of objects.
3104         /// </summary>
3105         /// <typeparam name="TElement">The element type of the resulting sequence</typeparam>
3106         /// <param name="reader">The DbDataReader to translate</param>
3107         /// <param name="mergeOption">Merge option to use for entity results.</param>
3108         /// <returns>The translated sequence of objects</returns>
3109         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
3110         public ObjectResult<TElement> Translate<TElement>(DbDataReader reader)
3111         {
3112             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type
3113             // is loaded into the workspace. If the schema types are not loaded
3114             // metadata, cache & query would be unable to reason about the type. We
3115             // either auto-load <TElement>'s assembly into the ObjectItemCollection or we
3116             // auto-load the user's calling assembly and its referenced assemblies.
3117             // If the entities in the user's result spans multiple assemblies, the
3118             // user must manually call LoadFromAssembly. *GetCallingAssembly returns
3119             // the assembly of the method that invoked the currently executing method.
3120             this.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(TElement), System.Reflection.Assembly.GetCallingAssembly());
3121
3122             return InternalTranslate<TElement>(reader, null /*entitySetName*/, MergeOption.AppendOnly, false);
3123         }
3124
3125         /// <summary>
3126         /// Translates the data from a DbDataReader into sequence of entities.
3127         /// </summary>
3128         /// <typeparam name="TEntity">The element type of the resulting sequence</typeparam>
3129         /// <param name="reader">The DbDataReader to translate</param>
3130         /// <param name="entitySetName">The entity set in which results should be tracked. Null indicates there is no entity set.</param>
3131         /// <param name="mergeOption">Merge option to use for entity results.</param>
3132         /// <returns>The translated sequence of objects</returns>
3133         [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
3134         public ObjectResult<TEntity> Translate<TEntity>(DbDataReader reader, string entitySetName, MergeOption mergeOption)
3135         {
3136             EntityUtil.CheckStringArgument(entitySetName, "entitySetName");
3137
3138             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type
3139             // is loaded into the workspace. If the schema types are not loaded
3140             // metadata, cache & query would be unable to reason about the type. We
3141             // either auto-load <TEntity>'s assembly into the ObjectItemCollection or we
3142             // auto-load the user's calling assembly and its referenced assemblies.
3143             // If the entities in the user's result spans multiple assemblies, the
3144             // user must manually call LoadFromAssembly. *GetCallingAssembly returns
3145             // the assembly of the method that invoked the currently executing method.
3146             this.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(TEntity), System.Reflection.Assembly.GetCallingAssembly());
3147
3148             return InternalTranslate<TEntity>(reader, entitySetName, mergeOption, false);
3149         }
3150
3151         private ObjectResult<TElement> InternalTranslate<TElement>(DbDataReader reader, string entitySetName, MergeOption mergeOption, bool readerOwned)
3152         {
3153             EntityUtil.CheckArgumentNull(reader, "reader");
3154             EntityUtil.CheckArgumentMergeOption(mergeOption);
3155             EntitySet entitySet = null;
3156             if (!string.IsNullOrEmpty(entitySetName))
3157             {
3158                 entitySet = this.GetEntitySetFromName(entitySetName);
3159             }
3160
3161             // make sure all metadata is available (normally this is handled by the call to EntityConnection.Open,
3162             // but translate does not necessarily use the EntityConnection)
3163             EnsureMetadata();
3164
3165             // get the expected EDM type
3166             EdmType modelEdmType;
3167             Type unwrappedTElement = Nullable.GetUnderlyingType(typeof(TElement)) ?? typeof(TElement);
3168             CollectionColumnMap columnMap;
3169             // for enums that are not in the model we use the enum underlying type
3170             if (MetadataHelper.TryDetermineCSpaceModelType<TElement>(this.MetadataWorkspace, out modelEdmType) ||
3171                 (unwrappedTElement.IsEnum && MetadataHelper.TryDetermineCSpaceModelType(unwrappedTElement.GetEnumUnderlyingType(), this.MetadataWorkspace, out modelEdmType)))
3172             {
3173                 if (entitySet != null && !entitySet.ElementType.IsAssignableFrom(modelEdmType))
3174                 {
3175                     throw EntityUtil.InvalidOperation(Strings.ObjectContext_InvalidEntitySetForStoreQuery(entitySet.EntityContainer.Name,
3176                         entitySet.Name, typeof(TElement)));
3177                 }
3178
3179                 columnMap = ColumnMapFactory.CreateColumnMapFromReaderAndType(reader, modelEdmType, entitySet, null);
3180             }
3181             else
3182             {
3183                 columnMap = ColumnMapFactory.CreateColumnMapFromReaderAndClrType(reader, typeof(TElement), this.MetadataWorkspace);
3184             }
3185
3186             // build a shaper for the column map to produce typed results
3187             System.Data.Common.QueryCache.QueryCacheManager cacheManager = this.MetadataWorkspace.GetQueryCacheManager();
3188             ShaperFactory<TElement> shaperFactory = Translator.TranslateColumnMap<TElement>(cacheManager, columnMap, this.MetadataWorkspace, null, mergeOption, false);
3189             Shaper<TElement> shaper = shaperFactory.Create(reader, this, this.MetadataWorkspace, mergeOption, readerOwned);
3190             return new ObjectResult<TElement>(shaper, entitySet, MetadataHelper.GetElementType(columnMap.Type), readerOwned);
3191         }
3192
3193         private DbCommand CreateStoreCommand(string commandText, params object[] parameters)
3194         {
3195             DbCommand command = this._connection.StoreConnection.CreateCommand();
3196             command.CommandText = commandText;
3197
3198             // get relevant state from the object context
3199             if (this.CommandTimeout.HasValue)
3200             {
3201                 command.CommandTimeout = this.CommandTimeout.Value;
3202             }
3203             EntityTransaction entityTransaction = this._connection.CurrentTransaction;
3204             if (null != entityTransaction)
3205             {
3206                 command.Transaction = entityTransaction.StoreTransaction;
3207             }
3208
3209             if (null != parameters && parameters.Length > 0)
3210             {
3211                 DbParameter[] dbParameters = new DbParameter[parameters.Length];
3212
3213                 // three cases: all explicit DbParameters, no explicit DbParameters
3214                 // or a mix of the two (throw in the last case)
3215                 if (parameters.All(p => p is DbParameter))
3216                 {
3217                     for (int i = 0; i < parameters.Length; i++)
3218                     {
3219                         dbParameters[i] = (DbParameter)parameters[i];
3220                     }
3221                 }
3222                 else if (!parameters.Any(p => p is DbParameter))
3223                 {
3224                     string[] parameterNames = new string[parameters.Length];
3225                     string[] parameterSql = new string[parameters.Length];
3226                     for (int i = 0; i < parameters.Length; i++)
3227                     {
3228                         parameterNames[i] = string.Format(CultureInfo.InvariantCulture, "p{0}", i);
3229                         dbParameters[i] = command.CreateParameter();
3230                         dbParameters[i].ParameterName = parameterNames[i];
3231                         dbParameters[i].Value = parameters[i] ?? DBNull.Value;
3232
3233                         // By default, we attempt to swap in a SQL Server friendly representation of the parameter.
3234                         // For other providers, users may write:
3235                         //
3236                         //      ExecuteStoreQuery("select * from foo f where f.X = ?", 1);
3237                         //
3238                         // rather than:
3239                         //
3240                         //      ExecuteStoreQuery("select * from foo f where f.X = {0}", 1);
3241                         parameterSql[i] = "@" + parameterNames[i];
3242                     }
3243                     command.CommandText = string.Format(CultureInfo.InvariantCulture, command.CommandText, parameterSql);
3244                 }
3245                 else
3246                 {
3247                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectContext_ExecuteCommandWithMixOfDbParameterAndValues);
3248                 }
3249
3250                 command.Parameters.AddRange(dbParameters);
3251             }
3252
3253             return command;
3254         }
3255
3256         /// <summary>
3257         /// Creates the database using the current store connection and the metadata in the StoreItemCollection. Most of the actual work
3258         /// is done by the DbProviderServices implementation for the current store connection.
3259         /// </summary>
3260         public void CreateDatabase()
3261         {
3262             DbConnection storeConnection = this._connection.StoreConnection;
3263             DbProviderServices services = DbProviderServices.GetProviderServices(this.GetStoreItemCollection().StoreProviderFactory);
3264             services.CreateDatabase(storeConnection, this.CommandTimeout, this.GetStoreItemCollection());
3265         }
3266
3267         /// <summary>
3268         /// Deletes the database that is specified as the database in the current store connection. Most of the actual work
3269         /// is done by the DbProviderServices implementation for the current store connection.
3270         /// </summary>
3271         public void DeleteDatabase()
3272         {
3273             DbConnection storeConnection = this._connection.StoreConnection;
3274             DbProviderServices services = DbProviderServices.GetProviderServices(this.GetStoreItemCollection().StoreProviderFactory);
3275             services.DeleteDatabase(storeConnection, this.CommandTimeout, this.GetStoreItemCollection());
3276         }
3277
3278         /// <summary>
3279         /// Checks if the database that is specified as the database in the current store connection exists on the store. Most of the actual work
3280         /// is done by the DbProviderServices implementation for the current store connection.
3281         /// </summary>
3282         public bool DatabaseExists()
3283         {
3284             DbConnection storeConnection = this._connection.StoreConnection;
3285             DbProviderServices services = DbProviderServices.GetProviderServices(this.GetStoreItemCollection().StoreProviderFactory);
3286             return services.DatabaseExists(storeConnection, this.CommandTimeout, this.GetStoreItemCollection());
3287         }
3288
3289         /// <summary>
3290         /// Creates the sql script that can be used to create the database for the metadata in the StoreItemCollection. Most of the actual work
3291         /// is done by the DbProviderServices implementation for the current store connection.
3292         /// </summary>
3293         public String CreateDatabaseScript()
3294         {
3295             DbProviderServices services = DbProviderServices.GetProviderServices(this.GetStoreItemCollection().StoreProviderFactory);
3296             string targetProviderManifestToken = this.GetStoreItemCollection().StoreProviderManifestToken;
3297             return services.CreateDatabaseScript(targetProviderManifestToken, this.GetStoreItemCollection());
3298         }
3299
3300         private StoreItemCollection GetStoreItemCollection()
3301         {
3302             var entityConnection = (EntityConnection)this.Connection;
3303             // retrieve the item collection from the entity connection rather than the context since:
3304             // a) it forces creation of the metadata workspace if it's not already there
3305             // b) the store item collection isn't guaranteed to exist on the context.MetadataWorkspace
3306             return (StoreItemCollection)entityConnection.GetMetadataWorkspace().GetItemCollection(DataSpace.SSpace);
3307         }
3308
3309         #endregion //Methods
3310
3311         #region Nested types
3312         /// <summary>
3313         /// Supports binding EntityClient parameters to Object Services parameters.
3314         /// </summary>
3315         private class ParameterBinder
3316         {
3317             private readonly EntityParameter _entityParameter;
3318             private readonly ObjectParameter _objectParameter;
3319
3320             internal ParameterBinder(EntityParameter entityParameter, ObjectParameter objectParameter)
3321             {
3322                 _entityParameter = entityParameter;
3323                 _objectParameter = objectParameter;
3324             }
3325
3326             internal void OnDataReaderClosingHandler(object sender, EventArgs args)
3327             {
3328                 // When the reader is closing, out/inout parameter values are set on the EntityParameter
3329                 // instance. Pass this value through to the corresponding ObjectParameter.
3330                 if (_entityParameter.Value != DBNull.Value && _objectParameter.MappableType.IsEnum)
3331                 {
3332                     _objectParameter.Value = Enum.ToObject(_objectParameter.MappableType, _entityParameter.Value);
3333                 }
3334                 else
3335                 {
3336                     _objectParameter.Value = _entityParameter.Value;
3337                 }
3338             }
3339         }
3340         #endregion
3341
3342         internal CollectionColumnMap ColumnMapBuilder { get; set; }
3343     }
3344 }