modelsbuilder/ModelsBuilder.Original

CustomTool hangs in VS2019

Closed this issue · 24 comments

See #199 - in some cases, the custom tool can hang.

Reproduced with a simple solution that @FransdeJong shared - contains two projects, on 1 project (created by UaaS.cmd) the tool hangs, on 1 project (created in VS) the tool works.

Now to figure what is causing this, and whether the problems comes from MB or UaaS...

@hfloyd did you start the project with a beta UaaS tool?

@FransdeJong in our tests... the YourProject.Core project, created by UaaS.cmd, is a <Project Sdk="Microsoft.NET.Sdk"> project type, whereas the YourProject.Test project, which we created manually in Visual Studio, is a <Project ToolsVersion="15.0"> project type.

There seem to be some problems with single file generators (ie IVsSingleFileGenerator, the interface we implement to provide the custom tool) and VS2019 and NET.Sdk projects. Almost looks like they wanted to dig custom tools entirely at one point. Digging into it now.

Ah. So, the APIs that Visual Studio provides to add/remove files to/from a project, just don't work with NET.Sdk projects. Need to modify the raw Xml of the project file, it seems. Working on it..

Is it worth it? Or can we convert the project to ToolsVersion="15.0"?
If a easy conversion is possible than it seems unnecessary since it affects only a few people?

Well, the problem will happen one day anyways, so I'd rather fix it + have the fix in MB not in UaaS.

True.

Hey. Want to try this VSIX: Umbraco.ModelsBuilder.CustomTool-8.1.1.vsix.zip ? By my tests, it works both on traditional (ToolsVersion...) projects and Net.Sdk projects. If it works for you too, I'll upload to the market place.

I tried it and it works in both projects here as well.
It looks like the Net.Sdk project is twice as fast to.

By the way I tested:

  • generate
  • regenerate
  • add property and regenerate
  • remove property and regenerate.

Awesome work, @zpqrtbnk ! I was able to install the updated VSIX with no issues. I moved my Models files back into my original ".Core" project and ran it - no errors, no hanging! :-) #H5YR

Well, I might have written too soon in regards to "no errors".

Though the "Output" window shows:

UmbracoModelsBuilder: Starting v8.1.1 7/17/2019 3:18:47 PM.
UmbracoModelsBuilder: Done.

The "Error List" window includes this one:

Error Custom tool UmbracoModelsBuilder failed to produce an output for input file 'Models\ContentTypes\_ModelBuilder.cs' but did not log a specific error.

referencing my MB "config" file (\Models\ContentTypes_ModelBuilder.cs)

The good news is that it actually DID work - my new Doctypes and properties are reflected in the .generated.cs files. So, I'm not sure why the error is getting displayed.

(This is in Visual Studio 2017)

Ah well. There is no need to produce an output, ie to generate anything, corresponding to the "config" file _ModelBuilder.cs, so I have simplified things by returning nothing. And, everything works, but I had not paid attention to the fact that it would log an error in the list.

Going to revert that change to generate something, again. Stay tuned.

Want to try Umbraco.ModelsBuilder.CustomTool-8.1.2.vsix.zip ? Same thing, but generates the dummy file again, so there should not be errors anymore. Let me know.

The generation still goes well but:

  • I get a notification that the project has been modified outside of VS
  • the modelsbuilder.generated.cs is not nested under the class with the custom tool

The previous version with the error was more user friendly in a way.

right - i hoped it would "just" work - working on it

right - i hoped it would "just" work - working on it

We have the same coding style I see :D

OK, this was interesting. Traditional custom tools implement a "Visual Studio Single File Generator" and then hack the generator to, in fact, generate more than one file. Turns out this leads to all sorts of problems when supporting both old-style "ToolsVersion" projects, and new-style "Net.Sdk" projects.

So... I want to move from using a "File Generator" to a proper "Visual Studio Command". What this means is:

  • You don't set a "custom tool" on a CSharp file anymore
  • Instead, you Add | New Item... then pick Visual C# Items | Umbraco and add an Umbraco Models placeholder, with a .md extension (the name does not matter, but the extension is important) - for instance, Models.md - the content of the file is, for now, of no importance
  • That placeholder will automatically have a Build Models command in its context menu. So, right-click and Build Models ... and models are generated and nicely nested under the placeholder, both in "ToolsVersion" projects and in "NET.Sdk" projects.

