Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataWeb / Design / system / Data / EntityModel / Emitters / EntityContainerEmitter.cs / 1 / EntityContainerEmitter.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System.CodeDom;
using System.Collections.Generic;
using System.Data.Services.Design;
using System.Data.Services.Design.Common;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Diagnostics;
using System.Linq;
namespace System.Data.EntityModel.Emitters
{
///
/// This class is responsible for emiting the code for the EntityContainer schema element
///
internal sealed class EntityContainerEmitter : SchemaTypeEmitter
{
#region Fields
private const string _onContextCreatedString = "OnContextCreated";
#endregion
#region Constructors
///
///
///
///
///
public EntityContainerEmitter(ClientApiGenerator generator, EntityContainer entityContainer)
: base(generator, entityContainer)
{
}
#endregion
#region Properties, Methods, Events & Delegates
///
/// Creates the CodeTypeDeclarations necessary to generate the code for the EntityContainer schema element
///
///
public override CodeTypeDeclarationCollection EmitApiClass()
{
Validate(); // emitter-specific validation
// declare the new class
// public partial class LOBScenario : ObjectContext
CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(Item.Name);
typeDecl.IsPartial = true;
// raise the TypeGenerated event
CodeTypeReference objectContextTypeRef = TypeReference.ObjectContext;
TypeGeneratedEventArgs eventArgs = new TypeGeneratedEventArgs(Item, objectContextTypeRef);
Generator.RaiseTypeGeneratedEvent(eventArgs);
if (eventArgs.BaseType != null && !eventArgs.BaseType.Equals(objectContextTypeRef))
{
typeDecl.BaseTypes.Add(eventArgs.BaseType);
}
else
{
typeDecl.BaseTypes.Add(TypeReference.ObjectContext);
}
AddInterfaces(Item.Name, typeDecl, eventArgs.AdditionalInterfaces);
CommentEmitter.EmitSummaryComments(Item, typeDecl.Comments);
EmitTypeAttributes(Item.Name, typeDecl, eventArgs.AdditionalAttributes);
bool needTypeMapper = (0 < this.Generator.NamespaceMap.Count);
var q = from a in this.Generator.EdmItemCollection.GetItems()
where (a.BaseType != null) &&
(a.BuiltInTypeKind == BuiltInTypeKind.ComplexType || a.BuiltInTypeKind == BuiltInTypeKind.EntityType)
select a;
bool hasInheritance = (null != q.FirstOrDefault());
CreateConstructors(typeDecl, needTypeMapper, hasInheritance);
// adding partial OnContextCreated method
CreateContextPartialMethods(typeDecl);
if (needTypeMapper || hasInheritance)
{
CreateTypeMappingMethods(typeDecl, needTypeMapper, hasInheritance);
}
foreach (EntitySetBase entitySetBase in Item.BaseEntitySets)
{
if (Helper.IsEntitySet(entitySetBase))
{
EntitySet set = (EntitySet)entitySetBase;
CodeMemberProperty codeProperty = CreateEntitySetProperty(set);
typeDecl.Members.Add(codeProperty);
CodeMemberField codeField = CreateEntitySetField(set);
typeDecl.Members.Add(codeField);
}
}
foreach (EntitySetBase entitySetBase in Item.BaseEntitySets)
{
if (Helper.IsEntitySet(entitySetBase))
{
EntitySet set = (EntitySet)entitySetBase;
CodeMemberMethod codeProperty = CreateEntitySetAddObjectProperty(set);
typeDecl.Members.Add(codeProperty);
}
}
// additional members, if provided by the event subscriber
AddMembers(Item.Name, typeDecl, eventArgs.AdditionalMembers);
CodeTypeDeclarationCollection typeDecls = new CodeTypeDeclarationCollection();
typeDecls.Add(typeDecl);
return typeDecls;
}
///
/// Emitter-specific validation: check if there exist entity containers and
/// entity sets that have the same name but differ in case
///
protected override void Validate()
{
base.Validate();
Generator.VerifyLanguageCaseSensitiveCompatibilityForEntitySet(Item);
VerifyEntityTypeAndSetAccessibilityCompatability();
}
///
/// Verify that Entity Set and Type have compatible accessibilty.
/// They are compatible if the generated code will compile.
///
private void VerifyEntityTypeAndSetAccessibilityCompatability()
{
foreach (EntitySetBase entitySetBase in Item.BaseEntitySets)
{
if (Helper.IsEntitySet(entitySetBase))
{
EntitySet set = (EntitySet)entitySetBase;
if (!AreTypeAndSetAccessCompatible(GetEntityTypeAccessibility(set.ElementType), GetEntitySetPropertyAccessibility(set)))
{
Generator.AddError(
Strings.EntityTypeAndSetAccessibilityConflict(
set.ElementType.Name, GetAccessibilityCsdlStringFromMemberAttribute(GetEntityTypeAccessibility(set.ElementType)), set.Name, GetAccessibilityCsdlStringFromMemberAttribute(GetEntitySetPropertyAccessibility(set))),
ModelBuilderErrorCode.EntityTypeAndSetAccessibilityConflict,
EdmSchemaErrorSeverity.Error);
}
}
}
}
///
/// Tells whether Entity Type's specified accessibility and Entity Set Property's specified Accessibility will work together (compile) when codegen'd.
/// False if (Type is internal and Set's Property is Public OR, type is internal and Set's property is protected).
/// True otherwise
///
private bool AreTypeAndSetAccessCompatible(MemberAttributes typeAccess, MemberAttributes setAccess)
{
return !(typeAccess == MemberAttributes.Assembly && (setAccess == MemberAttributes.Public || setAccess == MemberAttributes.Family));
}
///
/// Creates the necessary constructors for the entity container.
///
private void CreateConstructors(CodeTypeDeclaration typeDecl, bool setupTypeMapper, bool hasInheritance)
{
// Constructor that takes a uri
//
// public ctor(System.Uri serviceRoot)
// : base(serviceRoot)
// {
// this.OnContextCreated();
// }
CodeConstructor connectionWorkspaceCtor = new CodeConstructor();
connectionWorkspaceCtor.Attributes = MemberAttributes.Public;
CodeParameterDeclarationExpression connectionParam = new CodeParameterDeclarationExpression(TypeReference.FromString("System.Uri", true), "serviceRoot");
connectionWorkspaceCtor.Parameters.Add(connectionParam);
connectionWorkspaceCtor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression(connectionParam.Name));
CommentEmitter.EmitSummaryComments(Strings.CtorSummaryComment(Item.Name), connectionWorkspaceCtor.Comments);
// If we have an externally provided namespage (e.g. from Visual Studio), we need to
// inject a type-mapper because type names won't match between client and server
if (setupTypeMapper || hasInheritance)
{
connectionWorkspaceCtor.Statements.Add(
new CodeAssignStatement(
new CodeFieldReferenceExpression(ThisRef, "ResolveName"),
new CodeDelegateCreateExpression(
TypeReference.ForType(typeof(Func<,>), TypeReference.ForType(typeof(Type)), TypeReference.ForType(typeof(String))),
ThisRef,
"ResolveNameFromType"
)
)
);
}
if (setupTypeMapper)
{
connectionWorkspaceCtor.Statements.Add(
new CodeAssignStatement(
new CodeFieldReferenceExpression(ThisRef, "ResolveType"),
new CodeDelegateCreateExpression(
TypeReference.ForType(typeof(Func<,>), TypeReference.ForType(typeof(String)), TypeReference.ForType(typeof(Type))),
ThisRef,
"ResolveTypeFromName"
)
)
);
}
connectionWorkspaceCtor.Statements.Add(OnContextCreatedCodeMethodInvokeExpression());
typeDecl.Members.Add(connectionWorkspaceCtor);
}
///
/// Adds the OnContextCreated partial method for the entity container.
///
private void CreateContextPartialMethods(CodeTypeDeclaration typeDecl)
{
CodeMemberMethod onContextCreatedPartialMethod = new CodeMemberMethod();
onContextCreatedPartialMethod.Name = _onContextCreatedString;
onContextCreatedPartialMethod.ReturnType = new CodeTypeReference(typeof(void));
onContextCreatedPartialMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
typeDecl.Members.Add(onContextCreatedPartialMethod);
Generator.FixUps.Add(new FixUp(Item.Name + "." + _onContextCreatedString, FixUpType.MarkAbstractMethodAsPartial));
}
private void CreateTypeMappingMethods(CodeTypeDeclaration typeDecl, bool needTypeMapper, bool hasInheritance)
{
// Special case to compensate for VB's "root namespace" feature.
if (this.Generator.Language == LanguageOption.GenerateVBCode)
{
AddRootNamespaceField(typeDecl);
}
CodeExpression comparisonExpression = new CodePropertyReferenceExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(StringComparison))),
Enum.GetName(typeof(StringComparison), this.Generator.LanguageAppropriateStringComparer));
if (needTypeMapper)
{
CodeMemberMethod resolveTypeFromName = new CodeMemberMethod();
resolveTypeFromName.Name = "ResolveTypeFromName";
resolveTypeFromName.Attributes = MemberAttributes.Final | MemberAttributes.Family;
resolveTypeFromName.Parameters.Add(new CodeParameterDeclarationExpression(TypeReference.ForType(typeof(string)), "typeName"));
resolveTypeFromName.ReturnType = TypeReference.ForType(typeof(Type));
CommentEmitter.EmitSummaryComments(Strings.TypeMapperDescription, resolveTypeFromName.Comments);
// NOTE: since multiple namespaces can have the same prefix and match the namespace
// prefix condition, it's important that the prefix check is done is prefix-length
// order, starting with the longest prefix.
var pairs = this.Generator.NamespaceMap.OrderByDescending(p => p.Key.Length).ThenBy(p => p.Key);
foreach (var pair in pairs)
{
// Assuming pair.Key is "abc" and pair.Value is "def" and len(def)=3, generate:
// if (typeName.StartsWith("abc", StringComparison.Ordinal))
// return this.GetType().Assembly.GetType(string.Concat("def", typeName.Substring(3)), true)
resolveTypeFromName.Statements.Add(
new CodeConditionStatement(
new CodeMethodInvokeExpression(
new CodeVariableReferenceExpression("typeName"),
"StartsWith",
new CodePrimitiveExpression(pair.Key),
comparisonExpression
),
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
new CodePropertyReferenceExpression(
new CodeMethodInvokeExpression(
ThisRef,
"GetType"
),
"Assembly"
),
"GetType",
new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(string))),
"Concat",
this.LanguageSpecificNamespace(pair.Value),
new CodeMethodInvokeExpression(
new CodeVariableReferenceExpression("typeName"),
"Substring",
new CodePrimitiveExpression(pair.Key.Length)
)
),
new CodePrimitiveExpression(true)
// This overload doesn't exist in Silverlight
//new CodePrimitiveExpression(!Generator.IsLanguageCaseSensitive)
)
)
)
);
}
resolveTypeFromName.Statements.Add(
new CodeMethodReturnStatement(
new CodePrimitiveExpression(null)));
typeDecl.Members.Add(resolveTypeFromName);
}
CodeMemberMethod resolveNameFromType = new CodeMemberMethod();
resolveNameFromType.Name = "ResolveNameFromType";
resolveNameFromType.Attributes = MemberAttributes.Final | MemberAttributes.Family;
resolveNameFromType.Parameters.Add(new CodeParameterDeclarationExpression(TypeReference.ForType(typeof(Type)), "clientType"));
resolveNameFromType.ReturnType = TypeReference.ForType(typeof(String));
CommentEmitter.EmitSummaryComments(Strings.TypeMapperDescription, resolveNameFromType.Comments);
// NOTE: in this case order also matters, but the length of the CLR
// namespace is what needs to be considered.
var reversePairs = Generator.NamespaceMap.OrderByDescending(p => p.Value.Length).ThenBy(p => p.Key);
foreach (var pair in reversePairs)
{
// Assuming pair.Key is "abc" and pair.Value is "def", generate:
// if (t.Namespace.Equals("def", StringComparison.Ordinal)) return string.Concat("abc.", t.Name);
resolveNameFromType.Statements.Add(
new CodeConditionStatement(
new CodeMethodInvokeExpression(
new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("clientType"),
"Namespace"
),
"Equals",
this.LanguageSpecificNamespace(pair.Value),
comparisonExpression
),
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(string))),
"Concat",
new CodePrimitiveExpression(pair.Key + "."),
new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("clientType"),
"Name"
)
)
)
)
);
}
if (hasInheritance)
{
CodeExpression clientTypeFullName = new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("clientType"),
"FullName");
if (this.Generator.Language == LanguageOption.GenerateVBCode)
{
// return clientType.FullName.Substring(ROOTNAMESPACE.Length);
resolveNameFromType.Statements.Add(
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
clientTypeFullName,
"Substring",
new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("ROOTNAMESPACE"),
"Length"
)
)
)
);
}
else
{
// return clientType.FullName;
resolveNameFromType.Statements.Add(new CodeMethodReturnStatement(clientTypeFullName));
}
}
else
{
resolveNameFromType.Statements.Add(
new CodeMethodReturnStatement(
NullExpression
)
);
}
typeDecl.Members.Add(resolveNameFromType);
}
private void AddRootNamespaceField(CodeTypeDeclaration typeDecl)
{
// Add this field (VB declaration), where known client type name is ClientNS.BikesEntities
// ROOTNAMESPACE = GetType(ClientNS.BikesEntities).Namespace.Remove(GetType(ClientNS.BikesEntities).Namespace.LastIndexOf("ClientNS"))
EntityContainer container = (EntityContainer)this.Item;
string containerNamespace = Generator.GetClientTypeNamespace(this.Generator.GetContainerNamespace(container));
// VB-ism: if namespace and type names are the same (Blah.Blah), GetType(Blah.Blah) fails...
string vbScopedcontainerName = (string.IsNullOrEmpty(containerNamespace) || container.Name.Equals(containerNamespace, StringComparison.OrdinalIgnoreCase))
? container.Name : (containerNamespace + "." + container.Name);
CodeExpression namespaceAccess = new CodePropertyReferenceExpression(
new CodeTypeOfExpression(vbScopedcontainerName),
"Namespace");
CodeMemberField rootNamespaceField = new CodeMemberField(
TypeReference.ForType(typeof(string)),
"ROOTNAMESPACE");
rootNamespaceField.Attributes = MemberAttributes.Static | MemberAttributes.Private;
rootNamespaceField.InitExpression = new CodeMethodInvokeExpression(
namespaceAccess,
"Remove",
new CodeMethodInvokeExpression(
namespaceAccess,
"LastIndexOf",
new CodePrimitiveExpression(containerNamespace)));
typeDecl.Members.Add(rootNamespaceField);
}
private CodeExpression LanguageSpecificNamespace(string ns)
{
if (this.Generator.Language == LanguageOption.GenerateVBCode)
{
return new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(string))),
"Concat",
new CodeVariableReferenceExpression("ROOTNAMESPACE"),
new CodePrimitiveExpression(ns));
}
else
{
return new CodePrimitiveExpression(ns);
}
}
private CodeMemberField CreateEntitySetField(EntitySet set)
{
Debug.Assert(set != null, "Field is Null");
// trying to get
//
// private ObjectQuery _Customers = null;
CodeMemberField codeField = new CodeMemberField();
codeField.Attributes = MemberAttributes.Final | MemberAttributes.Private;
codeField.Name = Utils.FieldNameFromPropName(set.Name);
CodeTypeReference genericParameter = Generator.GetLeastPossibleQualifiedTypeReference(set.ElementType);
codeField.Type = TypeReference.AdoFrameworkGenericClass("DataServiceQuery", genericParameter);
return codeField;
}
private CodeMemberProperty CreateEntitySetProperty(EntitySet set)
{
Debug.Assert(set != null, "Property is Null");
// trying to get
//
// [System.ComponentModel.Browsable(false)]
// public ObjectQuery Customers
// {
// get
// {
// if ((this._Customers == null))
// {
// this._Customers = base.CreateQuery("[Customers]");
// }
// return this._Customers;
// }
// }
CodeMemberProperty codeProperty = new CodeMemberProperty();
codeProperty.Attributes = MemberAttributes.Final | GetEntitySetPropertyAccessibility(set);
codeProperty.Name = set.Name;
codeProperty.HasGet = true;
codeProperty.HasSet = false;
// SQLBUDT 598300: property to get query hides a property on base Context?
if (null != TypeReference.ObjectContextBaseClassType.GetProperty(set.Name))
{
codeProperty.Attributes |= MemberAttributes.New;
}
AttributeEmitter.AddBrowsableAttribute(codeProperty);
CodeTypeReference genericParameter = Generator.GetLeastPossibleQualifiedTypeReference(set.ElementType);
codeProperty.Type = TypeReference.AdoFrameworkGenericClass("DataServiceQuery", genericParameter);
string fieldName = Utils.FieldNameFromPropName(set.Name);
// raise the PropertyGenerated event before proceeding further
PropertyGeneratedEventArgs eventArgs = new PropertyGeneratedEventArgs(set, fieldName, codeProperty.Type);
Generator.RaisePropertyGeneratedEvent(eventArgs);
if (eventArgs.ReturnType == null || !eventArgs.ReturnType.Equals(codeProperty.Type))
{
throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnType(set.Name, Item.Name));
}
List additionalAttributes = eventArgs.AdditionalAttributes;
if (additionalAttributes != null && additionalAttributes.Count > 0)
{
try
{
codeProperty.CustomAttributes.AddRange(additionalAttributes.ToArray());
}
catch (ArgumentNullException e)
{
Generator.AddError(Strings.InvalidAttributeSuppliedForProperty(Item.Name),
ModelBuilderErrorCode.InvalidAttributeSuppliedForProperty,
EdmSchemaErrorSeverity.Error,
e);
}
}
// we need to insert user-specified code before other/existing code, including
// the return statement
List additionalGetStatements = eventArgs.AdditionalGetStatements;
if (additionalGetStatements != null && additionalGetStatements.Count > 0)
{
try
{
codeProperty.GetStatements.AddRange(additionalGetStatements.ToArray());
}
catch (ArgumentNullException e)
{
Generator.AddError(Strings.InvalidGetStatementSuppliedForProperty(Item.Name),
ModelBuilderErrorCode.InvalidGetStatementSuppliedForProperty,
EdmSchemaErrorSeverity.Error,
e);
}
}
codeProperty.GetStatements.Add(
new CodeConditionStatement(
EmitExpressionEqualsNull(new CodeFieldReferenceExpression(ThisRef, fieldName)),
new CodeAssignStatement(
new CodeFieldReferenceExpression(ThisRef, fieldName),
new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(
new CodeBaseReferenceExpression(),
"CreateQuery",
new CodeTypeReference[] { genericParameter }
),
new CodePrimitiveExpression(set.Name)
)
)
)
);
codeProperty.GetStatements.Add(
new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(
ThisRef,
fieldName
)
)
);
// property summary
CommentEmitter.EmitSummaryComments(set, codeProperty.Comments);
return codeProperty;
}
///
/// Create an AddTo-EntitysetName methiod for each entityset in the context.
///
/// EntityContainerEntitySet that we will go over to get the existing entitysets.
/// Method definition
private CodeMemberMethod CreateEntitySetAddObjectProperty(EntitySet set)
{
Debug.Assert(set != null, "Property is Null");
// trying to get
//
// public void AddToCustomer(Customer customer)
// {
// base.AddObject("Customer", customer);
// }
CodeMemberMethod codeMethod = new CodeMemberMethod();
codeMethod.Attributes = MemberAttributes.Final | GetEntityTypeAccessibility(set.ElementType);
codeMethod.Name = ("AddTo" + set.Name);
CodeParameterDeclarationExpression parameter = new CodeParameterDeclarationExpression();
parameter.Type = Generator.GetLeastPossibleQualifiedTypeReference(set.ElementType);
parameter.Name = Utils.CamelCase(set.ElementType.Name);
parameter.Name = Utils.SetSpecialCaseForFxCopOnPropertyName(parameter.Name);
codeMethod.Parameters.Add(parameter);
codeMethod.ReturnType = new CodeTypeReference(typeof(void));
codeMethod.Statements.Add(
new CodeMethodInvokeExpression(
new CodeBaseReferenceExpression(),
"AddObject",
new CodePrimitiveExpression(set.Name),
new CodeFieldReferenceExpression(null, parameter.Name)
)
);
// method summary
CommentEmitter.EmitSummaryComments(set, codeMethod.Comments);
return codeMethod;
}
///
/// return a code expression for invoking OnContextCreated partial method
///
private CodeMethodInvokeExpression OnContextCreatedCodeMethodInvokeExpression()
{
return (new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), _onContextCreatedString, new CodeExpression[] { }));
}
///
/// Returns the type specific SchemaElement
///
private new EntityContainer Item
{
get
{
return base.Item as EntityContainer;
}
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System.CodeDom;
using System.Collections.Generic;
using System.Data.Services.Design;
using System.Data.Services.Design.Common;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Diagnostics;
using System.Linq;
namespace System.Data.EntityModel.Emitters
{
///
/// This class is responsible for emiting the code for the EntityContainer schema element
///
internal sealed class EntityContainerEmitter : SchemaTypeEmitter
{
#region Fields
private const string _onContextCreatedString = "OnContextCreated";
#endregion
#region Constructors
///
///
///
///
///
public EntityContainerEmitter(ClientApiGenerator generator, EntityContainer entityContainer)
: base(generator, entityContainer)
{
}
#endregion
#region Properties, Methods, Events & Delegates
///
/// Creates the CodeTypeDeclarations necessary to generate the code for the EntityContainer schema element
///
///
public override CodeTypeDeclarationCollection EmitApiClass()
{
Validate(); // emitter-specific validation
// declare the new class
// public partial class LOBScenario : ObjectContext
CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(Item.Name);
typeDecl.IsPartial = true;
// raise the TypeGenerated event
CodeTypeReference objectContextTypeRef = TypeReference.ObjectContext;
TypeGeneratedEventArgs eventArgs = new TypeGeneratedEventArgs(Item, objectContextTypeRef);
Generator.RaiseTypeGeneratedEvent(eventArgs);
if (eventArgs.BaseType != null && !eventArgs.BaseType.Equals(objectContextTypeRef))
{
typeDecl.BaseTypes.Add(eventArgs.BaseType);
}
else
{
typeDecl.BaseTypes.Add(TypeReference.ObjectContext);
}
AddInterfaces(Item.Name, typeDecl, eventArgs.AdditionalInterfaces);
CommentEmitter.EmitSummaryComments(Item, typeDecl.Comments);
EmitTypeAttributes(Item.Name, typeDecl, eventArgs.AdditionalAttributes);
bool needTypeMapper = (0 < this.Generator.NamespaceMap.Count);
var q = from a in this.Generator.EdmItemCollection.GetItems()
where (a.BaseType != null) &&
(a.BuiltInTypeKind == BuiltInTypeKind.ComplexType || a.BuiltInTypeKind == BuiltInTypeKind.EntityType)
select a;
bool hasInheritance = (null != q.FirstOrDefault());
CreateConstructors(typeDecl, needTypeMapper, hasInheritance);
// adding partial OnContextCreated method
CreateContextPartialMethods(typeDecl);
if (needTypeMapper || hasInheritance)
{
CreateTypeMappingMethods(typeDecl, needTypeMapper, hasInheritance);
}
foreach (EntitySetBase entitySetBase in Item.BaseEntitySets)
{
if (Helper.IsEntitySet(entitySetBase))
{
EntitySet set = (EntitySet)entitySetBase;
CodeMemberProperty codeProperty = CreateEntitySetProperty(set);
typeDecl.Members.Add(codeProperty);
CodeMemberField codeField = CreateEntitySetField(set);
typeDecl.Members.Add(codeField);
}
}
foreach (EntitySetBase entitySetBase in Item.BaseEntitySets)
{
if (Helper.IsEntitySet(entitySetBase))
{
EntitySet set = (EntitySet)entitySetBase;
CodeMemberMethod codeProperty = CreateEntitySetAddObjectProperty(set);
typeDecl.Members.Add(codeProperty);
}
}
// additional members, if provided by the event subscriber
AddMembers(Item.Name, typeDecl, eventArgs.AdditionalMembers);
CodeTypeDeclarationCollection typeDecls = new CodeTypeDeclarationCollection();
typeDecls.Add(typeDecl);
return typeDecls;
}
///
/// Emitter-specific validation: check if there exist entity containers and
/// entity sets that have the same name but differ in case
///
protected override void Validate()
{
base.Validate();
Generator.VerifyLanguageCaseSensitiveCompatibilityForEntitySet(Item);
VerifyEntityTypeAndSetAccessibilityCompatability();
}
///
/// Verify that Entity Set and Type have compatible accessibilty.
/// They are compatible if the generated code will compile.
///
private void VerifyEntityTypeAndSetAccessibilityCompatability()
{
foreach (EntitySetBase entitySetBase in Item.BaseEntitySets)
{
if (Helper.IsEntitySet(entitySetBase))
{
EntitySet set = (EntitySet)entitySetBase;
if (!AreTypeAndSetAccessCompatible(GetEntityTypeAccessibility(set.ElementType), GetEntitySetPropertyAccessibility(set)))
{
Generator.AddError(
Strings.EntityTypeAndSetAccessibilityConflict(
set.ElementType.Name, GetAccessibilityCsdlStringFromMemberAttribute(GetEntityTypeAccessibility(set.ElementType)), set.Name, GetAccessibilityCsdlStringFromMemberAttribute(GetEntitySetPropertyAccessibility(set))),
ModelBuilderErrorCode.EntityTypeAndSetAccessibilityConflict,
EdmSchemaErrorSeverity.Error);
}
}
}
}
///
/// Tells whether Entity Type's specified accessibility and Entity Set Property's specified Accessibility will work together (compile) when codegen'd.
/// False if (Type is internal and Set's Property is Public OR, type is internal and Set's property is protected).
/// True otherwise
///
private bool AreTypeAndSetAccessCompatible(MemberAttributes typeAccess, MemberAttributes setAccess)
{
return !(typeAccess == MemberAttributes.Assembly && (setAccess == MemberAttributes.Public || setAccess == MemberAttributes.Family));
}
///
/// Creates the necessary constructors for the entity container.
///
private void CreateConstructors(CodeTypeDeclaration typeDecl, bool setupTypeMapper, bool hasInheritance)
{
// Constructor that takes a uri
//
// public ctor(System.Uri serviceRoot)
// : base(serviceRoot)
// {
// this.OnContextCreated();
// }
CodeConstructor connectionWorkspaceCtor = new CodeConstructor();
connectionWorkspaceCtor.Attributes = MemberAttributes.Public;
CodeParameterDeclarationExpression connectionParam = new CodeParameterDeclarationExpression(TypeReference.FromString("System.Uri", true), "serviceRoot");
connectionWorkspaceCtor.Parameters.Add(connectionParam);
connectionWorkspaceCtor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression(connectionParam.Name));
CommentEmitter.EmitSummaryComments(Strings.CtorSummaryComment(Item.Name), connectionWorkspaceCtor.Comments);
// If we have an externally provided namespage (e.g. from Visual Studio), we need to
// inject a type-mapper because type names won't match between client and server
if (setupTypeMapper || hasInheritance)
{
connectionWorkspaceCtor.Statements.Add(
new CodeAssignStatement(
new CodeFieldReferenceExpression(ThisRef, "ResolveName"),
new CodeDelegateCreateExpression(
TypeReference.ForType(typeof(Func<,>), TypeReference.ForType(typeof(Type)), TypeReference.ForType(typeof(String))),
ThisRef,
"ResolveNameFromType"
)
)
);
}
if (setupTypeMapper)
{
connectionWorkspaceCtor.Statements.Add(
new CodeAssignStatement(
new CodeFieldReferenceExpression(ThisRef, "ResolveType"),
new CodeDelegateCreateExpression(
TypeReference.ForType(typeof(Func<,>), TypeReference.ForType(typeof(String)), TypeReference.ForType(typeof(Type))),
ThisRef,
"ResolveTypeFromName"
)
)
);
}
connectionWorkspaceCtor.Statements.Add(OnContextCreatedCodeMethodInvokeExpression());
typeDecl.Members.Add(connectionWorkspaceCtor);
}
///
/// Adds the OnContextCreated partial method for the entity container.
///
private void CreateContextPartialMethods(CodeTypeDeclaration typeDecl)
{
CodeMemberMethod onContextCreatedPartialMethod = new CodeMemberMethod();
onContextCreatedPartialMethod.Name = _onContextCreatedString;
onContextCreatedPartialMethod.ReturnType = new CodeTypeReference(typeof(void));
onContextCreatedPartialMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
typeDecl.Members.Add(onContextCreatedPartialMethod);
Generator.FixUps.Add(new FixUp(Item.Name + "." + _onContextCreatedString, FixUpType.MarkAbstractMethodAsPartial));
}
private void CreateTypeMappingMethods(CodeTypeDeclaration typeDecl, bool needTypeMapper, bool hasInheritance)
{
// Special case to compensate for VB's "root namespace" feature.
if (this.Generator.Language == LanguageOption.GenerateVBCode)
{
AddRootNamespaceField(typeDecl);
}
CodeExpression comparisonExpression = new CodePropertyReferenceExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(StringComparison))),
Enum.GetName(typeof(StringComparison), this.Generator.LanguageAppropriateStringComparer));
if (needTypeMapper)
{
CodeMemberMethod resolveTypeFromName = new CodeMemberMethod();
resolveTypeFromName.Name = "ResolveTypeFromName";
resolveTypeFromName.Attributes = MemberAttributes.Final | MemberAttributes.Family;
resolveTypeFromName.Parameters.Add(new CodeParameterDeclarationExpression(TypeReference.ForType(typeof(string)), "typeName"));
resolveTypeFromName.ReturnType = TypeReference.ForType(typeof(Type));
CommentEmitter.EmitSummaryComments(Strings.TypeMapperDescription, resolveTypeFromName.Comments);
// NOTE: since multiple namespaces can have the same prefix and match the namespace
// prefix condition, it's important that the prefix check is done is prefix-length
// order, starting with the longest prefix.
var pairs = this.Generator.NamespaceMap.OrderByDescending(p => p.Key.Length).ThenBy(p => p.Key);
foreach (var pair in pairs)
{
// Assuming pair.Key is "abc" and pair.Value is "def" and len(def)=3, generate:
// if (typeName.StartsWith("abc", StringComparison.Ordinal))
// return this.GetType().Assembly.GetType(string.Concat("def", typeName.Substring(3)), true)
resolveTypeFromName.Statements.Add(
new CodeConditionStatement(
new CodeMethodInvokeExpression(
new CodeVariableReferenceExpression("typeName"),
"StartsWith",
new CodePrimitiveExpression(pair.Key),
comparisonExpression
),
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
new CodePropertyReferenceExpression(
new CodeMethodInvokeExpression(
ThisRef,
"GetType"
),
"Assembly"
),
"GetType",
new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(string))),
"Concat",
this.LanguageSpecificNamespace(pair.Value),
new CodeMethodInvokeExpression(
new CodeVariableReferenceExpression("typeName"),
"Substring",
new CodePrimitiveExpression(pair.Key.Length)
)
),
new CodePrimitiveExpression(true)
// This overload doesn't exist in Silverlight
//new CodePrimitiveExpression(!Generator.IsLanguageCaseSensitive)
)
)
)
);
}
resolveTypeFromName.Statements.Add(
new CodeMethodReturnStatement(
new CodePrimitiveExpression(null)));
typeDecl.Members.Add(resolveTypeFromName);
}
CodeMemberMethod resolveNameFromType = new CodeMemberMethod();
resolveNameFromType.Name = "ResolveNameFromType";
resolveNameFromType.Attributes = MemberAttributes.Final | MemberAttributes.Family;
resolveNameFromType.Parameters.Add(new CodeParameterDeclarationExpression(TypeReference.ForType(typeof(Type)), "clientType"));
resolveNameFromType.ReturnType = TypeReference.ForType(typeof(String));
CommentEmitter.EmitSummaryComments(Strings.TypeMapperDescription, resolveNameFromType.Comments);
// NOTE: in this case order also matters, but the length of the CLR
// namespace is what needs to be considered.
var reversePairs = Generator.NamespaceMap.OrderByDescending(p => p.Value.Length).ThenBy(p => p.Key);
foreach (var pair in reversePairs)
{
// Assuming pair.Key is "abc" and pair.Value is "def", generate:
// if (t.Namespace.Equals("def", StringComparison.Ordinal)) return string.Concat("abc.", t.Name);
resolveNameFromType.Statements.Add(
new CodeConditionStatement(
new CodeMethodInvokeExpression(
new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("clientType"),
"Namespace"
),
"Equals",
this.LanguageSpecificNamespace(pair.Value),
comparisonExpression
),
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(string))),
"Concat",
new CodePrimitiveExpression(pair.Key + "."),
new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("clientType"),
"Name"
)
)
)
)
);
}
if (hasInheritance)
{
CodeExpression clientTypeFullName = new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("clientType"),
"FullName");
if (this.Generator.Language == LanguageOption.GenerateVBCode)
{
// return clientType.FullName.Substring(ROOTNAMESPACE.Length);
resolveNameFromType.Statements.Add(
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
clientTypeFullName,
"Substring",
new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("ROOTNAMESPACE"),
"Length"
)
)
)
);
}
else
{
// return clientType.FullName;
resolveNameFromType.Statements.Add(new CodeMethodReturnStatement(clientTypeFullName));
}
}
else
{
resolveNameFromType.Statements.Add(
new CodeMethodReturnStatement(
NullExpression
)
);
}
typeDecl.Members.Add(resolveNameFromType);
}
private void AddRootNamespaceField(CodeTypeDeclaration typeDecl)
{
// Add this field (VB declaration), where known client type name is ClientNS.BikesEntities
// ROOTNAMESPACE = GetType(ClientNS.BikesEntities).Namespace.Remove(GetType(ClientNS.BikesEntities).Namespace.LastIndexOf("ClientNS"))
EntityContainer container = (EntityContainer)this.Item;
string containerNamespace = Generator.GetClientTypeNamespace(this.Generator.GetContainerNamespace(container));
// VB-ism: if namespace and type names are the same (Blah.Blah), GetType(Blah.Blah) fails...
string vbScopedcontainerName = (string.IsNullOrEmpty(containerNamespace) || container.Name.Equals(containerNamespace, StringComparison.OrdinalIgnoreCase))
? container.Name : (containerNamespace + "." + container.Name);
CodeExpression namespaceAccess = new CodePropertyReferenceExpression(
new CodeTypeOfExpression(vbScopedcontainerName),
"Namespace");
CodeMemberField rootNamespaceField = new CodeMemberField(
TypeReference.ForType(typeof(string)),
"ROOTNAMESPACE");
rootNamespaceField.Attributes = MemberAttributes.Static | MemberAttributes.Private;
rootNamespaceField.InitExpression = new CodeMethodInvokeExpression(
namespaceAccess,
"Remove",
new CodeMethodInvokeExpression(
namespaceAccess,
"LastIndexOf",
new CodePrimitiveExpression(containerNamespace)));
typeDecl.Members.Add(rootNamespaceField);
}
private CodeExpression LanguageSpecificNamespace(string ns)
{
if (this.Generator.Language == LanguageOption.GenerateVBCode)
{
return new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(TypeReference.ForType(typeof(string))),
"Concat",
new CodeVariableReferenceExpression("ROOTNAMESPACE"),
new CodePrimitiveExpression(ns));
}
else
{
return new CodePrimitiveExpression(ns);
}
}
private CodeMemberField CreateEntitySetField(EntitySet set)
{
Debug.Assert(set != null, "Field is Null");
// trying to get
//
// private ObjectQuery _Customers = null;
CodeMemberField codeField = new CodeMemberField();
codeField.Attributes = MemberAttributes.Final | MemberAttributes.Private;
codeField.Name = Utils.FieldNameFromPropName(set.Name);
CodeTypeReference genericParameter = Generator.GetLeastPossibleQualifiedTypeReference(set.ElementType);
codeField.Type = TypeReference.AdoFrameworkGenericClass("DataServiceQuery", genericParameter);
return codeField;
}
private CodeMemberProperty CreateEntitySetProperty(EntitySet set)
{
Debug.Assert(set != null, "Property is Null");
// trying to get
//
// [System.ComponentModel.Browsable(false)]
// public ObjectQuery Customers
// {
// get
// {
// if ((this._Customers == null))
// {
// this._Customers = base.CreateQuery("[Customers]");
// }
// return this._Customers;
// }
// }
CodeMemberProperty codeProperty = new CodeMemberProperty();
codeProperty.Attributes = MemberAttributes.Final | GetEntitySetPropertyAccessibility(set);
codeProperty.Name = set.Name;
codeProperty.HasGet = true;
codeProperty.HasSet = false;
// SQLBUDT 598300: property to get query hides a property on base Context?
if (null != TypeReference.ObjectContextBaseClassType.GetProperty(set.Name))
{
codeProperty.Attributes |= MemberAttributes.New;
}
AttributeEmitter.AddBrowsableAttribute(codeProperty);
CodeTypeReference genericParameter = Generator.GetLeastPossibleQualifiedTypeReference(set.ElementType);
codeProperty.Type = TypeReference.AdoFrameworkGenericClass("DataServiceQuery", genericParameter);
string fieldName = Utils.FieldNameFromPropName(set.Name);
// raise the PropertyGenerated event before proceeding further
PropertyGeneratedEventArgs eventArgs = new PropertyGeneratedEventArgs(set, fieldName, codeProperty.Type);
Generator.RaisePropertyGeneratedEvent(eventArgs);
if (eventArgs.ReturnType == null || !eventArgs.ReturnType.Equals(codeProperty.Type))
{
throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnType(set.Name, Item.Name));
}
List additionalAttributes = eventArgs.AdditionalAttributes;
if (additionalAttributes != null && additionalAttributes.Count > 0)
{
try
{
codeProperty.CustomAttributes.AddRange(additionalAttributes.ToArray());
}
catch (ArgumentNullException e)
{
Generator.AddError(Strings.InvalidAttributeSuppliedForProperty(Item.Name),
ModelBuilderErrorCode.InvalidAttributeSuppliedForProperty,
EdmSchemaErrorSeverity.Error,
e);
}
}
// we need to insert user-specified code before other/existing code, including
// the return statement
List additionalGetStatements = eventArgs.AdditionalGetStatements;
if (additionalGetStatements != null && additionalGetStatements.Count > 0)
{
try
{
codeProperty.GetStatements.AddRange(additionalGetStatements.ToArray());
}
catch (ArgumentNullException e)
{
Generator.AddError(Strings.InvalidGetStatementSuppliedForProperty(Item.Name),
ModelBuilderErrorCode.InvalidGetStatementSuppliedForProperty,
EdmSchemaErrorSeverity.Error,
e);
}
}
codeProperty.GetStatements.Add(
new CodeConditionStatement(
EmitExpressionEqualsNull(new CodeFieldReferenceExpression(ThisRef, fieldName)),
new CodeAssignStatement(
new CodeFieldReferenceExpression(ThisRef, fieldName),
new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(
new CodeBaseReferenceExpression(),
"CreateQuery",
new CodeTypeReference[] { genericParameter }
),
new CodePrimitiveExpression(set.Name)
)
)
)
);
codeProperty.GetStatements.Add(
new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(
ThisRef,
fieldName
)
)
);
// property summary
CommentEmitter.EmitSummaryComments(set, codeProperty.Comments);
return codeProperty;
}
///
/// Create an AddTo-EntitysetName methiod for each entityset in the context.
///
/// EntityContainerEntitySet that we will go over to get the existing entitysets.
/// Method definition
private CodeMemberMethod CreateEntitySetAddObjectProperty(EntitySet set)
{
Debug.Assert(set != null, "Property is Null");
// trying to get
//
// public void AddToCustomer(Customer customer)
// {
// base.AddObject("Customer", customer);
// }
CodeMemberMethod codeMethod = new CodeMemberMethod();
codeMethod.Attributes = MemberAttributes.Final | GetEntityTypeAccessibility(set.ElementType);
codeMethod.Name = ("AddTo" + set.Name);
CodeParameterDeclarationExpression parameter = new CodeParameterDeclarationExpression();
parameter.Type = Generator.GetLeastPossibleQualifiedTypeReference(set.ElementType);
parameter.Name = Utils.CamelCase(set.ElementType.Name);
parameter.Name = Utils.SetSpecialCaseForFxCopOnPropertyName(parameter.Name);
codeMethod.Parameters.Add(parameter);
codeMethod.ReturnType = new CodeTypeReference(typeof(void));
codeMethod.Statements.Add(
new CodeMethodInvokeExpression(
new CodeBaseReferenceExpression(),
"AddObject",
new CodePrimitiveExpression(set.Name),
new CodeFieldReferenceExpression(null, parameter.Name)
)
);
// method summary
CommentEmitter.EmitSummaryComments(set, codeMethod.Comments);
return codeMethod;
}
///
/// return a code expression for invoking OnContextCreated partial method
///
private CodeMethodInvokeExpression OnContextCreatedCodeMethodInvokeExpression()
{
return (new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), _onContextCreatedString, new CodeExpression[] { }));
}
///
/// Returns the type specific SchemaElement
///
private new EntityContainer Item
{
get
{
return base.Item as EntityContainer;
}
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- VisualStyleElement.cs
- ConsoleCancelEventArgs.cs
- FixedHyperLink.cs
- ZipIOCentralDirectoryBlock.cs
- ServiceObjectContainer.cs
- DiscreteKeyFrames.cs
- CAGDesigner.cs
- ServiceDebugBehavior.cs
- OneToOneMappingSerializer.cs
- Array.cs
- AmbientValueAttribute.cs
- BinaryObjectReader.cs
- AssemblyNameProxy.cs
- basecomparevalidator.cs
- StringReader.cs
- PermissionAttributes.cs
- DocumentXmlWriter.cs
- Constraint.cs
- CodeGroup.cs
- OutputCacheSettings.cs
- XhtmlCssHandler.cs
- CollectionBuilder.cs
- MethodAccessException.cs
- XPathChildIterator.cs
- ValidatedControlConverter.cs
- QuotedPrintableStream.cs
- TextHidden.cs
- LockCookie.cs
- FileVersion.cs
- MarkupCompiler.cs
- StyleCollectionEditor.cs
- SyndicationDeserializer.cs
- MailHeaderInfo.cs
- ExpressionBindingCollection.cs
- InputScopeConverter.cs
- TextDpi.cs
- ClassDataContract.cs
- InheritanceAttribute.cs
- PageParserFilter.cs
- CodeEventReferenceExpression.cs
- CommandManager.cs
- LockedAssemblyCache.cs
- KeyInfo.cs
- DependencyPropertyValueSerializer.cs
- PenCursorManager.cs
- StructuralObject.cs
- FixedSOMImage.cs
- StrokeSerializer.cs
- ResolveNameEventArgs.cs
- DictionarySurrogate.cs
- DBCommandBuilder.cs
- Queue.cs
- SortedSetDebugView.cs
- IndexingContentUnit.cs
- PathGradientBrush.cs
- UnionCodeGroup.cs
- TableDetailsCollection.cs
- keycontainerpermission.cs
- FrameDimension.cs
- XmlArrayItemAttribute.cs
- TextRangeEditTables.cs
- Vector3DAnimation.cs
- XPathDocument.cs
- SapiGrammar.cs
- ApplicationCommands.cs
- SystemWebSectionGroup.cs
- DataServiceResponse.cs
- Matrix.cs
- EntityParameterCollection.cs
- Misc.cs
- OAVariantLib.cs
- BaseCollection.cs
- TextTreeTextBlock.cs
- ContextMenuAutomationPeer.cs
- RelatedPropertyManager.cs
- AuditLevel.cs
- LifetimeServices.cs
- FlowDocumentPaginator.cs
- RoleManagerEventArgs.cs
- IdentityNotMappedException.cs
- HotSpotCollection.cs
- WebEventTraceProvider.cs
- XmlTypeMapping.cs
- Renderer.cs
- MD5.cs
- PropertyReferenceExtension.cs
- IsolatedStoragePermission.cs
- SystemParameters.cs
- SmtpNegotiateAuthenticationModule.cs
- CallInfo.cs
- SymbolPair.cs
- AddInBase.cs
- DesignerCategoryAttribute.cs
- IxmlLineInfo.cs
- MimeTypeMapper.cs
- SerializationInfo.cs
- AdRotator.cs
- Pair.cs
- Listbox.cs
- Debug.cs