internal CodeGen(ICodeGenContext context)
		{
			this.context = context;
			this.cg = context as ConstructorGen;

			if (cg != null && cg.IsStatic)
				// #14 - cg is relevant for instance constructors - it wreaks havoc in a static constructor
				cg = null;

			il = context.GetILGenerator();
		}
        /// <summary>
        /// Generates the business logic class type.  We override this to control the base class and imports
        /// </summary>
        /// <param name="codeGenContext">The context to use to generate code.</param>
        /// <param name="codeNamespace">The namespace into which to generate code.</param>
        /// <param name="className">The name to use for the class.</param>
        /// <returns>The new type</returns>
        protected override CodeTypeDeclaration CreateBusinessLogicClass(ICodeGenContext codeGenContext, CodeNamespace codeNamespace, string className)
        {
            // Add an import for our domain service
            foreach (string import in BusinessLogicClassConstants.LinqToEntitiesImports)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(import));
            }

            // Add an import for the namespace of the DomainContext
            if (this.ContextType.Namespace != codeNamespace.Name)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(this.ContextType.Namespace));
            }

            // Add to the set of known references
            codeGenContext.AddReference(typeof(ObjectContext).Assembly.FullName);
            codeGenContext.AddReference(typeof(LinqToEntitiesDomainService<>).Assembly.FullName);

            CodeTypeDeclaration businessLogicClass = CodeGenUtilities.CreateTypeDeclaration(className, codeNamespace.Name);
            CodeTypeReference baseClass = new CodeTypeReference(BusinessLogicClassConstants.LinqToEntitiesDomainServiceTypeName, new CodeTypeReference(this.ContextType.Name));
            businessLogicClass.BaseTypes.Add(baseClass);
            return businessLogicClass;
        }
        /// <summary>
        /// Generates the update domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The code gen context.></param>
        /// <param name="businessLogicClass">The business logic class.</param>
        /// <param name="entity">The entity.</param>
        protected override void GenerateUpdateMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            string currentParameterName = "current" + entity.ClrType.Name;

            // public void Update$EntityName$($entityType$ current)
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);

            LinqToSqlEntity ltsEntity = (LinqToSqlEntity)entity;
            method.Name = "Update" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

            // parameter declaration
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, currentParameterName));

            if (!ltsEntity.HasTimestampMember)
            {
                // this.ChangeSet.GetOriginal(current)
                CodeExpression changeSetRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ChangeSet");
                CodeMethodReferenceExpression getOrigMethodRef = new CodeMethodReferenceExpression(changeSetRef, "GetOriginal");
                CodeMethodInvokeExpression changeSetGetOrig = new CodeMethodInvokeExpression(getOrigMethodRef, new CodeArgumentReferenceExpression(currentParameterName));

                // this.DataContext.$TablePropertyName$.Attach(current, this.ChangeSet.GetOriginal(current))
                CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DataContext");
                CodeExpression tableRef = new CodePropertyReferenceExpression(contextRef, ltsEntity.TablePropertyName);
                CodeMethodInvokeExpression attachCall = new CodeMethodInvokeExpression(tableRef, "Attach", new CodeArgumentReferenceExpression(currentParameterName), changeSetGetOrig);
                method.Statements.Add(attachCall);
            }
            else
            {
                // this.DataContext.$TablePropertyName$.Attach(current, true)
                CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DataContext");
                CodeExpression tableRef = new CodePropertyReferenceExpression(contextRef, ltsEntity.TablePropertyName);
                CodeMethodInvokeExpression attachCall = new CodeMethodInvokeExpression(tableRef, "Attach", new CodeArgumentReferenceExpression(currentParameterName), new CodePrimitiveExpression(true));
                method.Statements.Add(attachCall);
            }
        }
        /// <summary>
        /// Creates the business logic class.  Overridden to add imports, base class, etc.
        /// </summary>
        /// <param name="codeGenContext">The code gen context.></param>
        /// <param name="codeNamespace">The namespace name.</param>
        /// <param name="className">The class name.</param>
        /// <returns>the new type</returns>
        protected override CodeTypeDeclaration CreateBusinessLogicClass(ICodeGenContext codeGenContext, CodeNamespace codeNamespace, string className)
        {
            Debug.Assert(LinqToSqlContext.linqToSqlDomainServiceAssemblyPath != null, "Unexpected method call when LinqToSqlDomainService assembly path has not been initialized!");

            if (LinqToSqlContext.linqToSqlDomainServiceAssemblyPath == null)
            {
                return null;
            }

            // Add an import for our domain service
            foreach (string import in BusinessLogicClassConstants.LinqToSqlImports)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(import));
            }

            // Add an import for the namespace of the DomainContext
            if (this.ContextType.Namespace != codeNamespace.Name)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(this.ContextType.Namespace));
            }

            // Add to the set of known references
            codeGenContext.AddReference(typeof(DataContext).Assembly.FullName);
            codeGenContext.AddReference(LinqToSqlContext.linqToSqlDomainServiceAssemblyPath);

            CodeTypeDeclaration businessLogicClass = CodeGenUtilities.CreateTypeDeclaration(className, codeNamespace.Name);
            CodeTypeReference baseClass = new CodeTypeReference(BusinessLogicClassConstants.LinqToSqlDomainServiceTypeName, new CodeTypeReference(this.ContextType.Name));
            businessLogicClass.BaseTypes.Add(baseClass);
            return businessLogicClass;
        }
Пример #5
0
 /// <summary>
 /// Creates the "insert" method for the entity
 /// </summary>
 /// <param name="codeGenContext">The context into which to generate code.  It cannot be null.</param>
 /// <param name="businessLogicClass">The class into which to generate the method</param>
 /// <param name="entity">The entity which will be affected by this method</param>
 protected virtual void GenerateInsertMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
 {
 }
