.NET Core项目中EnvDTE.ProjectItem.FileCodeModel为何为Null?
Problem Description
I'm programmatically building a .NET Core console application project in VS 2017, but the FileCodeModel instance fcm is always null right after adding an item from a template. Strangely, if I force a MessageBox to pop up after calling AddFromTemplate(), the issue goes away—but I don't want to rely on this temporary workaround. Here's my code:
string itemName = "View.cs"; string itemTemplatePath = solution2.GetProjectItemTemplate("CodeFile", "CSharp"); destProject.ProjectItems.AddFromTemplate(itemTemplatePath, itemName); //if I forcefully show the msgbox, fcm is not null.!! //MessageBox.Show("ok","ok"); ProjectItem destProjectItem = null; foreach(ProjectItem projectItem in destProject.ProjectItems) { if(projectItem.Name == itemName) { destProjectItem = projectItem; break; } } FileCodeModel fcm = destProjectItem.FileCodeModel; if (fcm == null) throw new ArgumentNullException("fcm is null!!"); fcm.AddNamespace("MyNameSpace");
Root Cause
The issue stems from the fact that AddFromTemplate() is an asynchronous operation in Visual Studio's automation model. When you call this method, VS starts creating the file and initializing its metadata in the background—but your code immediately moves to fetch the ProjectItem and its FileCodeModel before VS has finished these tasks.
The MessageBox.Show() works as a hack because it blocks your main thread, giving VS enough time to complete the background file setup and populate the FileCodeModel. Without that forced delay, the object hasn't been initialized yet, so it returns null.
Solution
Instead of using a message box, you can tell Visual Studio to wait until it's done with all pending background operations before proceeding. Use the DTE.WaitForIdle() method, which blocks execution until VS enters an idle state (meaning all asynchronous tasks are completed).
Here's the fixed version of your code:
string itemName = "View.cs"; string itemTemplatePath = solution2.GetProjectItemTemplate("CodeFile", "CSharp"); destProject.ProjectItems.AddFromTemplate(itemTemplatePath, itemName); // Wait for Visual Studio to finish background initialization tasks solution2.DTE.WaitForIdle(); ProjectItem destProjectItem = null; foreach(ProjectItem projectItem in destProject.ProjectItems) { if(projectItem.Name == itemName) { destProjectItem = projectItem; break; } } // Optional extra safeguard to refresh the project item's metadata destProjectItem.Refresh(); FileCodeModel fcm = destProjectItem.FileCodeModel; if (fcm == null) throw new ArgumentNullException("fcm is null!!"); fcm.AddNamespace("MyNameSpace");
A couple of key notes:
- We access the
DTEinstance viasolution2.DTEsince you're already working withsolution2—this is the main automation object for Visual Studio. - Adding
destProjectItem.Refresh()is an optional but useful step to ensure the project item's metadata is fully loaded before accessingFileCodeModel.
内容的提问来源于stack exchange,提问作者Jinwoo Nam

