I'm trying to use Blazor WebAssembly hosted by ASP.NET Core. After implementing a page, I saw in Chrome DevTools many of unnecessary dlls are transmitted to client.
There is an example of situation. Let's assume we have following structure of projects in the solution:
BlazorApp.Client (contains Blazor pages)
Reference to BlazorApp.Shared
BlazorApp.Server (contains ASP.NET core)
Reference to BlazorApp.Client
Reference to BlazorApp.Shared
BlazorApp.Shared (contains shared classes)
Reference to ClassLibrary
ClassLibrary (contains some more shared classes)
NuGet reference to AWSSDK.Core
MyEnum.cs (enum, which is used in Blazor page; not using AWS SDK)
So basically BlazorApp.Shared project has reference to some other project, which could have many nuget packages. Minimum code to reproduce the issue is available in repo https://github.com/GTmAster/blazor-treeshake
My assumption is Mono Linker does a tree shaking in Release build, so all unused code and libraries will be excluded from resulting web assembly.
But when I run my app, I see it loads AWSSDK.Core.dll from the server:
Code in BlazorApp.Client doesn't used it, as well as the code in BlazorApp.Server and in BlazorApp.Shared. It is only loaded, because it is referenced in ClassLibrary.
Am I getting the wrong idea about Mono Linker tree shaking?
Is the only way to exclude this dll from shipping is to move MyEnum to BlazorApp.Shared and break BlazorApp.Shared -> ClassLibrary reference?
I've been investigating this too. It seems that the Linker doesn't do what you expect it to.
https://github.com/dotnet/aspnetcore/issues/28546#issuecomment-742100867
By default Blazor (and all of .NET Core) does not trim application code, it only trims the framework. There are patterns of code that are problematic with trimming. The runtime libraries have been updated and tested to make sure they play well with trimming, arbitrary libraries or user code might not be.
Upon further digging, it seems like you'd need to use Trimming to get further reductions in size. But I found this article, which at the bottom states:
The following trimming approach is being taken for Blazor Apps in .NET 5:
Assemblies from the shared framework/runtimepack get member-level
trimming
Microsoft.Extensions.* assemblies get type-level trimming
from TrimMode=Link
Assemblies from Microsoft.AspNetCore.* are trimmed
via hand generated XML file
All other assemblies are not trimmed
All in all means that the Linker / Trimming isn't going to remove unused assemblies as that's not what it does. It essentially just trims stuff out of the Framework assemblies that it believes you do not need.
You are right, the mono linker is tree shaking.
The only relevant information I could find is here: https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-2-release-now-available/
You may notice with this preview release that the download size of the app during development is now a bit larger, but build times are faster. This is because we no longer run the .NET IL linker during development to remove unused code. In previous Blazor previews we ran the linker on every build, which slowed down development.
So as it is stated:
Now we only run the linker for release builds, which are typically done as part of publishing the app.
Are you looking into release builds?
Try following the this advise if you want tree shaking in debug builds.
If you prefer to still run the .NET IL linker on each build during development, you can turn it on by adding <BlazorWebAssemblyEnableLinking>true</BlazorWebAssemblyEnableLinking> to your project file.
Related
I have a huge solution with many projects and in-house NuGet packages that has a pervasive dependency on Unity 4.0.1. We are evaluating migrating this solution to Unity 5.11.1 to improve performance and solve random DI-related crashes stemming from code that the Unity project outright deleted on the 5.0.0 release.
In searching for a way to ease the migration from the outside-in two tools have been developed:
A Roslyn-based source code converter
A bridge that implements the Unity 5 interface but in reality maps calls transparently to a wrapped Unity 4 container interface
Both tools pass their unit tests just fine and the converter managed to convert one key "leaf" project, however, we've hit a roadblock when trying to reference migrated leaf project from one inner project: The infamous NU1605.
I absolutely can see how the NU106 error is warranted, as the inner project still references Unity 4.0.1 and the leaf project references Unity 5.11.1. however, this is one case of the tools getting in our way: I require both versions to "co-exist", as I am manually bridging their inconsistencies.
On paper, this should be plenty viable as the DLLs have different versions and even namespaces are different.
Is there any way to "force" nuget into accepting this weird setup?
You have two options to suppress that code. One is to use the <NoWarn>NU1605</NoWarn> msbuild property (must be defined inside a PropertyGroup). Visual Studio's project properties probably has a way to edit it in the UI.
The other option is to add the NoWarn="NU1605" metadata to your ProjectReference item:
<ProjectReference Include="package id" Version="1.2.3" NoWarn="NU1605" />
Finally, NuGet actually reports NU1605 as a warning, which you might notice if you read the docs page title carefully. The .NET Core SDK elevates it to a warning using the WarningsAsErrors property. So, if you're sufficiently proficient with MSBuild, you could either remove it after they add it, or check how to prevent it from being added to the list. My guess as to the motivation is because the BCL is being distributed as packages for .NET Core 1.x and 2.x (it won't for 3.x) and when there's a security update, you don't want NuGet's nearest-wins rule causing a package with a known vulnerability to be accidentally used.
I stumbled into a cooperation project, where the other part references an interface library of mine and deploys a self compiled MEF Plugin for our tool. I know which methods those guys are using and I want to monitor our library during the the build process, if the method signatures have been changed (just to make sure, noone checked in stuff, which should lead to another interface version and impairs the plugins loadability).
Actually, I have a console project in mind, where the signatures are somehow hardcoded and checked via reflection - but maybe there is a more elegant or simple way.
Any hint would be great.
Thanks in advance!
Roslyn 2.3 introduces a feature for generating reference assemblies. That is an assembly containing only public types and members. When used together with the "deterministic" feature (=> reproducible builds), the generated reference assembly remains binary identical as long as no changes to the public interface is made (implementation changes and private/internal members don't matter).
So you can add this to your csproj:
<PropertyGroup>
<Deterministic>true</Deterministic>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>
Until VS 2017 15.5 comes out, I suggest adding <CompileUsingReferenceAssemblies>false</CompileUsingReferenceAssemblies> to all consuming projects because the IDE (e.g. "go to definition") has some problems with this feature unless you are using the "new project system" that is used for .NET Core and .NET Standard projects. (The idea would be that projects referencing the project are only rebuilt if the public interface changes - this speeds up incremental build for large solutions when only implementations change).
These changes will create a ref folder in your output. You can then check if the checksum of the assmbly in there matches a known cheksum on each build.
I ended up creating a small console application with a try catch block, using the same interface dll and the same objects as the project partner does - compiled with the last released interface library. During execution it falls into the catch branch if the signatures got invalid (discovered by the normal .NET processes) - then the exitcode is raised with -1.
Doing all this in the post build processes, cathcing the exit code as discribed this article and breaking build automatically.
Not very happy with that solution, but got it working ... Further ideas still wanted :-)
I have several issues with several SDK's comming from OEM manufacturers for specific devices. SDK is usually based on C or C++ dll, so I have a lot of Marshaling going around (a lot===YOU CAN'T EVEN IMAGINE). Problem start with next version of SDK when they extend some functions or some structures, they effectively break compatibility. In past I have made copy of our library supporting their device and start making changes to support new SDK. But each time our library was only for specific SDK, and upgrades of our systems were tough (Installation script if one heavy thing also ~ 3 GB install).
I have 78 projects in solution, commonly 4-5 libraries for each OEM Manufacturer, this is without any service tools. And Yesterday I said NO MORE. Started research on subject how to recompile C# code in runtime and reload/replace same assembly without quiting App.
And the result is the following:
- Class file that defines external C/C++ dll API was referenced from external Project referencing only System.dll. And me being insane I've already had each SDK version changes wrapped around #if #elif #endif so I could recompile last version of our library to support previous version of SDK. But that was maybe only once done, I've used #defines along with CSharpCodeProvider to recompile this assembly in runtime. Idea was like this:
Application loading ...
Open main SDK file get file version (extract version and identify it).
Load original External Assembly in new AppDomain (so I could destroy domain later).
Extract current version from external assembly.
Destroy new AppDomain to release hook from external assembly.
If versions mismatch, recompile external assembly (source code for external assembly is embedded within parent assembly), and replace original DLL with just compiled one.
Continue loading application...
So far this test approach works on one live demo system, and I was amazed. Switching from one to another SDK was flawless without any hick-ups.
And also code recompiles it self only when SDK version changes. So with safe guard I could say this is my first Metamorphic code I've wrote, that recompiles/changes it self from runtime.
Unfortunately this approach requires me to add one more Project for each OEM Manufacturers SDK. Which effectively kills my first though why I said NO MORE. True I now have only two libraries to maintain per one OEM manufacturer, and there will be no more projects added after this. But...
I wonder is there better approach which could allow me to replace DLL of currently loaded assembly in runtime from true within same assembly? Or change executing code on "fly" each time, this mainly includes Marshaled function, classes, structures, constants, ...?
Please notice code should be maintained from within same project without any externals. Also please notice this project exposes only hard-coded interface to "outside" world (Interface is referenced Interface only project - is more complex than I wrote). But this "outside" world is blind to any OEM specific stuff, which was the point using interface to have exactly same behavior across any OEM Device.
Any ideas? thoughts? suggestions?
I have two projects in my solution Bridge and BridgeInterface being used by my applications update process and it's throwing a FileLoadException when I try to run use the Initialize() method from the Bridge class.
The problem is I can't step into this method, and I have run out of ideas, I need to see where the fault lies in the code!
The Bridge projects where not originally originally part of the solution, so I added both the projects and have pointed to their PDB symbol files. (The .DLL's are showing as symbols loaded in the modules window.)
I also have "Just My Code" unchecked and all projects in the solution are targetting the .NET 4 Full Profile.
Checked to see if the implementer was using DebuggerStepperBoundary or DebuggerStepThroughAttribute no such luck.
Any ideas?
Change the assembly references in your main project to use project references.
This way VS can compile the debug versions of the bridge projects and properly keep track of what's going on so you can step into it.
Situation
I run a build system that executes many builds for many project. To avoid one build impacting another we lock down the build user to only its workspace. Builds run as a non privileged users who only have write ability to the workspace.
Challenge
During our new build we need to use a legacy 3rdparty DLL that exposes its interface through COM. The dev team wants to register the build(regsrv32.exe) but our build security regime blocks this activity. If we relax the regime then the 3rdparty DLL will impact other builds and if I have two build which need two different versions I may have the wrong build compile against the wrong version (a very real possibility).
Question
Are there any other options besides registration to handle legacy DLLs which expose their interface via COM?
Thanks for the help
Peter
For my original answer to a similar question see: TFS Build server and COM references - does this work?
A good way to compile .NET code that references COM components without the COM components being registered on the build server is to use the COMFileReference reference item in your project/build files instead of COMReference. A COMFileReference item looks like this:
<ItemGroup>
<COMFileReference Include="MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>
Since Visual Studio provides no designer support for COMFileReference, you must edit the project/build file by hand.
During a build, MSBuild extracts the type library information from the COM DLL and creates an interop assembly that can be either standalone or embedded in the calling .NET assembly.
Each COMFileReference item can also have a WrapperTool attribute but the default seemed to work for me just fine. The EmbedInteropTypes attribute is not documented as being applicable to COMFileReference, but it seems to work as intended.
See https://learn.microsoft.com/en-ca/visualstudio/msbuild/common-msbuild-project-items#comfilereference for a little more detail. This MSBuild item has been available since .NET 3.5.
It's a shame that no-one seems to know anything about this technique, which to me seems simpler than the alternatives. It's actually not surprising since I could only find just the one above reference to it on-line. I myself discovered this technique by digging into MSBuild's Microsoft.Common.targets file.
There's a walkthrough on registration-free COM here:
http://msdn.microsoft.com/en-us/library/ms973913.aspx
And excruciating detail here:
http://msdn.microsoft.com/en-us/library/aa376414
(the root of that document is actually here: http://msdn.microsoft.com/en-us/library/dd408052 )
Also, for building in general, you should be able to use Tlbimp or tlbexp to create a TLB file that you can use for building, assuming the point of registering is just to be able to compile successfully, and not to run specific tests.
Installation tools such as Installshield can extract the COM interfaces from the DLLs and add them to the registry. It can also use the self-registration process of the DLL (which I believe is what regsvr does), but this is not a Microsoft installer best practice.
in .NET COM is normally done thru Interop in order to register .DLL in .NET they are called Assemblies and that can be done several ways.. by adding references via VS IDE at the project level, or writing code that Loads and unloads the assembly.. by .Config file that haas the reference to the assembly as well as the using of that reference within the project... GAC.
If you have access to the 3rd party .DLL's you can GAC them, and reference them in your project
you can add a using to your .cs file header as well as add the reference to the project by right clicking on reference --> add Reference ...
you can also do the above step as well as set the copy local = true in the properties for that .dll.. I hope that this gives you some ideas.. keep in mind that .NET assemblies are Managed code so there are several ways to Consume those 3rd party .DLL's using other methods within C# like LoadFromAssembly ect..
Thanks for all the help.
We changed from early-binding to late-binding because we never really needed the DLL at compile time. This pushed the registration requirement from the build server to the integration test server (where we execute the installer which handles the registration). We try to keep the build system pristine and have easy-to-reset integration systems.
Thanks again
Peter