Asp.net release build vs debug build - asp.net

How do I determine if my app was compiled as "release" instead of "debug"? I went to VS 2008 Project Properties > Build and set the configuration from Debug to Release but I noticed no change? This is an ASP.NET project.

HttpContext.IsDebuggingEnabled

If you want to know if the dll was built in Debug mode, with the debug attributes, then your best bet is reflection.
Taken from "How to tell if an existing assembly is debug or release":
Assembly assembly = Assembly.GetAssembly(GetType());
bool debug = false;
foreach (var attribute in assembly.GetCustomAttributes(false)){
if (attribute.GetType() == typeof(System.Diagnostics.DebuggableAttribute)){
if (((System.Diagnostics.DebuggableAttribute)attribute)
.IsJITTrackingEnabled){
debug = true;
break;
}
}
}
This will get the assembly that is calling that code (in effect itself), and then set the debug boolean to true if the assembly was compiled in debug mode, otherwise it's false.
This could easily be dropped into a console app (as in the linked example), and then you pass in the path of the dll/exe you want to check. You would load the assembly from a path like this:
Assembly assembly =
Assembly.LoadFile(System.IO.Path.GetFullPath(m_DllPath.Text));

For one in Web.config debug will be set to true, however you can actually set this in a release application too.
In debug however defines like DEBUG are set, so it's simple to do:
bool is_debug;
#ifdef DEBUG
is_debug = true;
#else
is_debug = false;
#endif

You need to look for more than IsJITTrackingEnabled - which is completely independent of whether or not the code is compiled for optimization and JIT Optimization.
Also, the DebuggableAttribute is present if you compile in Release mode and choose DebugOutput to anything other than "none".
Please refer to my posts:
How to Tell if an Assembly is Debug or Release and
How to identify if the DLL is Debug or Release build (in .NET)

Related

Why does a force downgrade causes an assembly load exception in .Net Core?

I have a sample solution with a console and library project. Both reference the same nuget but a different version. The console project also has a reference to the library project. So the structure is like this:
- Solution
- ConsoleApp
- Project Reference: Library
- Nuget: NServiceBus.RabbitMQ (5.2.0)
- Library
- Nuget: NServiceBus.RabbitMQ (6.0.0)
You can find the solution here.
Since Nuget uses the nearest wins rule, the nuget package that gets resolved is version 5.2.0. This is what I want, so far so good. But when I run the application and run a method of the Library I get the following exception:
Could not load file or assembly 'NServiceBus.Transport.RabbitMQ, Version=6.0.0.0, Culture=neutral, PublicKeyToken=9fc386479f8a226c'. The located assembly's manifest definition does not match the assembly reference. (0x80131040)
In .NET Framework I would solve this with an assembly redirect. But that isn't available in .Net Core. I always thought that .Net Core solves this automatically by using the deps.json file. There I see the following statement:
"Library/1.0.0": {
"dependencies": {
"NServiceBus.RabbitMQ": "5.2.0"
},
"runtime": {
"Library.dll": {}
}
}
But still at runtime he tries to resolve the 6.0.0 version. I'm using the latest .Dot Net 3.1.X SDK.
I'm I doing something wrong or does this seem like a bug?
For the record, this is a simple sample project. The actual situation where I need this is much more complex. I also do understand that doing this can cause runtime exceptions while running the application.
It appears to be by design.
A little bit of searching, I found this: https://github.com/dotnet/fsharp/issues/3408#issuecomment-319466999
The coreclr will load an assembly of the version or higher than the reference. If the assembly discovered is lower than the reference then it fails.
Also this: https://github.com/dotnet/sdk/issues/384#issuecomment-260457776
downgrading the assembly version isn't supported on .NET Core
So, to confirm, I spent much more time than I intended looking/searching through https://github.com/dotnet/runtime. Eventually I found the assembly version compatibility method: https://github.com/dotnet/runtime/blob/172059af6623d04fa0468ec286ab2c240409abe3/src/coreclr/binder/assemblybindercommon.cpp#L49-L53
It checks all the components of the version separately, but if we look at just one, we can see what it's doing:
if (!pFoundVersion->HasMajor() || pRequestedVersion->GetMajor() > pFoundVersion->GetMajor())
{
// - A specific requested version component does not match an unspecified value for the same component in
// the found version, regardless of lesser-order version components
// - Or, the requested version is greater than the found version
return false;
}
As the comment says, the loader will reject the assembly if the assembly's version is lower than the requested version. In your case, assuming that the assembly version matches the package version (which it doesn't have to), your library is requesting version 6.0.0, but the assembly loader/binder, found version 5.2.0 on disk, which is lower. Hence, it rejects that dll, keeps looking, but then can't find a suitable version of the assembly on the probing path and eventually throws the FileLoadException.
What's not clear to me is if this assembly compatibility is checked only on the default assembly loader, or even if you add your own event handler to AssemblyLoadContext.Default.Resolving. You could try adding your own handler and when it requests the assembly of the higher version, you return the lower version assembly anyway. It might be a way to work around the issue.

