Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 1836deff6a
Fetching contributors…

Cannot retrieve contributors at this time

181 lines (163 sloc) 8.169 kb
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Linq;
using System.Web.Http;
namespace Microsoft.Web.Http.Data.EntityFramework
{
/// <summary>
/// Internal utility functions for dealing with EF types and metadata
/// </summary>
internal static class ObjectContextUtilities
{
/// <summary>
/// Retrieves the <see cref="StructuralType"/> corresponding to the given CLR type (where the
/// type is an entity or complex type).
/// </summary>
/// <remarks>
/// If no mapping exists for <paramref name="clrType"/>, but one does exist for one of its base
/// types, we will return the mapping for the base type.
/// </remarks>
/// <param name="workspace">The <see cref="MetadataWorkspace"/></param>
/// <param name="clrType">The CLR type</param>
/// <returns>The <see cref="StructuralType"/> corresponding to that CLR type, or <c>null</c> if the Type
/// is not mapped.</returns>
public static StructuralType GetEdmType(MetadataWorkspace workspace, Type clrType)
{
if (workspace == null)
{
throw Error.ArgumentNull("workspace");
}
if (clrType == null)
{
throw Error.ArgumentNull("clrType");
}
if (clrType.IsPrimitive || clrType == typeof(object))
{
// want to avoid loading searching system assemblies for
// types we know aren't entity or complex types
return null;
}
// We first locate the EdmType in "OSpace", which matches the name and namespace of the CLR type
EdmType edmType = null;
do
{
if (!workspace.TryGetType(clrType.Name, clrType.Namespace, DataSpace.OSpace, out edmType))
{
// If EF could not find this type, it could be because it is not loaded into
// its current workspace. In this case, we explicitly load the assembly containing
// the CLR type and try again.
workspace.LoadFromAssembly(clrType.Assembly);
workspace.TryGetType(clrType.Name, clrType.Namespace, DataSpace.OSpace, out edmType);
}
}
while (edmType == null && (clrType = clrType.BaseType) != typeof(object) && clrType != null);
// Next we locate the StructuralType from the EdmType.
// This 2-step process is necessary when the types CLR namespace does not match Edm namespace.
// Look at the EdmEntityTypeAttribute on the generated entity classes to see this Edm namespace.
StructuralType structuralType = null;
if (edmType != null &&
(edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType || edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType))
{
workspace.TryGetEdmSpaceType((StructuralType)edmType, out structuralType);
}
return structuralType;
}
/// <summary>
/// Method used to return the current <see cref="EntityState"/> of the specified
/// entity.
/// </summary>
/// <param name="context">The <see cref="ObjectContext"/></param>
/// <param name="entity">The entity to return the <see cref="EntityState"/> for</param>
/// <returns>The current <see cref="EntityState"/> of the specified entity</returns>
public static EntityState GetEntityState(ObjectContext context, object entity)
{
if (context == null)
{
throw Error.ArgumentNull("context");
}
if (entity == null)
{
throw Error.ArgumentNull("entity");
}
ObjectStateEntry stateEntry = null;
if (!context.ObjectStateManager.TryGetObjectStateEntry(entity, out stateEntry))
{
return EntityState.Detached;
}
return stateEntry.State;
}
/// <summary>
/// Determines if the specified EdmMember is a concurrency timestamp.
/// </summary>
/// <remarks>Since EF doesn't expose "timestamp" as a first class
/// concept, we use the below criteria to infer this for ourselves.
/// </remarks>
/// <param name="member">The member to check.</param>
/// <returns>True or false.</returns>
public static bool IsConcurrencyTimestamp(EdmMember member)
{
Facet facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "ConcurrencyMode");
if (facet == null || facet.Value == null || (ConcurrencyMode)facet.Value != ConcurrencyMode.Fixed)
{
return false;
}
facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "FixedLength");
if (facet == null || facet.Value == null || !((bool)facet.Value))
{
return false;
}
facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "MaxLength");
if (facet == null || facet.Value == null || (int)facet.Value != 8)
{
return false;
}
MetadataProperty md = ObjectContextUtilities.GetStoreGeneratedPattern(member);
if (md == null || facet.Value == null || (string)md.Value != "Computed")
{
return false;
}
return true;
}
/// <summary>
/// Gets the <see cref="StoreGeneratedPattern"/> property value from the edm member.
/// </summary>
/// <param name="member">The EdmMember from which to get the StoreGeneratedPattern value.</param>
/// <returns>The <see cref="StoreGeneratedPattern"/> value.</returns>
public static MetadataProperty GetStoreGeneratedPattern(EdmMember member)
{
MetadataProperty md;
member.MetadataProperties.TryGetValue("http://schemas.microsoft.com/ado/2009/02/edm/annotation:StoreGeneratedPattern", ignoreCase: true, item: out md);
return md;
}
public static ObjectStateEntry AttachAsModifiedInternal<TEntity>(TEntity current, TEntity original, ObjectContext objectContext)
{
ObjectStateEntry stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(current);
stateEntry.ApplyOriginalValues(original);
// For any members that don't have RoundtripOriginal applied, EF can't determine modification
// state by doing value comparisons. To avoid losing updates in these cases, we must explicitly
// mark such members as modified.
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(TEntity));
AttributeCollection attributes = TypeDescriptor.GetAttributes(typeof(TEntity));
bool isRoundtripType = attributes[typeof(RoundtripOriginalAttribute)] != null;
foreach (var fieldMetadata in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata)
{
string memberName = stateEntry.CurrentValues.GetName(fieldMetadata.Ordinal);
PropertyDescriptor property = properties[memberName];
// TODO: below we need to replace ExcludeAttribute logic with corresponding
// DataContractMember/IgnoreDataMember logic
if (property != null &&
(property.Attributes[typeof(RoundtripOriginalAttribute)] == null && !isRoundtripType)
/* && property.Attributes[typeof(ExcludeAttribute)] == null */)
{
stateEntry.SetModifiedProperty(memberName);
}
}
return stateEntry;
}
}
}
Jump to Line
Something went wrong with that request. Please try again.