|
//---------------------------------------------------------------------
// <copyright file="EntityCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.Objects.DataClasses
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Metadata.Edm;
using System.Data.Objects.Internal;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
/// <summary>
/// Collection of entities modeling a particular EDM construct
/// which can either be all entities of a particular type or
/// entities participating in a particular relationship.
/// </summary>
[Serializable]
public sealed class EntityCollection<TEntity> : RelatedEnd, ICollection<TEntity>, IListSource
where TEntity : class
{
// ------
// Fields
// ------
// The following field is serialized. Adding or removing a serialized field is considered
// a breaking change. This includes changing the field type or field name of existing
// serialized fields. If you need to make this kind of change, it may be possible, but it
// will require some custom serialization/deserialization code.
// Note that this field should no longer be used directly. Instead, use the _wrappedRelatedEntities
// field. This field is retained only for compatibility with the serialization format introduced in v1.
private HashSet<TEntity> _relatedEntities;
[NonSerialized]
private CollectionChangeEventHandler _onAssociationChangedforObjectView;
[NonSerialized]
private Dictionary<TEntity, IEntityWrapper> _wrappedRelatedEntities;
// ------------
// Constructors
// ------------
/// <summary>
/// Creates an empty EntityCollection.
/// </summary>
public EntityCollection()
: base()
{
}
internal EntityCollection(IEntityWrapper wrappedOwner, RelationshipNavigation navigation, IRelationshipFixer relationshipFixer)
: base(wrappedOwner, navigation, relationshipFixer)
{
}
// ---------
// Events
// ---------
/// <summary>
/// internal Event to notify changes in the collection.
/// </summary>
// Dev notes -2
// following statement is valid on current existing CLR:
// lets say Customer is an Entity, Array[Customer] is not Array[Entity]; it is not supported
// to do the work around we have to use a non-Generic interface/class so we can pass the EntityCollection<T>
// around safely (as RelatedEnd) without losing it.
// Dev notes -3
// this event is only used for internal purposes, to make sure views are updated before we fire public AssociationChanged event
internal override event CollectionChangeEventHandler AssociationChangedForObjectView
{
add
{
_onAssociationChangedforObjectView += value;
}
remove
{
_onAssociationChangedforObjectView -= value;
}
}
// ---------
// Properties
// ---------
private Dictionary<TEntity, IEntityWrapper> WrappedRelatedEntities
{
get
{
if (null == _wrappedRelatedEntities)
{
_wrappedRelatedEntities = new Dictionary<TEntity, IEntityWrapper>();
}
return _wrappedRelatedEntities;
}
}
// ----------------------
// ICollection Properties
// ----------------------
/// <summary>
/// Count of entities in the collection.
/// </summary>
public int Count
{
get
{
DeferredLoad();
return CountInternal;
}
}
internal int CountInternal
{
get
{
// count should not cause allocation
return ((null != _wrappedRelatedEntities) ? _wrappedRelatedEntities.Count : 0);
}
}
/// <summary>
/// Whether or not the collection is read-only.
/// </summary>
public bool IsReadOnly
{
get
{
return false;
}
}
// ----------------------
// IListSource Properties
// ----------------------
/// <summary>
/// IListSource.ContainsListCollection implementation. Always returns true
/// </summary>
bool IListSource.ContainsListCollection
{
get
{
return false; // this means that the IList we return is the one which contains our actual data, it is not a collection
}
}
// -------
// Methods
// -------
internal override void OnAssociationChanged(CollectionChangeAction collectionChangeAction, object entity)
{
Debug.Assert(!(entity is IEntityWrapper), "Object is an IEntityWrapper instance instead of the raw entity.");
if (!_suppressEvents)
{
if (_onAssociationChangedforObjectView != null)
{
_onAssociationChangedforObjectView(this, (new CollectionChangeEventArgs(collectionChangeAction, entity)));
}
if (_onAssociationChanged != null)
{
_onAssociationChanged(this, (new CollectionChangeEventArgs(collectionChangeAction, entity)));
}
}
}
// ----------------------
// IListSource method
// ----------------------
/// <summary>
/// IListSource.GetList implementation
/// </summary>
/// <returns>
/// IList interface over the data to bind
/// </returns>
IList IListSource.GetList()
{
EntityType rootEntityType = null;
if (WrappedOwner.Entity != null)
{
EntitySet singleEntitySet = null;
// if the collection is attached, we can use metadata information; otherwise, it is unavailable
if (null != this.RelationshipSet)
{
singleEntitySet = ((AssociationSet)this.RelationshipSet).AssociationSetEnds[this.ToEndMember.Name].EntitySet;
EntityType associationEndType = (EntityType)((RefType)((AssociationEndMember)this.ToEndMember).TypeUsage.EdmType).ElementType;
EntityType entitySetType = singleEntitySet.ElementType;
// the type is constrained to be either the entitySet.ElementType or the end member type, whichever is most derived
if (associationEndType.IsAssignableFrom(entitySetType))
{
// entity set exposes a subtype of the association
rootEntityType = entitySetType;
}
else
{
// use the end type otherwise
rootEntityType = associationEndType;
}
}
}
return ObjectViewFactory.CreateViewForEntityCollection(rootEntityType, this);
}
/// <summary>
/// Loads the related entity or entities into the local collection using the supplied MergeOption.
/// Do merge if collection was already filled
/// </summary>
public override void Load(MergeOption mergeOption)
{
CheckOwnerNull();
//Pass in null to indicate the CreateSourceQuery method should be used.
Load((List<IEntityWrapper>)null, mergeOption);
// do not fire the AssociationChanged event here,
// once it is fired in one level deeper, (at Internal void Load(IEnumerable<T>)), you don't need to add the event at other
// API that call (Internal void Load(IEnumerable<T>))
}
/// <summary>
/// Loads related entities into the local collection. If the collection is already filled
/// or partially filled, merges existing entities with the given entities. The given
/// entities are not assumed to be the complete set of related entities.
///
/// Owner and all entities passed in must be in Unchanged or Modified state. We allow
/// deleted elements only when the state manager is already tracking the relationship
/// instance.
/// </summary>
/// <param name="entities">Result of query returning related entities</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="entities"/> is null.</exception>
/// <exception cref="InvalidOperationException">Thrown when an entity in the given
/// collection cannot be related via the current relationship end.</exception>
public void Attach(IEnumerable<TEntity> entities)
{
EntityUtil.CheckArgumentNull(entities, "entities");
CheckOwnerNull();
//
IList<IEntityWrapper> wrappedEntities = new List<IEntityWrapper>();
foreach (TEntity entity in entities)
{
wrappedEntities.Add(EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext));
}
Attach(wrappedEntities, true);
}
/// <summary>
/// Attaches an entity to the EntityCollection. If the EntityCollection is already filled
/// or partially filled, this merges the existing entities with the given entity. The given
/// entity is not assumed to be the complete set of related entities.
///
/// Owner and all entities passed in must be in Unchanged or Modified state.
/// Deleted elements are allowed only when the state manager is already tracking the relationship
/// instance.
/// </summary>
/// <param name="entity">The entity to attach to the EntityCollection</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="entity"/> is null.</exception>
/// <exception cref="InvalidOperationException">Thrown when the entity cannot be related via the current relationship end.</exception>
public void Attach(TEntity entity)
{
EntityUtil.CheckArgumentNull(entity, "entity");
Attach(new IEntityWrapper[] { EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext) }, false);
}
/// <summary>
/// Requires: collection is null or contains related entities.
/// Loads related entities into the local collection.
/// </summary>
/// <param name="collection">If null, retrieves entities from the server through a query;
/// otherwise, loads the given collection
/// </param>
internal void Load(List<IEntityWrapper> collection, MergeOption mergeOption)
{
// Validate that the Load is possible
bool hasResults;
ObjectQuery<TEntity> sourceQuery = ValidateLoad<TEntity>(mergeOption, "EntityCollection", out hasResults);
// we do not want any Add or Remove event to be fired during Merge, we will fire a Refresh event at the end if everything is successful
_suppressEvents = true;
try
{
if (collection == null)
{
Merge<TEntity>(hasResults
? GetResults<TEntity>(sourceQuery)
: Enumerable.Empty<TEntity>(), mergeOption, true /*setIsLoaded*/);
}
else
{
Merge<TEntity>(collection, mergeOption, true /*setIsLoaded*/);
}
}
finally
{
_suppressEvents = false;
}
// fire the AssociationChange with Refresh
OnAssociationChanged(CollectionChangeAction.Refresh, null);
}
/// <summary>
///
/// </summary>
public void Add(TEntity entity)
{
EntityUtil.CheckArgumentNull(entity, "entity");
Add(EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext));
}
/// <summary>
/// Add the item to the underlying collection
/// </summary>
/// <param name="entity"></param>
internal override void DisconnectedAdd(IEntityWrapper wrappedEntity)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
// Validate that the incoming entity is also detached
if (null != wrappedEntity.Context && wrappedEntity.MergeOption != MergeOption.NoTracking)
{
throw EntityUtil.UnableToAddToDisconnectedRelatedEnd();
}
VerifyType(wrappedEntity);
// Add the entity to local collection without doing any fixup
AddToCache(wrappedEntity, /* applyConstraints */ false);
OnAssociationChanged(CollectionChangeAction.Add, wrappedEntity.Entity);
}
/// <summary>
/// Remove the item from the underlying collection
/// </summary>
/// <param name="entity"></param>
/// <param name="applyConstraints"></param>
internal override bool DisconnectedRemove(IEntityWrapper wrappedEntity)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
// Validate that the incoming entity is also detached
if (null != wrappedEntity.Context && wrappedEntity.MergeOption != MergeOption.NoTracking)
{
throw EntityUtil.UnableToRemoveFromDisconnectedRelatedEnd();
}
// Remove the entity to local collection without doing any fixup
bool result = RemoveFromCache(wrappedEntity, /* resetIsLoaded*/ false, /*preserveForeignKey*/ false);
OnAssociationChanged(CollectionChangeAction.Remove, wrappedEntity.Entity);
return result;
}
/// <summary>
/// Removes an entity from the EntityCollection. If the owner is
/// attached to a context, Remove marks the relationship for deletion and if
/// the relationship is composition also marks the entity for deletion.
/// </summary>
/// <param name="entity">
/// Entity instance to remove from the EntityCollection
/// </param>
/// <returns>Returns true if the entity was successfully removed, false if the entity was not part of the RelatedEnd.</returns>
public bool Remove(TEntity entity)
{
EntityUtil.CheckArgumentNull(entity, "entity");
DeferredLoad();
return RemoveInternal(entity);
}
internal bool RemoveInternal(TEntity entity)
{
return Remove(EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext), /*preserveForeignKey*/false);
}
internal override void Include(bool addRelationshipAsUnchanged, bool doAttach)
{
if (null != _wrappedRelatedEntities && null != this.ObjectContext)
{
List<IEntityWrapper> wrappedRelatedEntities = new List<IEntityWrapper>(_wrappedRelatedEntities.Values);
foreach (IEntityWrapper wrappedEntity in wrappedRelatedEntities)
{
// Sometimes with mixed POCO and IPOCO, you can get different instances of IEntityWrappers stored in the IPOCO related ends
// These should be replaced by the IEntityWrapper that is stored in the context
IEntityWrapper identityWrapper = EntityWrapperFactory.WrapEntityUsingContext(wrappedEntity.Entity, WrappedOwner.Context);
if (identityWrapper != wrappedEntity)
{
_wrappedRelatedEntities[(TEntity)identityWrapper.Entity] = identityWrapper;
}
IncludeEntity(identityWrapper, addRelationshipAsUnchanged, doAttach);
}
}
}
internal override void Exclude()
{
if (null != _wrappedRelatedEntities && null != this.ObjectContext)
{
if (!IsForeignKey)
{
foreach (IEntityWrapper wrappedEntity in _wrappedRelatedEntities.Values)
{
ExcludeEntity(wrappedEntity);
}
}
else
{
TransactionManager tm = ObjectContext.ObjectStateManager.TransactionManager;
Debug.Assert(tm.IsAddTracking || tm.IsAttachTracking, "Exclude being called while not part of attach/add rollback--PromotedEntityKeyRefs will be null.");
List<IEntityWrapper> values = new List<IEntityWrapper>(_wrappedRelatedEntities.Values);
foreach (IEntityWrapper wrappedEntity in values)
{
EntityReference otherEnd = GetOtherEndOfRelationship(wrappedEntity) as EntityReference;
Debug.Assert(otherEnd != null, "Other end of FK from a collection should be a reference.");
bool doFullRemove = tm.PopulatedEntityReferences.Contains(otherEnd);
bool doRelatedEndRemove = tm.AlignedEntityReferences.Contains(otherEnd);
if (doFullRemove || doRelatedEndRemove)
{
// Remove the related ends and mark the relationship as deleted, but don't propagate the changes to the target entity itself
otherEnd.Remove(otherEnd.CachedValue,
doFixup: doFullRemove,
deleteEntity: false,
deleteOwner: false,
applyReferentialConstraints: false,
preserveForeignKey: true);
// Since this has been processed, remove it from the list
if (doFullRemove)
{
tm.PopulatedEntityReferences.Remove(otherEnd);
}
else
{
tm.AlignedEntityReferences.Remove(otherEnd);
}
}
else
{
ExcludeEntity(wrappedEntity);
}
}
}
}
}
internal override void ClearCollectionOrRef(IEntityWrapper wrappedEntity, RelationshipNavigation navigation, bool doCascadeDelete)
{
if (null != _wrappedRelatedEntities)
{
//copy into list because changing collection member is not allowed during enumeration.
// If possible avoid copying into list.
List<IEntityWrapper> tempCopy = new List<IEntityWrapper>(_wrappedRelatedEntities.Values);
foreach (IEntityWrapper wrappedCurrent in tempCopy)
{
// Following condition checks if we have already visited this graph node. If its true then
// we should not do fixup because that would cause circular loop
if ((wrappedEntity.Entity == wrappedCurrent.Entity) && (navigation.Equals(RelationshipNavigation)))
{
Remove(wrappedCurrent, /*fixup*/false, /*deleteEntity*/false, /*deleteOwner*/false, /*applyReferentialConstraints*/false, /*preserveForeignKey*/false);
}
else
{
Remove(wrappedCurrent, /*fixup*/true, doCascadeDelete, /*deleteOwner*/false, /*applyReferentialConstraints*/false, /*preserveForeignKey*/false);
}
}
Debug.Assert(_wrappedRelatedEntities.Count == 0, "After removing all related entities local collection count should be zero");
}
}
internal override void ClearWrappedValues()
{
if (_wrappedRelatedEntities != null)
{
this._wrappedRelatedEntities.Clear();
}
if (_relatedEntities != null)
{
this._relatedEntities.Clear();
}
}
/// <summary>
///
/// </summary>
/// <param name="entity"></param>
/// <param name="relationshipAlreadyExists"></param>
/// <returns>True if the verify succeeded, False if the Add should no-op</returns>
internal override bool VerifyEntityForAdd(IEntityWrapper wrappedEntity, bool relationshipAlreadyExists)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
if (!relationshipAlreadyExists && this.ContainsEntity(wrappedEntity))
{
return false;
}
this.VerifyType(wrappedEntity);
return true;
}
internal override bool CanSetEntityType(IEntityWrapper wrappedEntity)
{
return wrappedEntity.Entity is TEntity;
}
internal override void VerifyType(IEntityWrapper wrappedEntity)
{
if (!CanSetEntityType(wrappedEntity))
{
throw EntityUtil.InvalidContainedTypeCollection(wrappedEntity.Entity.GetType().FullName, typeof(TEntity).FullName);
}
}
/// <summary>
/// Remove from the RelatedEnd
/// </summary>
/// <param name="wrappedEntity"></param>
/// <param name="resetIsLoaded"></param>
/// <returns></returns>
internal override bool RemoveFromLocalCache(IEntityWrapper wrappedEntity, bool resetIsLoaded, bool preserveForeignKey)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
if (_wrappedRelatedEntities != null && _wrappedRelatedEntities.Remove((TEntity)wrappedEntity.Entity))
{
if (resetIsLoaded)
{
_isLoaded = false;
}
return true;
}
return false;
}
/// <summary>
/// Remove from the POCO collection
/// </summary>
/// <param name="wrappedEntity"></param>
/// <returns></returns>
internal override bool RemoveFromObjectCache(IEntityWrapper wrappedEntity)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
// For POCO entities - remove the object from the CLR collection
if (this.TargetAccessor.HasProperty) // Null if the navigation does not exist in this direction
{
return this.WrappedOwner.CollectionRemove(this, (TEntity)wrappedEntity.Entity);
}
return false;
}
internal override void RetrieveReferentialConstraintProperties(Dictionary<string, KeyValuePair<object, IntBox>> properties, HashSet<object> visited)
{
// Since there are no RI Constraints which has a collection as a To/Child role,
// this method is no-op.
}
internal override bool IsEmpty()
{
return _wrappedRelatedEntities == null || (_wrappedRelatedEntities.Count == 0);
}
internal override void VerifyMultiplicityConstraintsForAdd(bool applyConstraints)
{
// no-op
}
// Update IsLoaded flag if necessary
// This method is called when Clear() was called on the other end of relationship (if the other end is EntityCollection)
// or when Value property of the other end was set to null (if the other end is EntityReference).
// This method is used only when NoTracking option was used.
internal override void OnRelatedEndClear()
{
// If other end of relationship was cleared, it means that this collection is also no longer loaded
_isLoaded = false;
}
internal override bool ContainsEntity(IEntityWrapper wrappedEntity)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
// Using operator 'as' instead of () allows calling ContainsEntity
// with entity of different type than TEntity.
TEntity entity = wrappedEntity.Entity as TEntity;
return _wrappedRelatedEntities == null ? false : _wrappedRelatedEntities.ContainsKey(entity);
}
// -------------------
// ICollection Methods
// -------------------
/// <summary>
/// Get an enumerator for the collection.
/// </summary>
public new IEnumerator<TEntity> GetEnumerator()
{
DeferredLoad();
return WrappedRelatedEntities.Keys.GetEnumerator();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
DeferredLoad();
return WrappedRelatedEntities.Keys.GetEnumerator();
}
internal override IEnumerable GetInternalEnumerable()
{
return WrappedRelatedEntities.Keys;
}
internal override IEnumerable<IEntityWrapper> GetWrappedEntities()
{
return WrappedRelatedEntities.Values;
}
/// <summary>
/// Removes all entities from the locally cached collection. Also removes
/// relationships related to this entities from the ObjectStateManager.
/// </summary>
public void Clear()
{
DeferredLoad();
if (WrappedOwner.Entity != null)
{
bool shouldFireEvent = (CountInternal > 0);
if (null != _wrappedRelatedEntities)
{
List<IEntityWrapper> affectedEntities = new List<IEntityWrapper>(_wrappedRelatedEntities.Values);
try
{
_suppressEvents = true;
foreach (IEntityWrapper wrappedEntity in affectedEntities)
{
// Remove Entity
Remove(wrappedEntity, false);
if (UsingNoTracking)
{
// The other end of relationship can be the EntityReference or EntityCollection
// If the other end is EntityReference, its IsLoaded property should be set to FALSE
RelatedEnd relatedEnd = GetOtherEndOfRelationship(wrappedEntity);
relatedEnd.OnRelatedEndClear();
}
}
Debug.Assert(_wrappedRelatedEntities.Count == 0);
}
finally
{
_suppressEvents = false;
}
if (UsingNoTracking)
{
_isLoaded = false;
}
}
if (shouldFireEvent)
{
OnAssociationChanged(CollectionChangeAction.Refresh, null);
}
}
else
{
// Disconnected Clear should be dispatched to the internal collection
if (_wrappedRelatedEntities != null)
{
_wrappedRelatedEntities.Clear();
}
}
}
/// <summary>
/// Determine if the collection contains a specific object by reference.
/// </summary>
/// <return>true if the collection contains the object by reference;
/// otherwise, false</return>
public bool Contains(TEntity entity)
{
DeferredLoad();
return _wrappedRelatedEntities == null ? false : _wrappedRelatedEntities.ContainsKey(entity);
}
/// <summary>
/// Copies the contents of the collection to an array,
/// starting at a particular array index.
/// </summary>
public void CopyTo(TEntity[] array, int arrayIndex)
{
DeferredLoad();
WrappedRelatedEntities.Keys.CopyTo(array, arrayIndex);
}
internal override void BulkDeleteAll(List<object> list)
{
if (list.Count > 0)
{
_suppressEvents = true;
try
{
foreach (object entity in list)
{
// Remove Entity
RemoveInternal(entity as TEntity);
}
}
finally
{
_suppressEvents = false;
}
OnAssociationChanged(CollectionChangeAction.Refresh, null);
}
}
internal override bool CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper)
{
Debug.Assert(this.RelationshipNavigation != null, "null RelationshipNavigation");
// If the navigation property doesn't exist (e.g. unidirectional prop), then it can't contain the entity.
if (!TargetAccessor.HasProperty)
{
return false;
}
object value = this.WrappedOwner.GetNavigationPropertyValue(this);
if (value != null)
{
if (!(value is IEnumerable))
{
throw new EntityException(System.Data.Entity.Strings.ObjectStateEntry_UnableToEnumerateCollection(
this.TargetAccessor.PropertyName, this.WrappedOwner.Entity.GetType().FullName));
}
//
foreach (object o in (value as IEnumerable))
{
if (Object.Equals(o, wrapper.Entity))
{
return true;
}
}
}
return false;
}
internal override void VerifyNavigationPropertyForAdd(IEntityWrapper wrapper)
{
// no-op
}
// This method is required to maintain compatibility with the v1 binary serialization format.
// In particular, it takes the dictionary of wrapped entities and creates a hash set of
// raw entities that will be serialized.
// Note that this is only expected to work for non-POCO entities, since serialization of POCO
// entities will not result in serialization of the RelationshipManager or its related objects.
[OnSerializing()]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public void OnSerializing(StreamingContext context)
{
if (!(WrappedOwner.Entity is IEntityWithRelationships))
{
throw new InvalidOperationException(System.Data.Entity.Strings.RelatedEnd_CannotSerialize("EntityCollection"));
}
_relatedEntities = _wrappedRelatedEntities == null ? null : new HashSet<TEntity>(_wrappedRelatedEntities.Keys);
}
// This method is required to maintain compatibility with the v1 binary serialization format.
// In particular, it takes the _relatedEntities HashSet and recreates the dictionary of wrapped
// entities from it. This is because the dictionary is not serialized.
// Note that this is only expected to work for non-POCO entities, since serialization of POCO
// entities will not result in serialization of the RelationshipManager or its related objects.
[OnDeserialized()]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public void OnCollectionDeserialized(StreamingContext context)
{
if (_relatedEntities != null)
{
// We need to call this here so that the hash set will be fully constructed
// ready for access. Normally, this would happen later in the process.
_relatedEntities.OnDeserialization(null);
_wrappedRelatedEntities = new Dictionary<TEntity, IEntityWrapper>();
foreach (TEntity entity in _relatedEntities)
{
_wrappedRelatedEntities.Add(entity, EntityWrapperFactory.WrapEntityUsingContext(entity, ObjectContext));
}
}
}
// Identical code is in EntityReference, but this can't be moved to the base class because it relies on the
// knowledge of the generic type, and the base class isn't generic
public ObjectQuery<TEntity> CreateSourceQuery()
{
CheckOwnerNull();
bool hasResults;
return CreateSourceQuery<TEntity>(DefaultMergeOption, out hasResults);
}
internal override IEnumerable CreateSourceQueryInternal()
{
return CreateSourceQuery();
}
//End identical code
#region Add
internal override void AddToLocalCache(IEntityWrapper wrappedEntity, bool applyConstraints)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
WrappedRelatedEntities[(TEntity)wrappedEntity.Entity] = wrappedEntity;
}
internal override void AddToObjectCache(IEntityWrapper wrappedEntity)
{
Debug.Assert(wrappedEntity != null, "IEntityWrapper instance is null.");
// For POCO entities - add the object to the CLR collection
if (this.TargetAccessor.HasProperty) // Null if the navigation does not exist in this direction
{
this.WrappedOwner.CollectionAdd(this, wrappedEntity.Entity);
}
}
#endregion
}
}
|