Determine if Host Assembly is in debug from class library assembly - c#

I have a class Library, I want to log to System.Diagnostics if host assembly is in debug (My class library is always in release since its downloaded from nuget).
Tried this
var debug =
Assembly.GetEntryAssembly()
.GetCustomAttributes()
.OfType<DebuggableAttribute>()
.Any(a => a.IsJITTrackingEnabled);
Problem is GetEntryAssembly return null, and both Executing and Calling assmebly methods return my class library assemly which is always in releae as stated above
Any ideas how to determin from a class library built in release if the hosting assembly is in debug?
edit: When downvoting, please specify why. Anyway,
Enviroment.UserInteractive returns true when you are using IIS Express but not IIS so its almost what I want. But you can use IIS to host your debug enviroment too and then it will not work. Any ideas how to detect if Host is in debug?
edit: This is what I have now, working, but not 100% bullet proof, wont work if you debug from IIS (Only works with debug from IIS express)
public class BusinessContext : IBusinessContext
{
private readonly DbContext _db;
private static readonly bool Debug;
static BusinessContext()
{
Debug = Environment.UserInteractive; //User is probably in debug from VS with IIS express
//Assembly.GetEntryAssembly()
//.GetCustomAttributes()
//.OfType<DebuggableAttribute>()
//.Any(a => a.IsJITTrackingEnabled);
}
public BusinessContext(DbContext db)
{
_db = db;
if (Debug)
_db.Database.Log = sql => Trace.WriteLine(sql);
}
...
}

Related

Failed XUnit tests (ASP.NET Core 7 MVC)

I tried to make unit tests for my project, but I ran into the problem of missing testhost.deps.json, although it should have been installed with Microsoft.AspNetCore.Mvc.Testing. As I was told the error may be related to shadow copy, but no matter what I tried to do the error is still displayed just during the creation of the client.
How do I fix this error? Thank you in advance for your answer.
System.InvalidOperationException : Can't find 'C:\Users\flybe\OneDrive\Desktop\HomeworkProject\HomeworkTest\bin\Debug\net7.0\testhost.deps.json'.
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.VisualStudio.TestPlatform.TestHost;
namespace HomeworkTest
{
public class UnitTest1 :IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public UnitTest1(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public void Test1()
{
var client = _factory.CreateClient();
}
}
}
I tried to repeat what was in the training video. I also tried doing a test according to the documentation and the JetBrains article, where this problem was solved with ReSharper, but nothing helped.
Make sure testhost.deps.json file exists in the bin folder. if it's missing make sure you have
<PreserveCompilationContext>true</PreserveCompilationContext>
in your .csproj file.
and if that's not work try to remove the below line from your code.
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

C# Is it possible to run nunit tests inside ASP.Net?

