CZEMacLeod / MSBuild.SDK.SystemWeb

This MSBuild SDK is designed to allow for the easy creation and use of SDK (shortform) projects targeting ASP.NET 4.x using System.Web.
MIT License
151 stars 8 forks source link

.aspx.designer.cs are not updated on .aspx save #11

Closed lscorcia closed 2 years ago

lscorcia commented 3 years ago

Hi, let me first tell you this project is awesome. I'm testing it on our rather complex solution and it seems to work as smooth as Visual Studio should just do. This is great!

I am however experiencing an issue with aspx pages (no MVC here, plain old Web Forms). To reproduce it, create a new project using the Empty C# template (sdk 4.0.47), then add a web form called Default.aspx , complete with its codebehind Default.aspx.cs and its designer file Default.aspx.designer.cs.

Add a new <asp:Label runat="server" id="myLabel"></asp:Label> control and save the .aspx file. The control member variable should be available in the .designer.cs now, but it's not. It seems like the file is never regenerated.

Do you have any idea why?

fretje commented 3 years ago

That's probably because this is a not yet supported use case :-)

I don't even see the option to "add a web form" on a SystemWeb project. I have tried it in a regular .NET Framework project, and I see that this gets added to the csproj file when adding a web form:

  <ItemGroup>
    <Compile Include="WebForm1.aspx.cs">
      <DependentUpon>WebForm1.aspx</DependentUpon>
      <SubType>ASPXCodeBehind</SubType>
    </Compile>
    <Compile Include="WebForm1.aspx.designer.cs">
      <DependentUpon>WebForm1.aspx</DependentUpon>
    </Compile>
    <Content Include="WebForm1.aspx" />
  </ItemGroup>