Пример #6
0
 internal void RegisterForCompletion(ICodeGenContext routine)
 {
     definitionQueue.Add(routine);
     completionQueue.Add(routine);
 }
        /// <summary>
        /// Tests whether the specified helper member has been generated, and if not, invokes
        /// a callback to generate it.
        /// </summary>
        /// <param name="codeGenContext">The context in which we are generating code.</param>
        /// <param name="businessLogicClass">The class containing the generated code.</param>
        /// <param name="helperMemberName">The name of the helper member.</param>
        /// <param name="generatorCallback">Callback that will create this helper if it does not yet exist.</param>
        public static void GenerateHelperMemberIfNecessary(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, string helperMemberName, Func<CodeTypeMember> generatorCallback)
        {
            System.Diagnostics.Debug.Assert(codeGenContext != null, "CodeGenContext cannot be null"); 
            System.Diagnostics.Debug.Assert(businessLogicClass != null, "BusinessLogicClass cannot be null");
            System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(helperMemberName), "Helper member name cannot be empty.");
            System.Diagnostics.Debug.Assert(generatorCallback != null, "callback cannot be null");

            Dictionary<string, CodeTypeMember> memberDictionary = BusinessLogicContext.GetHelperMemberDictionary(businessLogicClass);
            if (!memberDictionary.ContainsKey(helperMemberName))
            {
                CodeTypeMember member = generatorCallback();
                if (member != null)
                {
                    memberDictionary[helperMemberName] = member;
                }
            }
        }
 /// <summary>
 /// Generates additional metadata classes for the given entity if necessary.
 /// </summary>
 /// <param name="codeGenContext">The context to use to generate code.</param>
 /// <param name="optionalSuffix">If not null, optional suffix to class name and namespace</param>
 /// <param name="entity">The entity for which to generate the additional metadata.</param>
 /// <returns><c>true</c> means at least some code was generated.</returns>
 /// <remarks>
 /// This default implementation of the virtual method does not generate any additional classes. It needs to be overridden in the derived
 /// classes to generate additional metadata classes if necessary.
 /// </remarks>
 protected virtual bool GenerateAdditionalMetadataClasses(ICodeGenContext codeGenContext, string optionalSuffix, BusinessLogicEntity entity)
 {
     return false;
 }
 /// <summary>
 /// Creates the entire business logic class in the specified namespace
 /// </summary>
 /// <param name="codeGenContext">The context into which to generate code.  It cannot be null.</param>
 /// <param name="codeNamespace">The namespace object into which the type should be defined.</param>
 /// <param name="className">The name of the class.  It cannot be null.</param>
 /// <returns>A new CodeTypeDeclaration for the generated class.</returns>
 protected virtual CodeTypeDeclaration CreateBusinessLogicClass(ICodeGenContext codeGenContext, CodeNamespace codeNamespace, string className)
 {
     CodeTypeDeclaration businessLogicClass = CodeGenUtilities.CreateTypeDeclaration(className, codeNamespace.Name);
     businessLogicClass.BaseTypes.Add(BusinessLogicClassConstants.DomainServiceTypeName);
     return businessLogicClass;
 }
        /// <summary>
        /// Generates the select domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The code generation context.</param>
        /// <param name="businessLogicClass">Contains the business logic.</param>
        /// <param name="entity">The entity.</param>
        /// <returns>The newly created method.</returns>
        protected override CodeMemberMethod GenerateSelectMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            CodeMemberMethod method = null;
            LinqToEntitiesEntity efDbEntity = entity as LinqToEntitiesEntity;

            if (efDbEntity != null && efDbEntity.DefaultObjectSetName != null)
            {
                // public IQueryable<$entityType$> GetEntities()
                method = new CodeMemberMethod();
                businessLogicClass.Members.Add(method);

                // Add developer comment explaining they can add additional parameters
                method.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_Query_Method_Remarks, false));

                // And for EF, we add an additional comment warning they need to add ordering if they want paging
                string queryComment = String.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Query_Method_EF_Remarks, efDbEntity.DefaultObjectSetName);
                method.Comments.Add(new CodeCommentStatement(queryComment, false));

                method.Name = "Get" + CodeGenUtilities.MakeLegalEntityName(efDbEntity.DefaultObjectSetName);
                method.ReturnType = new CodeTypeReference("IQueryable", new CodeTypeReference(entity.Name));
                method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

                // return this.DbContext.$TablePropertyName$
                CodeExpression contextExpr = LinqToEntitiesDbContext.GetContextReferenceExpression();
                CodeExpression expr = new CodePropertyReferenceExpression(contextExpr, efDbEntity.DefaultObjectSetName);
                CodeMethodReturnStatement returnStmt = new CodeMethodReturnStatement(expr);
                method.Statements.Add(returnStmt);
            }
            return method;
        }
        /// <summary>
        /// Generates the business logic class type.  We override this to control the base class and imports
        /// </summary>
        /// <param name="codeGenContext">The context to use to generate code.</param>
        /// <param name="codeNamespace">The namespace into which to generate code.</param>
        /// <param name="className">The name to use for the class.</param>
        /// <returns>The new type</returns>
        protected override CodeTypeDeclaration CreateBusinessLogicClass(ICodeGenContext codeGenContext, CodeNamespace codeNamespace, string className)
        {
            // Add an import for our domain service
            foreach (string import in BusinessLogicClassConstants.LinqToEntitiesDbImports)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(import));
            }

            // Add an import for the namespace of the DomainContext
            if (this.ContextType.Namespace != codeNamespace.Name)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(BusinessLogicClassConstants.DbContextNamespace));
                codeNamespace.Imports.Add(new CodeNamespaceImport(this.ContextType.Namespace));
            }

            // Add to the set of known references
            codeGenContext.AddReference(typeof(EntityState).Assembly.FullName);
            
            // We used to add OpenRiaServices.DomainServices.EntityFramework, but due to
            // vstfdevdiv/DevDiv2 Bug 442272 - Domain Service Wizard failing when an EF DbContext is selected,
            // we need to avoid doing that.

            if (DbContextUtilities.DbContextTypeReference != null)
            {
                codeGenContext.AddReference(DbContextUtilities.DbContextTypeReference.Assembly.FullName);
            }
            
            CodeTypeDeclaration businessLogicClass = CodeGenUtilities.CreateTypeDeclaration(className, codeNamespace.Name);
            CodeTypeReference baseClass = new CodeTypeReference(BusinessLogicClassConstants.DbDomainServiceTypeName, new CodeTypeReference(this.ContextType.Name));
            businessLogicClass.BaseTypes.Add(baseClass);
            return businessLogicClass;
        }
 /// <summary>
 /// Tests whether we need to generate the GetEntityState helper method.  We do for POCO types.
 /// If we determine we need to generate that helper, this method generates it and adds it to
 /// the list of helper methods that will be appended to the generated code.
 /// </summary>
 /// <param name="codeGenContext">The context in which we are generating code.</param>
 /// <param name="businessLogicClass">The class containing the generated code.</param>
 /// <param name="entity">The entity that we need to test to determine whether the helper is needed.</param>
 /// <returns><c>true</c> means the helper should be used.</returns>
 private static bool GenerateGetEntityStateIfNecessary(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
 {
     if (typeof(EntityObject).IsAssignableFrom(entity.ClrType))
     {
         return false;
     }
     BusinessLogicContext.GenerateHelperMemberIfNecessary(codeGenContext, businessLogicClass, LinqToEntitiesContext.GetEntityStateHelperMethodName, () =>
     {
         return LinqToEntitiesContext.GenerateGetEntityState(codeGenContext, businessLogicClass);
     });
     return true;
 }
        /// <summary>
        /// Generates the GetEntityState helper method that allows POCO types to retrieve their
        /// entity state from the contect.  It is not available on the POCO types directly.
        /// </summary>
        /// <param name="codeGenContext">The context in which we are generating code.</param>
        /// <param name="businessLogicClass">The class we are generating.</param>
        /// <returns>The <see cref="CodeTypeMember"/> containing the helper method.</returns>
        private static CodeTypeMember GenerateGetEntityState(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass)
        {
            // Add an import for System.Data.Objects
            CodeNamespace codeNamespace = codeGenContext.GetNamespace(businessLogicClass);
            if (codeNamespace != null)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport("System.Data.Objects"));
            }

            //private EntityState GetEntityState(object entity)
            //{
            //    ObjectStateEntry stateEntry = null;
            //    if (!this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(entity, out stateEntry))
            //    {
            //        return EntityState.Detached;
            //    }
            //    return stateEntry.State;
            //}

            // Declaration
            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = LinqToEntitiesContext.GetEntityStateHelperMethodName;
            method.ReturnType = new CodeTypeReference(typeof(EntityState).Name);
            method.Attributes = MemberAttributes.Private;
            method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "entity"));

            // ObjectStateEntry stateEntry = null;
            method.Statements.Add(new CodeVariableDeclarationStatement("ObjectStateEntry", "stateEntry", new CodePrimitiveExpression(null)));

            CodeArgumentReferenceExpression entityArgRef = new CodeArgumentReferenceExpression("entity");
            CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");

            CodeFieldReferenceExpression detachedStateRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodePropertyReferenceExpression objectStateMgrRef = new CodePropertyReferenceExpression(contextRef, "ObjectStateManager");

            CodeVariableReferenceExpression entityStateRef = new CodeVariableReferenceExpression("stateEntry");

            // The "_out_" prefix will be replaced below with the language-appropriate modifier to make an out param.
            // CodeDom does not support this, so we must do some string manipulation
            CodeVariableReferenceExpression outEntityStateRef = new CodeVariableReferenceExpression("_out_stateEntry");

            // this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(entity, out stateEntry)
            CodeMethodInvokeExpression getObjectStateEntryCall = new CodeMethodInvokeExpression(objectStateMgrRef, "TryGetObjectStateEntry", entityArgRef, outEntityStateRef);

            // if (...TryGet())
            CodeExpression tryGetTest = CodeGenUtilities.MakeEqual(typeof(bool), getObjectStateEntryCall, new CodePrimitiveExpression(false), codeGenContext.IsCSharp);

            // if (...TryGet..) { return EntityState.Detached; }
            CodeMethodReturnStatement returnDetached = new CodeMethodReturnStatement(detachedStateRef);
            CodeConditionStatement ifTryGet = new CodeConditionStatement(tryGetTest, returnDetached);
            method.Statements.Add(ifTryGet);

            // Return entityState.State;
            method.Statements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(entityStateRef, "State")));

            // CodeDom does not support specifying 'out' parameters at the method call site.
            // So convert the entire method into a snippet of text

            StringBuilder snippet = null;
            CodeDomProvider provider = codeGenContext.Provider;
            using (StringWriter snippetWriter = new StringWriter(System.Globalization.CultureInfo.CurrentCulture))
            {
                provider.GenerateCodeFromMember(method, snippetWriter, codeGenContext.CodeGeneratorOptions);
                snippet = snippetWriter.GetStringBuilder();
            }

            // Our convention above is that "_out_" will be replaced by the language-appropriate "out" parameter modifier.
            // In the case of VB, it is the default
            snippet.Replace("_out_", codeGenContext.IsCSharp ? "out " : string.Empty);

            // We need to indent the entire snippet 2 levels
            string indent = codeGenContext.CodeGeneratorOptions.IndentString;
            indent += indent;

            string snippetText = indent + snippet.ToString().Replace(Environment.NewLine, Environment.NewLine + indent).TrimEnd(' ');
            CodeSnippetTypeMember methodAsText = new CodeSnippetTypeMember(snippetText);

            return methodAsText;
        }
        /// <summary>
        /// Generates the delete domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The context to use</param>
        /// <param name="businessLogicClass">The business logic class into which to generate it</param>
        /// <param name="entity">The entity for which to generate the method</param>
        protected override void GenerateDeleteMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            string parameterName = CodeGenUtilities.MakeLegalParameterName(entity.Name);

            // public void Delete$EntityName$($entityType$ $entityName$)
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);

            //LinqToEntitiesEntity efEntity = (LinqToEntitiesEntity)entity;
            method.Name = "Delete" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

            // parameter declaration
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, parameterName));

            // if ($entity$.EntityState != EntityState.Detached) 
            // {
            //      ObjectContext.ObjectStateManager.ChangeObjectState($entity$, EntityState.Deleted);
            // } 
            // else 
            // { 
            //      ObjectContext.Products.Attach($entity$); 
            //      ObjectContext.Products.DeleteObject($entity$);    
            // }
            //
            // In the case of POCO objects, we use "this.GetEntityState(entity)"
            // rather than referring to the entity's EntityState property

            LinqToEntitiesEntity efEntity = (LinqToEntitiesEntity)entity;
            CodeArgumentReferenceExpression entityExpr = new CodeArgumentReferenceExpression(parameterName);

            // If this is a POCO class, we need to generate a call to a helper method to get
            // the EntityState, otherwise we can directly de-reference it on the entity
            // If this entity does not have an EntityState member, we need to use a helper method instead.
            // This call tells us whether we need this helper and, if so, generates it.
            bool useGetEntityStateHelper = LinqToEntitiesContext.GenerateGetEntityStateIfNecessary(codeGenContext, businessLogicClass, entity);

            CodeExpression getEntityStateExpr;
            if (useGetEntityStateHelper)
            {
                // this.GetEntityState($entity$)...
                getEntityStateExpr = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), LinqToEntitiesContext.GetEntityStateHelperMethodName, entityExpr);
            }
            else
            {
                // $entity$.EntityState...
                getEntityStateExpr = new CodePropertyReferenceExpression(entityExpr, LinqToEntitiesContext.EntityStatePropertyName);
            }

            CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
            CodePropertyReferenceExpression objectSetRef = new CodePropertyReferenceExpression(contextRef, efEntity.DefaultObjectSetName);
            CodeFieldReferenceExpression detachedStateRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodeExpression equalTest = CodeGenUtilities.MakeNotEqual(typeof(EntityState), getEntityStateExpr, detachedStateRef, codeGenContext.IsCSharp);

            CodeFieldReferenceExpression deletedStateRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Deleted));
            CodePropertyReferenceExpression objectStateMgrRef = new CodePropertyReferenceExpression(contextRef, "ObjectStateManager");
            CodeMethodInvokeExpression changeStateExpr = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(objectStateMgrRef, "ChangeObjectState"), entityExpr, deletedStateRef);
            CodeStatement[] trueStatements = new CodeStatement[] { new CodeExpressionStatement(changeStateExpr) };

            CodeMethodInvokeExpression attachCall = new CodeMethodInvokeExpression(objectSetRef, "Attach", entityExpr);
            CodeMethodInvokeExpression deleteCall = new CodeMethodInvokeExpression(objectSetRef, "DeleteObject", entityExpr);
            CodeStatement[] falseStatements = new CodeStatement[] { new CodeExpressionStatement(attachCall), new CodeExpressionStatement(deleteCall) };

            CodeConditionStatement ifStmt = new CodeConditionStatement(equalTest, trueStatements, falseStatements);
            method.Statements.Add(ifStmt);
        }        
        /// <summary>
        /// Gets a <see cref="CodeTypeReference"/> for a CLR type.
        /// </summary>
        /// <param name="type">A CLR type.</param>
        /// <param name="codegenContext">A <see cref="ClientProxyGenerator"/>.</param>
        /// <param name="referencingType">The referencing type.</param>
        /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns>
        public static CodeTypeReference GetTypeReference(Type type, ICodeGenContext codegenContext, CodeTypeDeclaration referencingType)
        {
            if (type.IsPrimitive || type == typeof(void) || type == typeof(decimal) || type == typeof(string) || type == typeof(object))
            {
                return new CodeTypeReference(type);
            }

            if (codegenContext != null && referencingType != null)
            {
                CodeNamespace ns = codegenContext.GetNamespace(referencingType);

                if (ns != null && !ns.Name.Equals(type.Namespace))
                {
                    // If the namespace is already imported, the following line will be a no-op.
                    ns.Imports.Add(new CodeNamespaceImport(type.Namespace));
                }
            }

            if (type.IsArray)
            {
                return new CodeTypeReference(
                    CodeGenUtilities.GetTypeReference(type.GetElementType(), codegenContext, referencingType),
                    type.GetArrayRank());
            }

            if (type.IsGenericType)
            {
                Type[] genericArguments = type.GetGenericArguments();
                CodeTypeReference[] typeArguments = new CodeTypeReference[genericArguments.Length];
                for (int i = 0; i < genericArguments.Length; i++)
                {
                    typeArguments[i] = GetTypeReference(genericArguments[i]);
                }
                return new CodeTypeReference(type.Name, typeArguments);
            }

            return new CodeTypeReference(type.Name);
        }
        /// <summary>
        /// Generates the GetEntityState helper method that allows POCO types to retrieve their
        /// entity state from the contect.  It is not available on the POCO types directly.
        /// </summary>
        /// <param name="codeGenContext">The context in which we are generating code.</param>
        /// <param name="businessLogicClass">The class we are generating.</param>
        /// <returns>The <see cref="CodeTypeMember"/> containing the helper method.</returns>
        private static CodeTypeMember GenerateGetEntityState(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass)
        {
            // Add an import for System.Data.Objects
            CodeNamespace codeNamespace = codeGenContext.GetNamespace(businessLogicClass);

            if (codeNamespace != null)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport("System.Data.Objects"));
            }

            //private EntityState GetEntityState(object entity)
            //{
            //    ObjectStateEntry stateEntry = null;
            //    if (!this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(entity, out stateEntry))
            //    {
            //        return EntityState.Detached;
            //    }
            //    return stateEntry.State;
            //}

            // Declaration
            CodeMemberMethod method = new CodeMemberMethod();

            method.Name       = LinqToEntitiesContext.GetEntityStateHelperMethodName;
            method.ReturnType = new CodeTypeReference(typeof(EntityState).Name);
            method.Attributes = MemberAttributes.Private;
            method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "entity"));

            // ObjectStateEntry stateEntry = null;
            method.Statements.Add(new CodeVariableDeclarationStatement("ObjectStateEntry", "stateEntry", new CodePrimitiveExpression(null)));

            CodeArgumentReferenceExpression entityArgRef = new CodeArgumentReferenceExpression("entity");
            CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");

            CodeFieldReferenceExpression    detachedStateRef  = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodePropertyReferenceExpression objectStateMgrRef = new CodePropertyReferenceExpression(contextRef, "ObjectStateManager");

            CodeVariableReferenceExpression entityStateRef = new CodeVariableReferenceExpression("stateEntry");

            // The "_out_" prefix will be replaced below with the language-appropriate modifier to make an out param.
            // CodeDom does not support this, so we must do some string manipulation
            CodeVariableReferenceExpression outEntityStateRef = new CodeVariableReferenceExpression("_out_stateEntry");

            // this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(entity, out stateEntry)
            CodeMethodInvokeExpression getObjectStateEntryCall = new CodeMethodInvokeExpression(objectStateMgrRef, "TryGetObjectStateEntry", entityArgRef, outEntityStateRef);

            // if (...TryGet())
            CodeExpression tryGetTest = CodeGenUtilities.MakeEqual(typeof(bool), getObjectStateEntryCall, new CodePrimitiveExpression(false), codeGenContext.IsCSharp);

            // if (...TryGet..) { return EntityState.Detached; }
            CodeMethodReturnStatement returnDetached = new CodeMethodReturnStatement(detachedStateRef);
            CodeConditionStatement    ifTryGet       = new CodeConditionStatement(tryGetTest, returnDetached);

            method.Statements.Add(ifTryGet);

            // Return entityState.State;
            method.Statements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(entityStateRef, "State")));

            // CodeDom does not support specifying 'out' parameters at the method call site.
            // So convert the entire method into a snippet of text

            StringBuilder   snippet  = null;
            CodeDomProvider provider = codeGenContext.Provider;

            using (StringWriter snippetWriter = new StringWriter(System.Globalization.CultureInfo.CurrentCulture))
            {
                provider.GenerateCodeFromMember(method, snippetWriter, codeGenContext.CodeGeneratorOptions);
                snippet = snippetWriter.GetStringBuilder();
            }

            // Our convention above is that "_out_" will be replaced by the language-appropriate "out" parameter modifier.
            // In the case of VB, it is the default
            snippet.Replace("_out_", codeGenContext.IsCSharp ? "out " : string.Empty);

            // We need to indent the entire snippet 2 levels
            string indent = codeGenContext.CodeGeneratorOptions.IndentString;

            indent += indent;

            string snippetText = indent + snippet.ToString().Replace(Environment.NewLine, Environment.NewLine + indent).TrimEnd(' ');
            CodeSnippetTypeMember methodAsText = new CodeSnippetTypeMember(snippetText);

            return(methodAsText);
        }
        /// <summary>
        /// Generates the update domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The context to use</param>
        /// <param name="businessLogicClass">The business logic class into which to generate it</param>
        /// <param name="entity">The entity for which to generate the method</param>
        protected override void GenerateUpdateMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            string currentParameterName = "current" + entity.ClrType.Name;

            // public void Update$EntityName$($entityType$ current)
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);

            //LinqToEntitiesEntity efEntity = (LinqToEntitiesEntity)entity;
            method.Name = "Update" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

            // parameter declaration
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, currentParameterName));

            LinqToEntitiesEntity efEntity = (LinqToEntitiesEntity)entity;
            if (!efEntity.HasTimestampMember)
            {
                // this.ChangeSet.GetOriginal(current)
                CodeExpression changeSetRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ChangeSet");
                CodeMethodReferenceExpression getOrigMethodRef = new CodeMethodReferenceExpression(changeSetRef, "GetOriginal");
                CodeMethodInvokeExpression changeSetGetOrig = new CodeMethodInvokeExpression(getOrigMethodRef, new CodeArgumentReferenceExpression(currentParameterName));

                // this.DbContext.$ObjectSetName$.AttachAsModified($current$, this.ChangeSet.GetOriginal(current), this.DbContext);
                CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext");
                CodePropertyReferenceExpression objectSetRef = new CodePropertyReferenceExpression(contextRef, efEntity.DefaultObjectSetName);
                CodeMethodInvokeExpression attachCall = new CodeMethodInvokeExpression(objectSetRef, "AttachAsModified", 
                    new CodeArgumentReferenceExpression(currentParameterName), changeSetGetOrig, new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext"));
                CodeExpressionStatement attachStmt = new CodeExpressionStatement(attachCall);
                method.Statements.Add(attachStmt);
            }
            else
            {
                // this.DbContext.$ObjectSetName$.AttachAsModified($current$, this.DbContext);
                CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext");
                CodePropertyReferenceExpression objectSetRef = new CodePropertyReferenceExpression(contextRef, efEntity.DefaultObjectSetName);
                CodeMethodInvokeExpression attachCall = new CodeMethodInvokeExpression(objectSetRef, "AttachAsModified",
                    new CodeArgumentReferenceExpression(currentParameterName), new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DbContext"));
                CodeExpressionStatement attachStmt = new CodeExpressionStatement(attachCall);
                method.Statements.Add(attachStmt);
            }
        }
        /// <summary>
        /// Generates the metadata classes for all entities for the current context.
        /// </summary>
        /// <param name="codeGenContext">The context to use to generate code.</param>
        /// <param name="optionalSuffix">If nonblank, the suffix to append to namespace and class names for testing</param>
        /// <returns><c>true</c> means at least some code was generated.</returns>
        protected bool GenerateMetadataClasses(ICodeGenContext codeGenContext, string optionalSuffix)
        {
            bool generatedCode = false;
            if (this.NeedToGenerateMetadataClasses)
            {
                // Sort by entity name for baseline predictability
                foreach (BusinessLogicEntity entity in this.Entities.OrderBy(e => e.Name))
                {
                    if (entity.IsIncluded)
                    {
                        generatedCode |= this.GenerateMetadataClass(codeGenContext, optionalSuffix, entity.ClrType);

                        generatedCode |= this.GenerateAdditionalMetadataClasses(codeGenContext, optionalSuffix, entity);
                    }
                }
            }
            return generatedCode;
        }
        /// <summary>
        /// Generates the delete domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The context to use</param>
        /// <param name="businessLogicClass">The business logic class into which to generate it</param>
        /// <param name="entity">The entity for which to generate the method</param>
        protected override void GenerateDeleteMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            string parameterName = CodeGenUtilities.MakeLegalParameterName(entity.Name);

            // public void Delete$EntityName$($entityType$ $entityName$)
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);

            LinqToEntitiesEntity efDbEntity = (LinqToEntitiesEntity)entity;
            method.Name = "Delete" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

            // parameter declaration
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, parameterName));

            // Below we're generating the following method body
            
            // DbEntityEntry<$entityType$> entityEntry = this.DbContext.Entry($entity$);
            // if (entityEntry.State != EntityState.Detached)
            // {
            //     entityEntry.State = EntityState.Deleted;
            // }
            // else
            // {
            //     this.DbContext.$TablePropertyName$.Attach($entity$);
            //     this.DbContext.$TablePropertyName$.Remove($entity$);
            // }

            CodeArgumentReferenceExpression entityArgRef = new CodeArgumentReferenceExpression(parameterName);
            CodeExpression contextRef = LinqToEntitiesDbContext.GetContextReferenceExpression();

            CodeVariableDeclarationStatement entityEntryDeclaration = new CodeVariableDeclarationStatement(
                new CodeTypeReference("DbEntityEntry", new CodeTypeReference(entity.ClrType.Name)),
                "entityEntry",
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(contextRef, "Entry"), entityArgRef));
            method.Statements.Add(entityEntryDeclaration);

            CodeVariableReferenceExpression entityEntryRef = new CodeVariableReferenceExpression("entityEntry");
            CodePropertyReferenceExpression entityStateRef = new CodePropertyReferenceExpression(entityEntryRef, "State");
            CodeFieldReferenceExpression detachedStateRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Deleted));
            CodeExpression detachedStateTestExpr = CodeGenUtilities.MakeNotEqual(typeof(EntityState), entityStateRef, detachedStateRef, codeGenContext.IsCSharp);

            CodeFieldReferenceExpression deletedStateRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Deleted));
            CodeAssignStatement deletedStateExpr = new CodeAssignStatement(entityStateRef, deletedStateRef);
            CodeMethodInvokeExpression attachedEntityMethodCall = new CodeMethodInvokeExpression(LinqToEntitiesDbContext.GetDbSetReferenceExpression(efDbEntity), "Attach", entityArgRef);
            CodeMethodInvokeExpression removedEntityMethodCall = new CodeMethodInvokeExpression(LinqToEntitiesDbContext.GetDbSetReferenceExpression(efDbEntity), "Remove", entityArgRef);

            CodeConditionStatement changeStateOrAddStmt =
                new CodeConditionStatement(detachedStateTestExpr,
                    new CodeStatement[] { deletedStateExpr },
                    new CodeStatement[] { new CodeExpressionStatement(attachedEntityMethodCall), new CodeExpressionStatement(removedEntityMethodCall) });

            method.Statements.Add(changeStateOrAddStmt);
        }        
        /// <summary>
        /// Generates the metadata class for the given object (entity or complex object)
        /// </summary>
        /// <param name="codeGenContext">The context to use to generate code.</param>
        /// <param name="optionalSuffix">If not null, optional suffix to class name and namespace</param>
        /// <param name="type">The type of the object for which to generate the metadata class.</param>
        /// <returns><c>true</c> means at least some code was generated.</returns>
        public bool GenerateMetadataClass(ICodeGenContext codeGenContext, string optionalSuffix, Type type)
        {
            // If already have a buddy class, bypass all this logic
            // Use soft dependency (string name) to avoid static dependency on DataAnnotations.
            // We do this because this wizard must run from the GAC, and DataAnnotations will not necessarily be in the GAC too.
            Type buddyClassType = TypeUtilities.GetAssociatedMetadataType(type);
            if (buddyClassType != null)
            {
                return false;
            }

            string className = type.Name;
            string classNamespace = type.Namespace;

            bool addSuffix = !string.IsNullOrEmpty(optionalSuffix);
            if (addSuffix)
            {
                className += optionalSuffix;
                classNamespace += optionalSuffix;
            }

            // Every object could have a unique namespace (odd, but true)
            // So we logically create a new namespace for each object.  Those
            // sharing a namespace will reuse the CodeNamespace.
            // We allow the caller to specify in case it needs to override that
            // namespace.  Unit testing is such a scenario
            CodeNamespace codeNamespace = codeGenContext.GetOrGenNamespace(classNamespace);

            // If we redirected to a different namespace than the object, import the real object's namespace
            if (addSuffix)
            {
                CodeGenUtilities.AddImportIfNeeded(codeNamespace, type.Namespace);
            }

            // Name of buddy class is $objectClassName$Metadata (e.g. Orders --> OrdersMetadata)
            string buddyClassName = className + "Metadata";

            // We use the full outer.inner type naming convention for VB because they cannot resolve it otherwise.
            // C# can currently resolve it due to a bug in the compiler, but it is safer to use the legal syntax here.
            string fullBuddyClassName = className + "." + buddyClassName;

            CodeTypeDeclaration objectClass = null;

            // public class $objectType$ { }
            objectClass = CodeGenUtilities.CreateTypeDeclaration(className, classNamespace);
            objectClass.IsPartial = true;
            objectClass.TypeAttributes = TypeAttributes.Public;

            // Add explanatory comments about what the [MetadataTypeAttribute] does
            objectClass.Comments.Add(new CodeCommentStatement(String.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Entity_Partial_Class_Remarks, buddyClassName, className), false));

            // [MetadataType(typeof($objectType$.$objectType$_Metadata))]
            CodeAttributeDeclaration attr = CodeGenUtilities.CreateAttributeDeclaration(BusinessLogicClassConstants.MetadataTypeAttributeTypeName);

            CodeAttributeArgument attrArg = new CodeAttributeArgument(new CodeTypeOfExpression(fullBuddyClassName));
            attr.Arguments.Add(attrArg);
            objectClass.CustomAttributes.Add(attr);

            // public sealed class $objectType$_Metadata { }
            // (note: cannot set 'static' modified from CodeDom.)
            CodeTypeDeclaration buddyClass = CodeGenUtilities.CreateTypeDeclaration(buddyClassName, classNamespace);

            // Both VB and C# use a friend/public buddy class.  A private buddy class does not
            // compile in VB, and it compiles in C# only due to a bug.
            buddyClass.TypeAttributes = TypeAttributes.Sealed | TypeAttributes.NestedAssembly;
            bool generatedProperty = false;

            // Generate a developer comment describing what this class does
            buddyClass.Comments.Add(new CodeCommentStatement(String.Format(CultureInfo.CurrentCulture, Resources.Buddy_Class_Remarks, type.Name)));

            // Add a language-specific example
            string explanation = codeGenContext.IsCSharp ? Resources.Buddy_Class_Remarks_CSharp : Resources.Buddy_Class_Remarks_VB;
            buddyClass.Comments.Add(new CodeCommentStatement(explanation, false));

            // Generate a private ctor to make it impossible to instantiate this class
            CodeConstructor ctor = new CodeConstructor();
            ctor.Attributes = MemberAttributes.Private;
            ctor.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_Private_Ctor_Comment));
            buddyClass.Members.Add(ctor);

            // Sort by name order for baseline predictability
            foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).OrderBy(p => p.Name))
            {
                // CodeDom does not support auto-implemented properties, so we will generate fields and then transform them into properties
                Type propType = propertyInfo.PropertyType;
                if (propType.IsVisible && propertyInfo.GetGetMethod() != null && this.CanGeneratePropertyOfType(propType))
                {
                    // Add an import for this property type's namespace if needed
                    CodeGenUtilities.AddImportIfNeeded(codeNamespace, propertyInfo.PropertyType.Namespace);

                    CodeSnippetTypeMember property = CodeGenUtilities.CreateAutomaticPropertyDeclaration(codeGenContext, buddyClass, propertyInfo, !string.IsNullOrEmpty(codeNamespace.Name) /* insideNamespace */);

                    buddyClass.Members.Add(property);
                    generatedProperty = true;
                }
            }

            // Don't generate anything if the buddy class is empty
            if (generatedProperty)
            {
                // Add the partial object class to the namespace
                codeNamespace.Types.Add(objectClass);

                // Add the metadata class as a nested class inside the partial object class
                objectClass.Members.Add(buddyClass);
            }

            // false if no properties were generated, indicating no code should be emitted
            return generatedProperty;
        }
 internal CodeGen(ICodeGenContext context)
 {
     this.context = context;
     this.cg = context as ConstructorGen;
     il = context.GetILGenerator();
 }