I have a very simple question.
Is it possible to run Nunit tests inside an asp.net web app?
Here is an example project:
MyApp -> ASP.Net app (.net5)
MyTests -> Nunit Tests (.net5)
My Asp.net project (MYApp) contains all my controllers and such, with a depency on NUnit.Engine and my test project.
There is another Test project (MyTests), which is just a dummy project.
I want to be able to run in a controller, inside my web app, my tests.
Example controller:
namespace MyApp.Controllers
{
[Route("api/tests")]
[ApiController]
public class TestController: ControllerBase
{
// Some helper class to verify everything is working somehow
private class ReportListener : ITestEventListener
{
public void OnTestEvent(string report)
{
Console.WriteLine(report);
}
}
[HttpGet]
public async Task<ActionResult> Trigger()
{
try
{
using ITestEngine engine = TestEngineActivator.CreateInstance();
engine.Initialize();
engine.WorkDirectory = Path.Combine(Directory.GetCurrentDirectory(), "../","MyTests/");
// Create a simple test package - one assembly, no special settings
TestPackage package = new TestPackage(#".\bin\Debug\net5.0\MyTests.dll"); //Just for debugging and testing
// Get a runner for the test package
using ITestRunner runner = engine.GetRunner(package);
runner.Load();
// Run all the tests in the assembly
XmlNode testResult = runner.Run(listener: new ReportListener(), TestFilter.Empty);
var outputs = Enumerable.Empty<string>();
foreach (XmlNode elem in testResult.SelectNodes("//test-case/output"))
{
outputs = outputs.Append(elem.InnerText);
}
}catch(Exception e)
{
}
return Ok();
}
}
}
But unfortunately all my attemps so far have failed.
Am I missing something?
Is Nunit.Engine not made to be run in an asp.net context?
I am building all this in .NET5.0 (company policy)
If needed I can provide an example project
There could be more, but one small thing would explain the failure...
The NUnit engine defaults to running tests in a separate process, which it launches. So, assuming that your code is working correctly as written, a ProcessRunner will be created and the engine will communicate with it, telling it to run your tests.
This could fail in one of two ways:
You may not have permission to create a process.
If you succeed in creating it, the code will definitely not be running in the asp.net context. In that case, it would probably error out and terminate with very little debug information provided.
A simple fix is to add a setting to the test package, telling the engine to run the tests in process.
TestPackage package = new TestPackage(#".\bin\Debug\net5.0\MyTests.dll");
package.AddSetting("ProcessModel", "InProcess");
If you get a second error after doing this, it should at least result in a clearer message and you should be able to debug through the code.

How do environment variables know what environment I am running on and how do I set them in C#?

I am reading a selenium guidebook for c# and they show this:
class BaseTest
{
private static string VendorDirectory = System.IO.Directory.GetParent(
System.AppContext.BaseDirectory).
Parent.Parent.Parent.FullName
+ #"/vendor";
protected IWebDriver Driver;
public static string BaseUrl;
[SetUp]
protected void SetUp()
{
BaseUrl = System.Environment.GetEnvironmentVariable("BASE_URL") ??
"http://the-internet.herokuapp.com";
But it doesn't show how they are actually setting the environment variables. Is BASE_URL coming from appsettings.json? I'm not sure where they are getting it from. Right now, I have a class with all of the urls I am using throughout my tests like this:
public static class Urls
{
public static readonly string baseUrl = "https://localhost:5001/";
public static readonly string aboutUrl = $"{baseUrl}about";
public static readonly string citiesUrl = $"{baseUrl}cities";
public static readonly string countriesUrl = $"{baseUrl}countries";
}
I don't think this is the best way to do it and want to try to use environment variables instead but I am not sure how to do that. When I change from localhost to a production environment how I have it now will obviously break. How can I set the baseUrl so it knows what environment I am in?
EDITED
My test solution is in a separate repo from my project solution. My test solution is a c# xunit test project. I added an appsettings.json file to my solution. It looks like this
{
"Base_Url": "https://testurl/",
"AllowedHosts": "*"
}
Inside one of my tests that uses the url, I am doing this
public static IConfiguration InitConfiguration()
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
return config;
}
[Fact]
public void LoadFaqs()
{
using IWebDriver driver = new ChromeDriver();
var config = InitConfiguration();
var faqurl = config["Base_Url"] + "faqs";
driver.Navigate().GoToUrl(faqurl);
}
When I run my test, it is failing because it cannot find my appsettings.json file. I also tried putting it inside my test folder and it still couldn't find it. I'm not sure what I am doing wrong.
Locally, they must be setting them up by using either the project configuration (this is for Visual Studio):
Project properties -> Debug tab -> Environment variables
or by running some MSBuild scripts/tasks on build events that ensure the environment variables are added. (quick search on SO). Or by adding them manually on the code when the test host is being built somehow (I'm guessing this is for some functional or integration testing)
In CI/CD pipelines they are set up depending on the platform you're using (Github, Gitlab, Azure DevOps etc)
The downside of using hard-coded classes for configuration is that whenever the configuration changes (like when you change your environment and instead of pointing to a local api you need to point to the production one), you need to change/recompile your code, like you said.
So yeah, I'd say have a look and explore those options. Configuration can use several configuration providers. On asp.net core, by default, they use one called ChainedConfigurationSource, which includes reading from the appsettings.json files, environment variables etc.
Environment variable are set by the Windows system with predefined values
the most common ENV variable is PATH
you can display env variables on cmd with: echo %ENV_NAME%
for example echo %PATH% gives you:
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;
Env variables is a way to pass parameters between a parent process to a child process ( in the cases where args on command lines are a problem)
Then env context is only known by the child process and disappear once the process is killed.
Your selenium launcher might set additional variable with win api like:
public static void SetEnvironmentVariable (string variable, string? value);
SetEnvironmentVariable
In your example I would say: do not care about how selenium set this env variable , just take the content of BASE_URL ( hoping it is not empty....)

ASP.Net Core, detecting debugging vs. not debugging in a controller

I am writing my first ASP.Net code web app and in my controller I would like to have an if statement that checks to see if I am in debugging mode or not. I know in the Startup.cs file I can check env.IsDevelopment() but that is because the IHostingEnvironment is passed into it. I have not been able to find a way to check for this status inside a normal controller. Is there a way in ASP.Net Core to detect when I am in debug mode inside the controller that I am missing?
Update: #Pradeep Kumar's post below is the more correct answer here. This answer only indicates how to access the IsDevelopment() environment flag via dependency injection in a controller.
Update:
IHostingEnvironment is obsolete in .Net Core 3.1 see the following for .Net Core 3.1+
https://stackoverflow.com/a/61703339/2525561
You should be able to just inject IHostingEnvironment into your controller constructor.
protected readonly IHostingEnvironment HostingEnvironment;
public TestController(IConfiguration configuration, IHostingEnvironment hostingEnv){
this.Configuration = configuration;
this.HostingEnvironment = hostingEnv;
}
[HttpGet]
public IActionResult Test(){
if(this.HostingEnvironment.IsDevelopment()){
// Do something
}
return View();
}
IHostingEnvironment lets you know the environment in which the application is running. Looks like what you need is the build configuration used to build the application i.e Debug/Release. In an ASP.NET Core web application, In order to get this information at compile time, there is no straight forward way, however you can have a property with conditional compilation using compiler directives, something like
public static bool IsDebug
{
get
{
bool isDebug = false;
#if DEBUG
isDebug = true;
#endif
return isDebug;
}
}
At runtime, you can check the value of IsDebug property to determine the build configuration. I would suggest to add this property to a common static or utility class which can be accessible from all your controllers.
It's not IHostingEnvironment nowadays, it's IWebHostEnvironment.
In ASP.NET Core 3.1, IHostingEnvironment causes a warning
CS0618 'IHostingEnvironment' is obsolete: 'This type is obsolete
and will be removed in a future version.
The recommended alternative is
Microsoft.AspNetCore.Hosting.IWebHostEnvironment.'
Consequently, the accepted answer should be updated as follows:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
public class TestController : Controller
{
protected readonly IWebHostEnvironment HostEnvironment;
public TestController(IWebHostEnvironment hostEnv) {
this.HostEnvironment = hostEnv;
}
[HttpGet]
public IActionResult Test(){
if (this.HostEnvironment.IsDevelopment()){
// Do something
}
return View();
}
}
An alternative to my own answer, prompted by the comment by user #roblem, and based on an alternative reading of the OP's question, is as follows:
Managed Debuggers
To detect if a managed (.NET) debugger is actually attached to the process, one can use System.Diagnostics.Debugger.IsAttached property. This is useful during development when you need to programmatically determine if your code is running from the IDE or standalone.
using System.Diagnostics;
if (Debugger.IsAttached)
{
// Debugger is attached, we are probably running from the IDE
}
else
{
// We are probably running standalone
}
Note that the above only works for managed debuggers, such as the one built into the IDE.
Unmanaged Debuggers
To detect if an unmanaged debugger is attached, one needs to call CheckRemoteDebuggerPresent WinAPI function:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
public class DetectDebugger
{
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool CheckRemoteDebuggerPresent(IntPtr hProcess, ref bool isDebuggerAttached);
public static void Main()
{
bool isDebuggerAttached = false;
CheckRemoteDebuggerPresent(Process.GetCurrentProcess().Handle, ref isDebuggerAttached);
Console.WriteLine("Debugger is attached: " + isDebuggerAttached);
// Prevent the console window from immediately closing:
Console.ReadLine();
}
}
Summary
In summary (found here):
CheckRemoteDebuggerPresent - works for any running process and detects native debuggers too.
Debugger.IsAttached - works only for the current process and detects only managed debuggers. As an example, OllyDbg won’t be detected by this.

C# IIS 7 Base Url

Recently I asked this :
Get Base URL of My Web Application
This worked to an extent in debug, as I use the VS Development server.
I then produced an Install, the install will then point to IIS 7.
I had :
void Application_Start(object sender, EventArgs e)
{
_baseUrl = HttpContext.Current.Request.Url.ToString();
....
}
But this threw the following error :
Request is not available in this context
I then did some reading up and here is why this happens :
http://mvolo.com/iis7-integrated-mode-request-is-not-available-in-this-context-exception-in-applicationstart
I then moved the code out from Application_Start to Application_BeginRequest, using the technique above as I found Application_BeginRequest was being executed several times.
But the problem is I need the Base URL of IIS 7, for use in Application_Start, and so I have a global string I tried to set in :
FirstRequestInitialization.Initialize(context);
But not surpriseingly when attempting this :
Application["BaseUrl"] = HttpContext.Current.Request.Url.ToString();
I get this error :
'Microsoft.Web.Administration.Application' is a 'type' but is used like a 'variable'
All I want is the Base URL of IIS 7.
I can't use Directory Entries as I can't support IIS 6.
How can I do this? Any workarounds? Can I execute AppCmd from VS?
Any help much appreciated. Thanks!
Short answer: you can't get it, because the websites do not have a single canonical base URI - a website (or rather, a web application) can be configured to answer to requests on any binding, any domain name, and any resource path - and a website can be reconfigured in the host webserver (IIS) without the application being made aware of this at all.
If you really want to store your "base URL" (even though such a thing doesn't really exist) then you can do it from within Application_BeginRequest like so:
private static readonly Object _arbitraryUrlLock = new Object();
private static volatile String _arbitraryUrl;
public void Application_BeginRequest() {
if( _arbitraryUrl == null )
lock( _arbitraryUrlLock )
if( _arbitraryUrl == null )
_arbitraryUrl = HttpContext.Current.Request.Url.ToString();
}

Categories