/// <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);
        }
Beispiel #2
0
        public void RunStarted(object automationObject,
                               Dictionary <string, string> replacementsDictionary,
                               WizardRunKind runKind, object[] customParams)
        {
            // This instance may be reused -- reinitialize each time
            this._project = null;

            this._dte2 = automationObject as DTE2;
            if (this._dte2 == null)
            {
                this.TerminateWizard(Resources.WizardError_No_DTE);
            }

            // Get active project.  Throws if null.
            Project project = this.ActiveProject;

            ITypeDiscoveryService typeDiscoveryService = this.GetTypeDiscoveryService(project);

            if (typeDiscoveryService == null)
            {
                this.TerminateWizard(Resources.BusinessLogicClass_Error_No_TypeDiscoveryService);
            }

            // Check if the project has a reference to the assembly that has DbDomainService, supporting DbContext.
            VSProject vsproj         = project.Object as VSProject;
            bool      allowDbContext = false;

            if (vsproj != null)
            {
                allowDbContext = vsproj.References.Cast <Reference>().Any(r => String.Equals(r.Name, BusinessLogicClassConstants.DbDomainServiceAssemblyShortName));
            }

            // Get the list of available ObjectContexts, DataContexts, and optionally DbContexts.
            bool foundDbContext           = false;
            IEnumerable <Type> candidates = this.GetCandidateTypes(typeDiscoveryService, allowDbContext, out foundDbContext);

            // Ensure the user entered a non-null file name
            string fileName = replacementsDictionary["$rootname$"];

            fileName = fileName.Trim();
            if (string.IsNullOrEmpty(fileName))
            {
                this.TerminateWizard(Resources.WizardError_Empty_Filename);
            }

            // Class name is file name minus extension.  Validate not empty.
            string className = Path.GetFileNameWithoutExtension(fileName);

            if (string.IsNullOrEmpty(className))
            {
                this.TerminateWizard(Resources.WizardError_Empty_Filename);
            }

            // We infer language from extension
            string extension = Path.GetExtension(fileName);
            bool   isVb      = extension.EndsWith("vb", StringComparison.OrdinalIgnoreCase);
            string language  = isVb ? "VB" : "C#";

            Property rootNamespaceProperty = project.Properties.Item("RootNamespace");
            string   namespaceName         = rootNamespaceProperty == null ? null : (string)(rootNamespaceProperty.Value);

            if (string.IsNullOrEmpty(namespaceName))
            {
                this.TerminateWizard(Resources.BusinessLogicClass_Error_No_RootNamespace);
            }

            // Extract VB root namespace for code gen.
            // If non-empty, it means we want to avoid generating namespaces that begin with it.
            string rootNamespace = isVb ? namespaceName : string.Empty;

            // Get the name of the assembly produced by the current project
            Property assemblyNameProperty = project.Properties.Item("AssemblyName");
            string   assemblyName         = assemblyNameProperty == null ? null : (string)(assemblyNameProperty.Value);

            if (string.IsNullOrEmpty(assemblyName))
            {
                this.TerminateWizard(Resources.BusinessLogicClass_Error_No_AssemblyName);
            }

            // We extract all the context types from the TypeDiscovery service and will pass
            // those to another AppDomain for analysis
            IEnumerable <Type> candidateTypes = candidates.Where(t => CodeGenUtilities.IsValidGenericTypeParam(t));

            // We need the project path for the ClientBuildManager source folder
            string projectPath = project.FullName;

            if (string.IsNullOrEmpty(projectPath))
            {
                this.TerminateWizard(Resources.BusinessLogicClass_No_Project_Path);
            }
            string projectDirectory = Path.GetDirectoryName(projectPath);

            try
            {
                IVsHelp help = this.GetService(typeof(IVsHelp)) as IVsHelp;
                // Strategy: we instantiate the contexts in another AppDomain so they can load the assembly outside of the current
                // Visual Studio root AppDomain.  The main reason for this is the ability to reopen the DSWizard onto modified
                // client assemblies and see modified types.  If they were loaded into the root AppDomain, we would not be able to
                // reload.  The BusinessLogicViewModel is IDisposable and controls the other AppDomain's lifetime.
                using (BusinessLogicViewModel businessLogicViewModel = new BusinessLogicViewModel(projectDirectory, className, language, rootNamespace, assemblyName, candidateTypes, help))
                {
                    businessLogicViewModel.ShowDbContextWarning = false; // foundDbContext; //Removed by CDB //TODO: remove commented out section

                    // Intercept exceptions to report to VS UI.
                    businessLogicViewModel.ExceptionHandler = delegate(Exception ex)
                    {
                        this.ShowError(ex.Message);
                        throw ex;
                    };

                    // Invoke the wizard UI now
                    this._dialog       = new BusinessLogicClassDialog();
                    this._dialog.Model = businessLogicViewModel;

                    IVsUIShell uiShell         = this.GetService(typeof(IVsUIShell)) as IVsUIShell;
                    IntPtr     dialogOwnerHwnd = default(IntPtr);
                    int        result          = uiShell.GetDialogOwnerHwnd(out dialogOwnerHwnd);
                    if (result == 0 && dialogOwnerHwnd != default(IntPtr))
                    {
                        WindowInteropHelper windowHelper = new WindowInteropHelper(this._dialog);
                        windowHelper.Owner = dialogOwnerHwnd;
                    }
                    this._dialog.ShowInTaskbar = false;

                    this._dialog.ShowDialog();
                    bool success = this._dialog.DialogResult.HasValue && this._dialog.DialogResult.Value;
                    this._dialog.Model = null;

                    // If user cancels dialog, cancel wizard
                    if (!success)
                    {
                        throw new WizardCancelledException();
                    }

                    // Capture some model state to we can dispose the contexts and models
                    ContextViewModel currentContext = businessLogicViewModel.CurrentContextViewModel;
                    this._isClientAccessEnabled  = currentContext != null && currentContext.IsClientAccessEnabled;
                    this._isODataEndpointEnabled = currentContext != null && currentContext.IsODataEndpointEnabled;

                    // Compute a namespace that includes folder names
                    namespaceName = this.ComputeNamespace();

                    // User said OK -- so let's generate the code
                    IGeneratedCode generatedCode = businessLogicViewModel.GenerateBusinessLogicClass(namespaceName);

                    replacementsDictionary.Add("$generatedcode$", generatedCode.SourceCode);
                    this.AddReferences(generatedCode.References);
                    this._businessLogicCode = generatedCode;

                    // If user elected metadata classes, do that into a separate file
                    if (businessLogicViewModel.IsMetadataClassGenerationRequested)
                    {
                        // The null namespace asks to generate into the entity types own namespaces
                        IGeneratedCode generatedMetadataCode = businessLogicViewModel.GenerateMetadataClasses(null /* optionalSuffix */);
                        replacementsDictionary.Add("$generatedmetadatacode$", generatedMetadataCode.SourceCode);
                        this.AddReferences(generatedMetadataCode.References);
                        this._generateMetadataFile = generatedMetadataCode.SourceCode.Length > 0;
                        this._metadataCode         = generatedMetadataCode;
                    }
                    else
                    {
                        this._generateMetadataFile = false;
                    }
                }
            }
            finally
            {
                this._dialog = null;
            }
        }
        /// <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);
        }