And it should be way more robust, and easier to maintain.

Want to test? Umbraco.ModelsBuilder.CustomTool-8.1.3.vsix.zip

In the old project it works great.

In the Net.Sdk project it fails with following error:

UmbracoModelsBuilder: UmbracoModelsBuilder failed to generate code: Exception: Response status code does not indicate success (InternalServerError)
{"Message":"An error has occurred.","ExceptionMessage":"{ expected","ExceptionType":"Umbraco.ModelsBuilder.Building.CompilerException","StackTrace":"   at Umbraco.ModelsBuilder.Building.Compiler.ThrowExceptionFromDiagnostic(String path, String code, Diagnostic diagnostic)\r\n   at Umbraco.ModelsBuilder.Building.Compiler.<>c__DisplayClass14_0.<GetCompilation>b__0(KeyValuePair`2 x)\r\n   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()\r\n   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)\r\n   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)\r\n   at Umbraco.ModelsBuilder.Building.Compiler.GetCompilation(String assemblyName, IDictionary`2 files, SyntaxTree[]& trees)\r\n   at Umbraco.ModelsBuilder.Building.Builder.PrepareAmbiguousSymbols()\r\n   at Umbraco.ModelsBuilder.Building.Builder.IsAmbiguousSymbol(String symbol, String match)\r\n   at Umbraco.ModelsBuilder.Building.TextBuilder.WriteNonGenericClrType(StringBuilder sb, String s)\r\n   at Umbraco.ModelsBuilder.Building.TextBuilder.WriteClrType(StringBuilder sb, String type)\r\n   at Umbraco.ModelsBuilder.Building.TextBuilder.WriteInterfaceProperty(StringBuilder sb, PropertyModel property)\r\n   at Umbraco.ModelsBuilder.Building.TextBuilder.WriteContentType(StringBuilder sb, TypeModel type)\r\n   at Umbraco.ModelsBuilder.Building.TextBuilder.Generate(StringBuilder sb, TypeModel typeModel)\r\n   at Umbraco.ModelsBuilder.Api.ApiHelper.GetModels(UmbracoServices umbracoServices, String modelsNamespace, IDictionary`2 files)\r\n   at Umbraco.ModelsBuilder.Api.ModelsBuilderApiController.GetModels(GetModelsData data)\r\n   at lambda_method(Closure , Object , Object[] )\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_2.<GetExecutor>b__2(Object instance, Object[] methodParameters)\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()"}
UmbracoModelsBuilder:    at Umbraco.ModelsBuilder.Api.ApiClient.EnsureSuccess(HttpResponseMessage result)
   at Umbraco.ModelsBuilder.Api.ApiClient.GetModels(Dictionary`2 ourFiles, String modelsNamespace)
   at Umbraco.ModelsBuilder.CustomTool.Generator.TryGenerate(ProjectItem sourceItem)
   at Umbraco.ModelsBuilder.CustomTool.Generator.Generate(AsyncPackage package, ProjectItem sourceItem)

I had this error before in V7 projects when using IPublishedContent as model somewhere but I cant find something like that in this project. Also the previous versions worked so let me know if you want me to test something specific.

Cause: the models.mb file was in a directory named "New Models" which we turned into a namespace "New Models" which is not a valid C# namespace. Need to use the same (or similar) logic Visual Studio uses to derive namespaces from folder names.

Want to test? Umbraco.ModelsBuilder.CustomTool-8.1.4.vsix.zip

And then... with NET.Sdk projects, there's some race conditions in the Solution Explorer where files appear un-nested and then nested, or even remain un-nested. Trying to fix.

Want to test? Umbraco.ModelsBuilder.CustomTool-8.1.5.vsix.zip

I tested this version and it works great in the standard and the old project. No more unnested models.
Great work Stephan!!

Fixed in the new Visual Studio Extension which replaces the old Visual Studio Custom Tool, read this blog post for details. Closing.

It should be possible to put them in a cs file like before.

And the question to this answer disappeared...

Awesome work, @zpqrtbnk ! Thanks for your ongoing dedication to this tool. :-)

It should be possible to put them in a cs file like before.

And the question to this answer disappeared...

Haha! Yes, sorry about that - I had left this page open from yesterday and hadn't refreshed for Stephan's comment about the blog post - until I posted my question. I realized that all questions were answered in the Blog and thought to hide my mistake - but you, sir are way too quick for me! :-)