When I create an empty SystemWeb project, and add the webform files (like you describe), the Compile Items and DependentUpon logic already seems to be baked into the sdk, but the aspx file isn't a Content Item, and the aspx.cs file doesn't have the SubType (probably as I can't see that in Visual Studio in the UI), so I tried adding this to the project file:

  <ItemGroup>
    <Content Include="WebForm1.aspx" />
    <Compile Update="WebForm1.aspx.cs">
      <SubType>ASPXCodeBehind</SubType>
    </Compile>
  </ItemGroup>

But unfortunately that doesn't seem to have done the trick. The .designer.cs file is still not regenerated.

This is as far as my knowledge goes... but maybe it's a start ;-)

CZEMacLeod commented 3 years ago

@lscorcia Unfortunately this looks like some Visual Studio magic which we don't have access to. It isn't even triggered with a custom tool entry - it just seems to be part of the editor and web application project handling. It is probably the same reason that the View Code Gen and View Designer menu entries are missing. I would normally only recommend using this project for managing applications where the content is not 'under development' and the most likely changes required are to CSS, or just updating text etc. It does work quite well for MVC, especially if combined with RazorGenerator where you can move the MVC classes and views to a class library, but I will admit I converted the last of my old aspx webforms applications to MVC quite a while ago, so this is not something I have tested with. If you can get any input from the VS team, or find a way to make this work, please provide the feedback. In the meantime, you will have to manually add/remove/update the appropriate CodeBehind entries for any elements you change. I would probably recommend copying the contents of the .aspx.designer.cs file into the .aspx.cs file and deleting the designer file, to make sure you remember that it is not generated/updated for you.

lscorcia commented 3 years ago

Thanks for the additional investigation @fretje and @CZEMacLeod ! Maybe it's worth mentioning those limitations in the readme?

Maybe it's just finally time to convert those project to MVC. It would be easier if I could just port them directly to .net core, unfortunately they depend on some Microsoft libraries that aren't still available on netstandard/netcore (SharePoint CSOM, Exchange API), and they're still actively developed so there's lots of moving parts. I just hate this new Microsoft that jumped on the bandwagon of "move fast, break things"... anyway, feel free to close this issue whenever you want.

CZEMacLeod commented 3 years ago

I am looking to try and capture information from the issues into documentation. It looks like this is a limitation that does not have a solution for us just now (barring some help from the VS team on the designer side). This would obviously go into a 'known limitations' area of the documentation. Documentation?

AvremelM commented 3 years ago

@CZEMacLeod Not sure if this is something you could/would integrate, but there is an open source .designer.cs generator tool: https://github.com/seanofw/Redesigner

TheJayMann commented 3 years ago

Seems like an interesting project. I myself thought it would be nice if it were possible to use a source generator to generate the designer files. Maybe even it might be possible to reimplement the Redesigner project as a source generator. Not sure if it would actually be possible, given that, as far as I know, source generators execute in a .NET 5 runtime, and I'm not sure what dependencies on .NET Framework are required.

CZEMacLeod commented 3 years ago

@AvremelM @TheJayMann I would have thought it could maybe run as an MSBuild task a bit like RazorGenerator's RazorGenerator.MSBuild package. The designer files would be generated on demand as part of the build process. Unfortunately, I'm not sure that is entirely possible for these specific files; especially as that project requires that the assembly can build first to regenerate the files. It seems likely that it would be possible to make this project into a visual studio extension though, which could be invoked manually as and when required.

AvremelM commented 3 years ago

It seems likely that there's a way this could be done programmatically via System.Web.Compilation, I just can't figure out if the functionality for generating the designer source is built-in, or if it just builds the type and then VS generates the source from that.

ulrichb commented 2 years ago

For all the ReSharper users: We had exactly the same issue and solved it by adding an explicit Update ASP.NET Designer File command to the ReSharper Roflcoper extension (requires the current R# 2021.2 and can be installed in the ReSharper extension manager).

See: https://github.com/ulrichb/Roflcopter/blob/master/README.md#update-aspnet-designer-file-r-only

OronDF343 commented 1 year ago

RE: @CZEMacLeod @AvremelM

The .designer.cs files can be generated by the old System.Web compiler used in (deprecated) Web Site projects (which is also why they don't have/need these files).

To test this I have used an old Web Site project that does not have .designer.cs files. This is the example code that generates the designer file:

var cbm = new System.Web.Compilation.ClientBuildManager("~", @"<project dir>");
var code = cbm.GenerateCode("~/Menu.aspx", File.ReadAllText(@"<project dir>\Menu.aspx"), out var tbl);

The variable code now contains the code for Menu.aspx.designer.cs.

Unfortunately, this example is not suitable for source generators or most design-time tooling because it will compile the application partially and will read many other files in the background. In fact, it might be identical to the Build Page action in VS (for Web Site projects).

The source code of this compiler is available here as part of .NET Framework. In particular, PageCodeDomTreeGenerator is the class that generates designer code for aspx files. Even so, for use as a source generator this might need to be completely rewritten, including the aspx parser (System.Web.UI.PageParser).

OronDF343 commented 1 year ago

Update: I was wrong about System.Web.Compilation, which turned out to be a good thing. The .designer.cs files generates in VS contain only field definitions, while the files generated by the System.Web compiler contains all of the required code at runtime (such as event handler wiring and control initialization). Even when published, the main DLL is compiled against these incomplete designer files while the "real" code is generated at runtime. Only precompiling the app can skip this runtime step (and fortunately precompiling still works via MSBuild publish so I don't need to reimplement it). This means that the task of generating .designer.cs files is relatively easy.

I am now using AspxParser (with a minor modification) to parse the pages and some logic lifted from the reference source to determine the types of fields to generate. Here are the basics:

What works:

What does not work:

Many things weren't tested yet because my source generator is very incomplete and not optimized.

CZEMacLeod commented 1 year ago

@OronDF343 This looks like interesting stuff. Should we perhaps move this over to a new issue or a discussion; specifically something like "Add a source generator to enable .designer.cs updates"?

Are you thinking of making your code public? And would you like to add it to this repository or maintain it yourself? As this would not be relevant for all users of this SDK, I would suggest that we move it to its own package - perhaps MSBuild.SDK.SystemWeb.WebForms' or similar. Then either add it as a package reference, or we could make it a standalone top level SDK (probably relying onMSBuild.SDK.SystemWeb` underneath it).

I'm not quite sure if VS can handle injecting new 'add item' types from an SDK or other nuget package; I know that project types can be done via the templates package: MSBuild.SDK.SystemWeb.Templates.

It might be that to enable that would require building a VSExtension which would need to be installed. (Not my favourite solution as then you need to ensure that the extension is installed on any machine you are using to work on the project).

OronDF343 commented 1 year ago

@CZEMacLeod Sure, I'll open a discussion once I have more to share. It might take a while for me to have an update on this, and the decision to make the code public partly depends on my employer as this all started as an internal tool to improve maintainability of older applications. In testing my proof of concept against a very large Web Forms project, I have found many more smaller issues that need to be addressed before it is usable for real-world projects.

jeffjmc commented 12 months ago

Should this be closed? This is still unresolved / a known issue

CZEMacLeod commented 12 months ago

@jeffjmc Unfortunately, this is a known limitation, and is not something that can be addressed within the SDK. It would require input from the Visual Studio team to wire up the designer system to work with CPS and this SDK type, which is unlikely to happen for a deprecated technology.

This SDK makes it easy to continue to maintain existing projects, and potentially to work on migrating to ASPNet Core, perhaps using something like the https://github.com/dotnet/systemweb-adapters project. It is not intended for use in 'green field' development as Net 4.x is not the place for that.

If you need to work on webforms code, and require designer support, then I would suggest keeping a branch of your project in the legacy format where you can do such work.

AvremelM commented 12 months ago

Technically Web Forms isn't officially deprecated (though obviously, it de-facto is), so maybe a feature request to just expose the existing functionality for generating designer files wouldn't be completely crazy?

And also, they still haven't closed https://github.com/dotnet/project-system/issues/2670 yet, so theoretically... 😅

(Or maybe JetBrains would open source their implementation, which they developed just a couple years ago for Rider, but that seems less likely somehow.)