Пример #22
0
        public CodeGen(ICodeGenContext context, bool isOwner = true)
		{
	        _isOwner = isOwner;
	        Context = context;
#if !PHONE8

            _cg = context as ConstructorGen;

			if (_cg != null && _cg.IsStatic)
				// #14 - cg is relevant for instance constructors - it wreaks havoc in a static constructor
				_cg = null;
            
#endif
            IL = context.GetILGenerator();
		}
Пример #23
0
 /// <summary>
 /// Generates additional metadata classes for the given entity if necessary.
 /// </summary>
 /// <param name="codeGenContext">The context to use to generate code.</param>
 /// <param name="optionalSuffix">If not null, optional suffix to class name and namespace</param>
 /// <param name="entity">The entity for which to generate the additional metadata.</param>
 /// <returns><c>true</c> means at least some code was generated.</returns>
 /// <remarks>
 /// This default implementation of the virtual method does not generate any additional classes. It needs to be overridden in the derived
 /// classes to generate additional metadata classes if necessary.
 /// </remarks>
 protected virtual bool GenerateAdditionalMetadataClasses(ICodeGenContext codeGenContext, string optionalSuffix, BusinessLogicEntity entity)
 {
     return(false);
 }
Пример #24
0
 /// <summary>
 /// Creates the "select" domain operation entry for the entity
 /// </summary>
 /// <remarks>This base class unconditionally returns <c>null</c> and should not be called by derived classes.
 /// This allows the base class to be used for the blank <see cref="DomainService"/>.</remarks>
 /// <param name="codeGenContext">The context into which to generate code.  It cannot be null.</param>
 /// <param name="businessLogicClass">The class into which to generate the method</param>
 /// <param name="entity">The entity which will be affected by this method</param>
 /// <returns>The newly created method</returns>
 protected virtual CodeMemberMethod GenerateSelectMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
 {
     return(null);
 }