Error: Can't load metadata reference from the entry assembly. Make sure PreserveCompilationContext is set to true in *.csproj file

This problem is specific to RazorLight.
Error:
Can't load metadata reference from the entry assembly. Make sure
PreserveCompilationContext is set to true in *.csproj file
This error pops up only after we deploy to AWS. On the local machine things work fine. I've already added PreserveCompilationContext to the *.csproj file.
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
We use circleci for deployments. The API that's having this problem is hosted in AWS Lambda.
private async Task<string> GenerateText(string template, ProseModel model)
{
var engine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.Build();
try
{
// ERROR thrown on next line
var result = await engine.CompileRenderAsync(Guid.NewGuid().ToString(), template, model);
return result;
}
catch(Exception e)
{
Logger.LogError("Error generating template", e);
throw e;
}
}
I found that people are getting this same error in Azure Functions. Is that similar to Lambda's and maybe requires a similar fix? If yes, how can I fix this in a Lambda?
I've also tried to set SetOperatingAssembly(Assembly. GetExecutingAssembly())
I ran into the same issue but the fix that you posted for the Azure Function hack worked for me. You must make sure to replace the "RazorLight" package with the "RazorLight.Unofficial" package version beta1.3. Then it should work.
The problem is that the entry assembly when running on Lambda is called:
Bootstrap, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Which I'm assuming isn't compiled to preserve the compilation context.

Entry point not found in .NET Core 2.0 DLL

