|
//---------------------------------------------------------------------
// <copyright file="Function.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner willa
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.EntityModel.SchemaObjectModel
{
using System.Data.Entity;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Xml;
internal class FunctionImportElement : Function
{
private string _unresolvedEntitySet = null;
private bool _entitySetPathDefined = false;
private EntityContainer _container = null;
private EntityContainerEntitySet _entitySet = null;
private bool? _isSideEffecting = null;
internal FunctionImportElement(EntityContainer container)
: base(container.Schema)
{
if (Schema.DataModel == SchemaDataModelOption.EntityDataModel)
OtherContent.Add(Schema.SchemaSource);
_container = container;
// By default function imports are non-composable.
_isComposable = false;
}
public override bool IsFunctionImport { get { return true; } }
public override string FQName
{
get
{
return _container.Name + "." + this.Name;
}
}
public override string Identity
{
get
{
return base.Name;
}
}
public EntityContainer Container
{
get { return _container; }
}
public EntityContainerEntitySet EntitySet { get { return _entitySet; } }
protected override bool HandleAttribute(XmlReader reader)
{
if (base.HandleAttribute(reader))
{
return true;
}
else if (CanHandleAttribute(reader, XmlConstants.EntitySet))
{
string entitySetName;
if (Utils.GetString(Schema, reader, out entitySetName))
{
_unresolvedEntitySet = entitySetName;
}
return true;
}
else if (CanHandleAttribute(reader, XmlConstants.EntitySetPath))
{
string entitySetPath;
if (Utils.GetString(Schema, reader, out entitySetPath))
{
// EF does not support this EDM 3.0 attribute, we only use it for validation.
_entitySetPathDefined = true;
}
return true;
}
else if (CanHandleAttribute(reader, XmlConstants.IsBindable))
{
// EF does not support this EDM 3.0 attribute, so ignore it.
return true;
}
else if (CanHandleAttribute(reader, XmlConstants.IsSideEffecting))
{
// Even though EF does not support this attribute, we want to remember the value in order to throw an error
// in case user specifies IsComposable = true and IsSideEffecting = true.
bool isSideEffecting = true;
if (HandleBoolAttribute(reader, ref isSideEffecting))
{
_isSideEffecting = isSideEffecting;
}
return true;
}
return false;
}
internal override void ResolveTopLevelNames()
{
base.ResolveTopLevelNames();
ResolveEntitySet(this, _unresolvedEntitySet, ref _entitySet);
}
internal void ResolveEntitySet(SchemaElement owner, string unresolvedEntitySet, ref EntityContainerEntitySet entitySet)
{
Debug.Assert(IsFunctionImport, "Only FunctionImport elkements specify EntitySets");
Debug.Assert(null != _container, "function imports must know container");
// resolve entity set
if (null == entitySet && null != unresolvedEntitySet)
{
entitySet = _container.FindEntitySet(unresolvedEntitySet);
if (null == entitySet)
{
owner.AddError(ErrorCode.FunctionImportUnknownEntitySet,
EdmSchemaErrorSeverity.Error,
System.Data.Entity.Strings.FunctionImportUnknownEntitySet(unresolvedEntitySet, this.FQName));
}
}
}
internal override void Validate()
{
base.Validate();
ValidateFunctionImportReturnType(this, _type, this.CollectionKind, _entitySet, _entitySetPathDefined);
if (_returnTypeList != null)
{
foreach (ReturnType returnType in _returnTypeList)
{
Debug.Assert(returnType.Type != null, "FunctionImport/ReturnType element must not have subelements.");
ValidateFunctionImportReturnType(returnType, returnType.Type, returnType.CollectionKind, returnType.EntitySet, returnType.EntitySetPathDefined);
}
}
if (_isComposable && _isSideEffecting.HasValue && _isSideEffecting.Value == true)
{
this.AddError(ErrorCode.FunctionImportComposableAndSideEffectingNotAllowed,
EdmSchemaErrorSeverity.Error,
Strings.FunctionImportComposableAndSideEffectingNotAllowed(this.FQName));
}
if (_parameters != null)
{
foreach (Parameter p in _parameters)
{
if (p.IsRefType || p.CollectionKind != Metadata.Edm.CollectionKind.None)
{
this.AddError(ErrorCode.FunctionImportCollectionAndRefParametersNotAllowed,
EdmSchemaErrorSeverity.Error,
Strings.FunctionImportCollectionAndRefParametersNotAllowed(this.FQName));
}
if (!p.TypeUsageBuilder.Nullable)
{
this.AddError(ErrorCode.FunctionImportNonNullableParametersNotAllowed,
EdmSchemaErrorSeverity.Error,
Strings.FunctionImportNonNullableParametersNotAllowed(this.FQName));
}
}
}
}
private void ValidateFunctionImportReturnType(SchemaElement owner, SchemaType returnType, CollectionKind returnTypeCollectionKind, EntityContainerEntitySet entitySet, bool entitySetPathDefined)
{
if (returnType != null && !ReturnTypeMeetsFunctionImportBasicRequirements(returnType, returnTypeCollectionKind))
{
owner.AddError(ErrorCode.FunctionImportUnsupportedReturnType,
EdmSchemaErrorSeverity.Error,
owner,
GetReturnTypeErrorMessage(Schema.SchemaVersion, this.Name)
);
}
ValidateFunctionImportReturnType(owner, returnType, entitySet, entitySetPathDefined);
}
private bool ReturnTypeMeetsFunctionImportBasicRequirements(SchemaType type, CollectionKind returnTypeCollectionKind)
{
if (type is ScalarType && returnTypeCollectionKind == CollectionKind.Bag)
return true;
if (type is SchemaEntityType && returnTypeCollectionKind == CollectionKind.Bag) return true;
if (Schema.SchemaVersion == XmlConstants.EdmVersionForV1_1)
{
if (type is ScalarType && returnTypeCollectionKind == CollectionKind.None) return true;
if (type is SchemaEntityType && returnTypeCollectionKind == CollectionKind.None) return true;
if (type is SchemaComplexType && returnTypeCollectionKind == CollectionKind.None) return true;
if (type is SchemaComplexType && returnTypeCollectionKind == CollectionKind.Bag) return true;
}
if (Schema.SchemaVersion >= XmlConstants.EdmVersionForV2)
{
if (type is SchemaComplexType && returnTypeCollectionKind == CollectionKind.Bag) return true;
}
if (Schema.SchemaVersion >= XmlConstants.EdmVersionForV3)
{
if (type is SchemaEnumType && returnTypeCollectionKind == CollectionKind.Bag) return true;
}
return false;
}
/// <summary>
/// validate the following negative scenarios:
/// ReturnType="Collection(EntityTypeA)"
/// ReturnType="Collection(EntityTypeA)" EntitySet="ESet.EType is not oftype EntityTypeA"
/// EntitySet="A"
/// ReturnType="Collection(ComplexTypeA)" EntitySet="something"
/// ReturnType="Collection(ComplexTypeA)", but the ComplexTypeA has a nested complexType property, this scenario will be handle in the runtime
/// </summary>
private void ValidateFunctionImportReturnType(SchemaElement owner, SchemaType returnType, EntityContainerEntitySet entitySet, bool entitySetPathDefined)
{
// If entity type, verify specification of entity set and that the type is appropriate for the entity set
SchemaEntityType entityType = returnType as SchemaEntityType;
if (entitySet != null && entitySetPathDefined)
{
owner.AddError(ErrorCode.FunctionImportEntitySetAndEntitySetPathDeclared,
EdmSchemaErrorSeverity.Error,
Strings.FunctionImportEntitySetAndEntitySetPathDeclared(this.FQName));
}
if (null != entityType)
{
// entity type
if (null == entitySet)
{
// ReturnType="Collection(EntityTypeA)"
owner.AddError(ErrorCode.FunctionImportReturnsEntitiesButDoesNotSpecifyEntitySet,
EdmSchemaErrorSeverity.Error,
System.Data.Entity.Strings.FunctionImportReturnEntitiesButDoesNotSpecifyEntitySet(this.FQName));
}
else if (null != entitySet.EntityType && !entityType.IsOfType(entitySet.EntityType))
{
// ReturnType="Collection(EntityTypeA)" EntitySet="ESet.EType is not oftype EntityTypeA"
owner.AddError(ErrorCode.FunctionImportEntityTypeDoesNotMatchEntitySet,
EdmSchemaErrorSeverity.Error,
System.Data.Entity.Strings.FunctionImportEntityTypeDoesNotMatchEntitySet(
this.FQName, entitySet.EntityType.FQName, entitySet.Name));
}
}
else
{
// complex type
SchemaComplexType complexType = returnType as SchemaComplexType;
if (complexType != null)
{
if (entitySet != null || entitySetPathDefined)
{
// ReturnType="Collection(ComplexTypeA)" EntitySet="something"
owner.AddError(
ErrorCode.ComplexTypeAsReturnTypeAndDefinedEntitySet,
EdmSchemaErrorSeverity.Error,
owner.LineNumber,
owner.LinePosition,
System.Data.Entity.Strings.ComplexTypeAsReturnTypeAndDefinedEntitySet(this.FQName, complexType.Name));
}
}
else
{
Debug.Assert(returnType == null || returnType is ScalarType || returnType is SchemaEnumType || returnType is Relationship,
"null return type, scalar return type, enum return type or relationship expected here.");
// scalar type or no return type
if (entitySet != null || entitySetPathDefined)
{
// EntitySet="A"
owner.AddError(ErrorCode.FunctionImportSpecifiesEntitySetButDoesNotReturnEntityType,
EdmSchemaErrorSeverity.Error,
System.Data.Entity.Strings.FunctionImportSpecifiesEntitySetButNotEntityType(this.FQName));
}
}
}
}
private string GetReturnTypeErrorMessage(double schemaVersion, string functionName)
{
string errorMessage;
if (Schema.SchemaVersion == XmlConstants.EdmVersionForV1)
{
errorMessage = Strings.FunctionImportWithUnsupportedReturnTypeV1(functionName);
}
else if (Schema.SchemaVersion == XmlConstants.EdmVersionForV1_1)
{
errorMessage = Strings.FunctionImportWithUnsupportedReturnTypeV1_1(functionName);
}
else
{
Debug.Assert(
XmlConstants.EdmVersionForV3 == XmlConstants.SchemaVersionLatest,
"Please update the error message accordingly");
errorMessage = Strings.FunctionImportWithUnsupportedReturnTypeV2(functionName);
}
return errorMessage;
}
internal override SchemaElement Clone(SchemaElement parentElement)
{
FunctionImportElement function = new FunctionImportElement((EntityContainer)parentElement);
CloneSetFunctionFields(function);
function._container = _container;
function._entitySet = _entitySet;
function._unresolvedEntitySet = _unresolvedEntitySet;
function._entitySetPathDefined = _entitySetPathDefined;
return function;
}
}
}
|