Пример #25
0
        /// <summary>
        /// Generates the metadata class for the given object (entity or complex object)
        /// </summary>
        /// <param name="codeGenContext">The context to use to generate code.</param>
        /// <param name="optionalSuffix">If not null, optional suffix to class name and namespace</param>
        /// <param name="type">The type of the object for which to generate the metadata class.</param>
        /// <returns><c>true</c> means at least some code was generated.</returns>
        public bool GenerateMetadataClass(ICodeGenContext codeGenContext, string optionalSuffix, Type type)
        {
            // If already have a buddy class, bypass all this logic
            // Use soft dependency (string name) to avoid static dependency on DataAnnotations.
            // We do this because this wizard must run from the GAC, and DataAnnotations will not necessarily be in the GAC too.
            Type buddyClassType = TypeUtilities.GetAssociatedMetadataType(type);

            if (buddyClassType != null)
            {
                return(false);
            }

            string className      = type.Name;
            string classNamespace = type.Namespace;

            bool addSuffix = !string.IsNullOrEmpty(optionalSuffix);

            if (addSuffix)
            {
                className      += optionalSuffix;
                classNamespace += optionalSuffix;
            }

            // Every object could have a unique namespace (odd, but true)
            // So we logically create a new namespace for each object.  Those
            // sharing a namespace will reuse the CodeNamespace.
            // We allow the caller to specify in case it needs to override that
            // namespace.  Unit testing is such a scenario
            CodeNamespace codeNamespace = codeGenContext.GetOrGenNamespace(classNamespace);

            // If we redirected to a different namespace than the object, import the real object's namespace
            if (addSuffix)
            {
                CodeGenUtilities.AddImportIfNeeded(codeNamespace, type.Namespace);
            }

            // Name of buddy class is $objectClassName$Metadata (e.g. Orders --> OrdersMetadata)
            string buddyClassName = className + "Metadata";

            // We use the full outer.inner type naming convention for VB because they cannot resolve it otherwise.
            // C# can currently resolve it due to a bug in the compiler, but it is safer to use the legal syntax here.
            string fullBuddyClassName = className + "." + buddyClassName;

            CodeTypeDeclaration objectClass = null;

            // public class $objectType$ { }
            objectClass                = CodeGenUtilities.CreateTypeDeclaration(className, classNamespace);
            objectClass.IsPartial      = true;
            objectClass.TypeAttributes = TypeAttributes.Public;

            // Add explanatory comments about what the [MetadataTypeAttribute] does
            objectClass.Comments.Add(new CodeCommentStatement(String.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Entity_Partial_Class_Remarks, buddyClassName, className), false));

            // [MetadataType(typeof($objectType$.$objectType$_Metadata))]
            CodeAttributeDeclaration attr = CodeGenUtilities.CreateAttributeDeclaration(BusinessLogicClassConstants.MetadataTypeAttributeTypeName);

            CodeAttributeArgument attrArg = new CodeAttributeArgument(new CodeTypeOfExpression(fullBuddyClassName));

            attr.Arguments.Add(attrArg);
            objectClass.CustomAttributes.Add(attr);

            // public sealed class $objectType$_Metadata { }
            // (note: cannot set 'static' modified from CodeDom.)
            CodeTypeDeclaration buddyClass = CodeGenUtilities.CreateTypeDeclaration(buddyClassName, classNamespace);

            // Both VB and C# use a friend/public buddy class.  A private buddy class does not
            // compile in VB, and it compiles in C# only due to a bug.
            buddyClass.TypeAttributes = TypeAttributes.Sealed | TypeAttributes.NestedAssembly;
            bool generatedProperty = false;

            // Generate a developer comment describing what this class does
            buddyClass.Comments.Add(new CodeCommentStatement(String.Format(CultureInfo.CurrentCulture, Resources.Buddy_Class_Remarks, type.Name)));

            // Add a language-specific example
            string explanation = codeGenContext.IsCSharp ? Resources.Buddy_Class_Remarks_CSharp : Resources.Buddy_Class_Remarks_VB;

            buddyClass.Comments.Add(new CodeCommentStatement(explanation, false));

            // Generate a private ctor to make it impossible to instantiate this class
            CodeConstructor ctor = new CodeConstructor();

            ctor.Attributes = MemberAttributes.Private;
            ctor.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_Private_Ctor_Comment));
            buddyClass.Members.Add(ctor);

            // Sort by name order for baseline predictability
            foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).OrderBy(p => p.Name))
            {
                // CodeDom does not support auto-implemented properties, so we will generate fields and then transform them into properties
                Type propType = propertyInfo.PropertyType;
                if (propType.IsVisible && propertyInfo.GetGetMethod() != null && this.CanGeneratePropertyOfType(propType))
                {
                    // Add an import for this property type's namespace if needed
                    CodeGenUtilities.AddImportIfNeeded(codeNamespace, propertyInfo.PropertyType.Namespace);

                    CodeSnippetTypeMember property = CodeGenUtilities.CreateAutomaticPropertyDeclaration(codeGenContext, buddyClass, propertyInfo, !string.IsNullOrEmpty(codeNamespace.Name) /* insideNamespace */);

                    buddyClass.Members.Add(property);
                    generatedProperty = true;
                }
            }

            // Don't generate anything if the buddy class is empty
            if (generatedProperty)
            {
                // Add the partial object class to the namespace
                codeNamespace.Types.Add(objectClass);

                // Add the metadata class as a nested class inside the partial object class
                objectClass.Members.Add(buddyClass);
            }

            // false if no properties were generated, indicating no code should be emitted
            return(generatedProperty);
        }