I couldn't find anything that explains this -- for some reason my .NET Core 2.0 ASP.NET application does not run as a DLL via:
dotnet MyProject.Web.dll
And instead I get the exception:
Unhandled Exception: System.MissingMethodException: Entry point not found in assembly 'MyProject.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
namespace MyProject.Web
{
public class Program
{
public static void Main(string[] args)
{
LoadDependencies();
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
private static void LoadDependencies()
{
DependencyLocator.Instance.DefineIfUndefined<IDataProvider, DataProvider>();
}
}
}
It runs fine as a standalone executable (when targeting a "Console Application" in the project's config), but now that I'm trying to deploy to a server that needs it to run via the dotnet command (as a DLL, i.e. "dotnet .\MyProject.Web.dll"), it seems to be having issues. I get the above exception on both my server and my local development box.
I'm kind of blown away that it cannot locate the Main method -- it's declared as static and in Program.cs. Am I missing something?
(EDIT: To clarify, the DLL I'm trying to run against the "dotnet" command is from the target compiling as a "Console Library," since my server is explicitly asking for a DLL, since they will not run executables).
OK, so this is annoying and will hopefully help someone else out.
My host wants to specifically run DLL's thru .NET Core ONLY. They do not allow for executables to be run.
Because DLL's are frequently built as "Class Library" output types on the project, I assumed that this was the workflow necessary to build it. However, I found out that whenever you build your project as a "Console Application," it builds a DLL in addition to an EXE. So, in the above example, MyProject.Web.exe and MyProject.Web.dll are both built when the output type is "Console Application."
MyProject.Web.dll that comes from "Console Application" is different than MyProject.Web.Dll that comes from "Class Library." The one that comes from "Class Library" will NOT have an entry point that can be discovered on it, which will lead to the problem above.
So, if you're getting this error, look for the DLL that ships with your EXE of the same name -- that's the actual DLL you'll want to run in your dotnet console (i.e. dotnet MyProject.Web.dll)

InvalidOperationException: Could not find 'UserSecretsIdAttribute' on assembly

After deploying ASP.NET Core app to azure and opening the site, I get the following error:
InvalidOperationException: Could not find 'UserSecretsIdAttribute' on
assembly '******, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'.
The exception details also include that the error happens at Startup.cs on this line of code:
builder.AddUserSecrets();
Thank you
There was an update to the user secrets module just recently. Version 1.0.1 and up now requires you specify an assembly-level attribute for the id of the user secrets, or as a fallback, the way it was previously in project.json.
Here is the announcement on GitHub: https://github.com/aspnet/Announcements/issues/209
You can define the secrets id in the .csproj like this:
<PropertyGroup>
<UserSecretsId>aspnet-TestApp-ce345b64-19cf-4972-b34f-d16f2e7976ed</UserSecretsId>
</PropertyGroup>
This generates the following assembly-level attribute. Alternatively, instead of adding it in the .csproj file, you can of course add it yourself e.g. to Startup.cs:
[assembly: UserSecretsId("aspnet-TestApp-ce345b64-19cf-4972-b34f-d16f2e7976ed")]
Also, you should use:
builder.AddUserSecrets<Startup>();
It will search for that attribute in the assembly of the given type, in this case I used the Startup class.
Note: this will be deprecated in 2.0: (1.0.2 and 1.1.1 have marked it obsolete)
builder.AddUserSecrets();
I checked the source code for the user secrets configuration, and calling AddUserSecrets() without the type does this:
var attribute = entryAssembly.GetCustomAttribute<UserSecretsIdAttribute>();
if (attribute != null)
{
return AddUserSecrets(configuration, attribute.UserSecretsId);
}
// try fallback to project.json for legacy support
try
{
var fileProvider = configuration.GetFileProvider();
return AddSecretsFile(configuration, PathHelper.GetSecretsPath(fileProvider));
}
catch
{ }
// Show the error about missing UserSecretIdAttribute instead an error about missing
// project.json as PJ is going away.
throw MissingAttributeException(entryAssembly);
It's trying to find the UserSecretsId attribute on your assembly, and failing that, checking if it could find it in project.json. Then (as commented) returns an error about the missing attribute as they wouldn't want to complain about project.json anymore as it is being deprecated.
I want to add to this answer, for those in my situation.
I am writing a .NET Core console app, trying to use the secrets manager (not sure it's meant for console apps). The only way I was able to rid myself of the error was using the assembly level attribute on the assembly where I was using the secrets manager.
As I said, I am not sure if the secrets manager is meant for console apps. So maybe there is an issue with .xproj files vs. .csproj files.
My .NET Core 3.1 Worker Service required additional setup (more than a Web project).
In Program.cs in the CreateHostBuilder method I needed this:
.ConfigureAppConfiguration((ctx, builder) =>
{
// enable secrets in development
if (ctx.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Worker>();
}
})
But (unlike my Web project) I explicitly needed to add this nuget package:
install-package Microsoft.Extensions.Configuration.UserSecrets
After that I could access secrets.

Can't deploy precompiled, merged webapp to Azure

I'm trying to deploy an ASP.NET web application to Azure. It's hybrid Web Forms, MVC, and WebAPI, and there are a TON of aspx/ascx files, such that they really need to be precompiled or every deploy will render the site sluggish for awhile.
I am trying to deploy via SCM integration with GitHub via kudu, with precompiled views, all merged to a single assembly.
Note that:
Deploy works fine with precompilation disabled.
Deploy works fine from Visual Studio
Build works fine if I copy the msbuild command from the Azure log, replace the relevant paths, and run it locally on my Windows 8.1 machine.
I've set up the Advanced Precompile settings as:
Don't allow precompiled site to be udpatable
Don't emit debug information
Merge all pages and control outputs to a single assembly = AppViews.dll
Here's the .deployment file for Azure
[config]
project = WebSite/WebSite.csproj
SCM_BUILD_ARGS=/p:Configuration=Release;PublishProfile=azure-prod /v:n
You notice I'm sending the verbosity /v to "normal" for extra diagnostic information.
Here is info I get toward the tail of the deployment log:
AspNetPreCompile:
D:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe -v \ -p D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\Source -c D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\TempBuildDir
GenerateAssemblyInfoFromExistingAssembleInfo:
Creating directory "obj\Release\AssemblyInfo".
D:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe /out:obj\Release\AssemblyInfo\AssemblyInfo.dll /target:library Properties\AssemblyInfo.cs
AspNetMerge:
Running aspnet_merge.exe.
D:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\aspnet_merge.exe D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\TempBuildDir -w AppViews.dll -copyattrs obj\Release\AssemblyInfo\AssemblyInfo.dll -a
aspnet_merge : error 1003: The directory 'D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\TempBuildDir' does not exist. [D:\home\site\repository\WebSite\WebSite.csproj]
Done Building Project "D:\home\site\repository\WebSite\WebSite.csproj" (Build;pipelinePreDeployCopyAllFilesToOneFolder target(s)) -- FAILED.
Build FAILED.
It looks like aspnet_compiler.exe runs, but doesn't do what it's supposed to, which is why the TempBuildDir directory (supposed to be the output of the compiler) does not exist in time for the AspNetMerge target. Contrast that with my system, where that directory DOES in fact exist, containing the marker aspx/ascx/etc. files, static content, a PrecompiledApp.config file, and a whole mess of stuff in the bin directory.
aspnet_compiler.exe has an -errorstack flag but it's not clear to me how I could get MSBuild to add this just via the .deployment file, or even if that app is really even throwing an error.
I could just deploy via Visual Studio, but I would really like to take advantage of the SCM integration so I can just push to my prod branch and let it go. Any suggestions?
I replied on https://github.com/projectkudu/kudu/issues/1341, but copying my answer here in case someone lands here...
Way back, we had found that aspnet_compiler.exe was not working within Azure Websites due to how it dealt with the profile folder. We made a change at the time that's a bit of a hack but got us going: we turned it into a no-op, by pointing HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\aspnet_compiler.exe to our own dummy exe (D:\Program Files (x86)\aspnet_compiler\KuduAspNetCompiler.exe).
But trying it now, it appears to work correctly today, likely thanks to improvements in the Azure Websites hosting environment. So we will try getting rid of this hack and doing a full test pass to make sure it doesn't cause any major regressions. If all goes well, we can get that into production, which should enable those scenarios.
In the short term, you may be able to work around this by having your build script:
copy aspnet_compiler.exe from D:\Windows\Microsoft.NET\Framework\v4.0.30319 into your own site files, but under a different name (e.g. aspnet_compiler2.exe)
convince msbuild to use that one
Note: This GitHub issue on projectkudu will eventually make this solution obsolete, but for the meantime, that issue is filed as Backlog, and this works right now.
Thank you thank you David Ebbo. With this information, I was able to bootstrap my build to work for the short term.
First, I downloaded the aspnet_compiler.exe from the Azure instance using the Diagnostic Console available at https://{WEBSITE_NAME}.scm.azurewebsites.net/DebugConsole and added that to my own repository. This way there's no question about any difference between 32/64-bit, etc. I renamed it to azure_aspnet_compiler.exe in my repository.
Second, the AspNetCompiler task doesn't give you the option to change the tool name. It's hardcoded, but as a virtual property so it's overrideable. So I had to create my own task class, and package it in its own assembly, which I built in Release mode and also included in my repository.
public class AzureAspNetCompiler : Microsoft.Build.Tasks.AspNetCompiler
{
private string _toolName = "aspnet_compiler.exe";
protected override string ToolName
{
get { return _toolName; }
}
public string CustomToolName // Because ToolName cannot have a setter
{
get { return _toolName; }
set { _toolName = value; }
}
}
Next I needed to replace the AspNetPreCompile task in MSBuild, but I couldn't figure out how to do that directly. But that task wasn't doing anything anyway, so why not just run right after it?
I added this to the top of my Website.csproj file to import the DLL containing the AzureAspNetCompiler class. Note that the path is relative to the Website.csproj file I'm editing.
<UsingTask TaskName="AzureBuildTargets.AzureAspNetCompiler"
AssemblyFile="..\DeploymentTools\AzureBuildTargets.dll" />
Then I added this right below it, which is basically stealing the MSBuild target definition of AspNetPreCompile from C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Transform\Microsoft.Web.Publishing.AspNetCompileMerge.targets, with some of the property setting stuff near the top of it left out (because the original task will do that for us anyway.) Just take note of the ToolPath and CustomToolName values at the bottom of the (renamed) AzureAspNetCompiler element.
<PropertyGroup>
<!--Relative to solution root apparently-->
<LocalRepoDeploymentTools>.\DeploymentTools</LocalRepoDeploymentTools>
<AzureAspnetCompilerPath>$([System.IO.Path]::GetFullPath($(LocalRepoDeploymentTools)))</AzureAspnetCompilerPath>
</PropertyGroup>
<Target Name="NoReallyAspNetPreCompile" AfterTargets="AspNetPreCompile">
<AzureAspNetCompiler
PhysicalPath="$(_PreAspnetCompileMergeSingleTargetFolderFullPath)"
TargetPath="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)"
VirtualPath="$(_AspNetCompilerVirtualPath)"
Force="$(_AspNetCompilerForce)"
Debug="$(DebugSymbols)"
Updateable="$(EnableUpdateable)"
KeyFile="$(_AspNetCompileMergeKeyFile)"
KeyContainer="$(_AspNetCompileMergeKeyContainer)"
DelaySign="$(DelaySign)"
AllowPartiallyTrustedCallers="$(AllowPartiallyTrustedCallers)"
FixedNames="$(_AspNetCompilerFixedNames)"
Clean="$(Clean)"
MetabasePath="$(_AspNetCompilerMetabasePath)"
ToolPath="$(AzureAspnetCompilerPath)"
CustomToolName="azure_aspnet_compiler.exe"
/>
<!--
Removing APP_DATA is done here so that the output groups reflect the fact that App_data is
not present
-->
<RemoveDir Condition="'$(DeleteAppDataFolder)' == 'true' And Exists('$(_PostAspnetCompileMergeSingleTargetFolderFullPath)\App_Data')"
Directories="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)\App_Data" />
<CollectFilesinFolder Condition="'$(UseMerge)' != 'true'"
RootPath="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)" >
<Output TaskParameter="Result" ItemName="_AspnetCompileMergePrecompiledOutputNoMetadata" />
</CollectFilesinFolder>
<ItemGroup Condition="'$(UseMerge)' != 'true'">
<FileWrites Include="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)\**"/>
</ItemGroup>
With this in place, everything works as I would expect it to.

Resources