Пример #26
0
 public CodeGen(ICodeGenContext context)
 {
     this.context = context;
     this.cg      = context as ConstructorGen;
     il           = context.GetILGenerator();
 }
        /// <summary>
        /// Creates all the domain operation entries for the given entity.
        /// </summary>
        /// <param name="codeGenContext">The context into which to generate code.  It cannot be null.</param>
        /// <param name="businessLogicClass">The class into which to generate the method</param>
        /// <param name="entity">The entity which will be affected by this method</param>
        public void GenerateEntityDomainOperationEntries(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            CodeMemberMethod selectMethod = this.GenerateSelectMethod(codeGenContext, businessLogicClass, entity);
            if (selectMethod != null)
            {
                // If OData endpoint is requested, generate [Query(IsDefault=true)].
                if (this.IsODataEndpointEnabled)
                {
                    CodeAttributeDeclaration attributeDeclaration = new CodeAttributeDeclaration(
                                                                        new CodeTypeReference("Query"),
                                                                        new CodeAttributeArgument[] { new CodeAttributeArgument("IsDefault", new CodePrimitiveExpression(true)) });
                    selectMethod.CustomAttributes.Add(attributeDeclaration);
                }
            }

            if (entity.IsEditable)
            {
                this.GenerateInsertMethod(codeGenContext, businessLogicClass, entity);
                this.GenerateUpdateMethod(codeGenContext, businessLogicClass, entity);
                this.GenerateDeleteMethod(codeGenContext, businessLogicClass, entity);
            }
        }
        /// <summary>
        /// Generates the select domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The code gen context.></param>
        /// <param name="businessLogicClass">The business logic class.</param>
        /// <param name="entity">The entity.</param>
        /// <returns>The newly created method</returns>
        protected override CodeMemberMethod GenerateSelectMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            CodeMemberMethod method = null;
            LinqToSqlEntity ltsEntity = entity as LinqToSqlEntity;

            if (ltsEntity != null && ltsEntity.TablePropertyName != null)
            {
                // public IQueryable<$entityType$> GetEntities()
                method = new CodeMemberMethod();
                businessLogicClass.Members.Add(method);

                // Add developer comment explaining they can add additional parameters
                method.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_Query_Method_Remarks, false));
            
                method.Name = "Get" + CodeGenUtilities.MakeLegalEntityName(ltsEntity.TablePropertyName);
                method.ReturnType = new CodeTypeReference("IQueryable", new CodeTypeReference(entity.Name));
                method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

                // return this.DataContext.$TablePropertyName$
                CodeExpression contextExpr = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DataContext");
                CodeExpression expr = new CodePropertyReferenceExpression(contextExpr, ltsEntity.TablePropertyName);
                CodeMethodReturnStatement returnStmt = new CodeMethodReturnStatement(expr);
                method.Statements.Add(returnStmt);
            }
            return method;
        }
 /// <summary>
 /// Creates the "select" domain operation entry for the entity
 /// </summary>
 /// <remarks>This base class unconditionally returns <c>null</c> and should not be called by derived classes.
 /// This allows the base class to be used for the blank <see cref="DomainService"/>.</remarks>
 /// <param name="codeGenContext">The context into which to generate code.  It cannot be null.</param>
 /// <param name="businessLogicClass">The class into which to generate the method</param>
 /// <param name="entity">The entity which will be affected by this method</param>
 /// <returns>The newly created method</returns>
 protected virtual CodeMemberMethod GenerateSelectMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
 {
     return null;
 }
        /// <summary>
        /// Generates the delete domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The code gen context.></param>
        /// <param name="businessLogicClass">The business logic class.</param>
        /// <param name="entity">The entity.</param>
        protected override void GenerateDeleteMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            string parameterName = CodeGenUtilities.MakeLegalParameterName(entity.Name);

            // public void Delete$EntityName$($entityType$ $entityName$)
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);

            LinqToSqlEntity ltsEntity = (LinqToSqlEntity)entity;
            method.Name = "Delete" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

            // parameter declaration
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, parameterName));

            // this.DataContext.$TablePropertyName$.Attach(current)
            CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "DataContext");
            CodeExpression tableRef = new CodePropertyReferenceExpression(contextRef, ltsEntity.TablePropertyName);
            CodeMethodInvokeExpression attachCall = new CodeMethodInvokeExpression(tableRef, "Attach", new CodeArgumentReferenceExpression(parameterName));
            method.Statements.Add(attachCall);

            // this.DataContext.$TablePropertyName$.DeleteOnSubmit(current)
            CodeMethodInvokeExpression deleteCall = new CodeMethodInvokeExpression(tableRef, "DeleteOnSubmit", new CodeArgumentReferenceExpression(parameterName));
            method.Statements.Add(deleteCall);
        }
 /// <summary>
 /// Creates the "delete" method for the entity
 /// </summary>
 /// <param name="codeGenContext">The context into which to generate code.  It cannot be null.</param>
 /// <param name="businessLogicClass">The class into which to generate the method</param>
 /// <param name="entity">The entity which will be affected by this method</param>
 protected virtual void GenerateDeleteMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
 {
 }
        /// <summary>
        /// Creates a property declaration for buddy class.
        /// </summary>
        /// <param name="codeGenContext">Gode generation context.</param>
        /// <param name="buddyClass"><see cref="CodeTypeDeclaration"/> for a buddy class.</param>
        /// <param name="propertyInfo"><see cref="PropertyInfo"/> for the original property.</param>
        /// <param name="insideNamespace">Whether or not our buddy class is inside a namespace.</param>
        /// <returns>A code snippet for a buddy property.</returns>
        public static CodeSnippetTypeMember CreateAutomaticPropertyDeclaration(ICodeGenContext codeGenContext, CodeTypeDeclaration buddyClass, PropertyInfo propertyInfo, bool insideNamespace)
        {
            // Create a field declaration: public static $propertyType$ $propertyName;
            CodeTypeReference propTypeRef = CodeGenUtilities.GetTypeReference(propertyInfo.PropertyType, codeGenContext, buddyClass);
            CodeMemberField field = new CodeMemberField(propTypeRef, propertyInfo.Name);
            field.Attributes = MemberAttributes.Public;

            CodeSnippetTypeMember property = null;
            CodeGeneratorOptions codeGeneratorOptions = codeGenContext.CodeGeneratorOptions;
            using (StringWriter stringWriter = new StringWriter(System.Globalization.CultureInfo.InvariantCulture))
            {
                // create a StringBuilder with correct identation (we expect identation to stay constant)
                string indentString = codeGeneratorOptions.IndentString;
                StringBuilder stringBuilder = new StringBuilder(indentString);
                stringBuilder.Append(indentString);
                
                // If we're inside an namespace increase indent (in VB there's usually no explicit namespace)
                if (insideNamespace)
                {
                    stringBuilder.Append(indentString);
                }

                // generate the code for a field
                codeGenContext.Provider.GenerateCodeFromMember(field, stringWriter, codeGenContext.CodeGeneratorOptions);
                stringBuilder.Append(stringWriter.GetStringBuilder());
                stringBuilder.Replace(Environment.NewLine, String.Empty);

                // do a manual replace to transform a field into a property
                if (codeGenContext.IsCSharp)
                {
                    stringBuilder.Replace(";", " { get; set; }");
                }
                else
                {
                    // typical VB code gen looks like: Public MyName As Integer
                    // insert 'Property' between 'Public' and 'MyName'
                    int propertyTokenPosition = stringBuilder.ToString().IndexOf("Public", StringComparison.Ordinal) + "Public".Length;
                    stringBuilder.Insert(propertyTokenPosition, " Property");
                }
                // create a code snippet out of resulting code
                property = new CodeSnippetTypeMember(stringBuilder.ToString());
            }
            return property;
        }
        /// <summary>
        /// Generates the delete domain operation entry
        /// </summary>
        /// <param name="codeGenContext">The context to use</param>
        /// <param name="businessLogicClass">The business logic class into which to generate it</param>
        /// <param name="entity">The entity for which to generate the method</param>
        protected override void GenerateDeleteMethod(ICodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, IBusinessLogicEntity entity)
        {
            string parameterName = CodeGenUtilities.MakeLegalParameterName(entity.Name);

            // public void Delete$EntityName$($entityType$ $entityName$)
            CodeMemberMethod method = new CodeMemberMethod();

            businessLogicClass.Members.Add(method);

            //LinqToEntitiesEntity efEntity = (LinqToEntitiesEntity)entity;
            method.Name       = "Delete" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;   // final needed to prevent virtual

            // parameter declaration
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, parameterName));

            // if ($entity$.EntityState != EntityState.Detached)
            // {
            //      ObjectContext.ObjectStateManager.ChangeObjectState($entity$, EntityState.Deleted);
            // }
            // else
            // {
            //      ObjectContext.Products.Attach($entity$);
            //      ObjectContext.Products.DeleteObject($entity$);
            // }
            //
            // In the case of POCO objects, we use "this.GetEntityState(entity)"
            // rather than referring to the entity's EntityState property

            LinqToEntitiesEntity            efEntity   = (LinqToEntitiesEntity)entity;
            CodeArgumentReferenceExpression entityExpr = new CodeArgumentReferenceExpression(parameterName);

            // If this is a POCO class, we need to generate a call to a helper method to get
            // the EntityState, otherwise we can directly de-reference it on the entity
            // If this entity does not have an EntityState member, we need to use a helper method instead.
            // This call tells us whether we need this helper and, if so, generates it.
            bool useGetEntityStateHelper = LinqToEntitiesContext.GenerateGetEntityStateIfNecessary(codeGenContext, businessLogicClass, entity);

            CodeExpression getEntityStateExpr;

            if (useGetEntityStateHelper)
            {
                // this.GetEntityState($entity$)...
                getEntityStateExpr = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), LinqToEntitiesContext.GetEntityStateHelperMethodName, entityExpr);
            }
            else
            {
                // $entity$.EntityState...
                getEntityStateExpr = new CodePropertyReferenceExpression(entityExpr, LinqToEntitiesContext.EntityStatePropertyName);
            }

            CodeExpression contextRef = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
            CodePropertyReferenceExpression objectSetRef     = new CodePropertyReferenceExpression(contextRef, efEntity.DefaultObjectSetName);
            CodeFieldReferenceExpression    detachedStateRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodeExpression equalTest = CodeGenUtilities.MakeNotEqual(typeof(EntityState), getEntityStateExpr, detachedStateRef, codeGenContext.IsCSharp);

            CodeFieldReferenceExpression    deletedStateRef   = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Deleted));
            CodePropertyReferenceExpression objectStateMgrRef = new CodePropertyReferenceExpression(contextRef, "ObjectStateManager");
            CodeMethodInvokeExpression      changeStateExpr   = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(objectStateMgrRef, "ChangeObjectState"), entityExpr, deletedStateRef);

            CodeStatement[] trueStatements = new CodeStatement[] { new CodeExpressionStatement(changeStateExpr) };

            CodeMethodInvokeExpression attachCall = new CodeMethodInvokeExpression(objectSetRef, "Attach", entityExpr);
            CodeMethodInvokeExpression deleteCall = new CodeMethodInvokeExpression(objectSetRef, "DeleteObject", entityExpr);

            CodeStatement[] falseStatements = new CodeStatement[] { new CodeExpressionStatement(attachCall), new CodeExpressionStatement(deleteCall) };

            CodeConditionStatement ifStmt = new CodeConditionStatement(equalTest, trueStatements, falseStatements);

            method.Statements.Add(ifStmt);
        }