How to add global values in .NET Core 3? - api

I need to set global values and read it from anywhere in my project.
what is the scenario, if AddSingleton how?
I have seen this, but it's not working for me:
Global Variables in ASP.Net Core 2

The answer from Noah is very good, however for simpler cases you generally want to:
Create a static class that holds all your data, in the root namespace
public static class Cache
{
public static string Value1 => "Example";
public static int Value2 => 42;
}
Done
You can access it's values system-wide, like so:
var mySharedStringValue = Cache.Value1;
If you need to "load" those values in the startup, you can change the signature of the properties to
public static string Value1 { get; private set; }
and use a static constructor or a simple static initialization method that you call in the Startup.cs class when the APP is starting.

If you want to read a static value globally, I'd suggest using the configuration system for that. It allows injecting the configuration wherever needed for reading.
If you need to share data that can change, you need some shared thread-safe mechanism. One option built into the framework is to use the in-memory cache, which is a global shared cache injectable anywhere.

Related

Adding DbContext for list of context types at runtime

To implement a plug-in system in a AspNet Core Mvc app, I would like a non-generic method to add a data context from a list of assemblies loaded dynamically at runtime, taking a Type parameter like this:
foreach(Type tp in pluginContexts)
{
services.AddDbContext(tp, options => ...);
}
instead of the usual
services.AddDbContext<PluginDataContext>(options => ...);
That's because for dynamically loaded assemblies, I can not provide the TContext type parameter to the AddDbContextPool method, since that's statically compiled and not available at compile time.
Background
This is for a larger Asp.Net Core MVC app. The plugins must be able to both access the main database of the overall app and a separate database of their own.
Plugin assemblies, containing domain code and their private database context are to be dropped in a specified directory.
The main app loads the plugin assembly dynamically upon startup.
The way I am solving this now is to have each controller get the IConfiguration instance injected, obtain the appropriate connection string from the config, and the database context is instantiated in the controller. Not so nice but does work.
One can easily inject a general class into the Services collection with AddScoped<>, and then use it as a sort of ServiceLocator - however, that is considered an antipattern.
I looked into the source code for AddDbContext but honestly I am lost.
Is there any simple way to achieve this?
Solved it by creating an extensibility point in the plugin assembly.
Define an interface in the main app, which all plugins must implement.
public interface IPluginContextRegistration
{
void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction);
String GetDatabaseName();
}
Create a class implementing this interface (in the plugin). It has access to the type of its private database context, thus can use the generic AddDbContext method:
public class DatabaseRegistration : IPluginContextRegistration
{
public void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction)
{
services.AddDbContext<Test1DbContext>(optionsAction);
}
public String GetDatabaseName()
{
return "test-plugin-db";
}
}
Then in the main app ASP.Net Startup.cs file, add following code, which calls the RegisterContext() method for each plugin. For example, if you want to use Sql Server:
void RegisterPluginDbContexts(ref IServiceCollection services, List<Assembly> assemblyList)
{
IEnumerable<IPluginContextRegistration> registrars = new List<IPluginContextRegistration>();
foreach (Assembly assembly in assemblyList)
{
registrars = registrars.Concat(GetClassInstances<IPluginContextRegistration>(assembly));
}
foreach (var reg in registrars)
{
String name = reg.GetDatabaseName();
String connStr = Configuration.GetConnectionString(name);
reg.RegisterContext(ref services, options => options.UseSqlServer(connStr));
}
}
For completeness - the method "GetClassInstances" is just a helper method using Reflection to obtain an instance of classes implementing the specified interface.
So it's simple after all - no need for re-writing framework code .

How to make a Dictionary accessible from all controllers in a .Net 5 API?

I have a Dictionary that will be populated with data from the database at startup, with a method that takes the key as a parameter, and returns the value. How to make the dictionary publicly accessible to all controllers? After searching, I learned that I would need to use Dependency Injection, but I'm failing at implementing it. Any resource that can get me on track is highly appreciated.
There are many ways to implement your question with/without DI. One of which is to write a static class that will be filled upon app startup.
No dependency injection:
Declare a static class that contains your dictionary. By being static there would only be 1 instance on app start.
public static class StaticDictionary {
public Dictionary<string,int> MyDictionary {get;set;}
}
In your Startup.cs - Configure method, append your db context in the parameters.
public void Configure(..., YourDbContext dbContext)
In the Configure method again, append your code that fills the dictionary.
public void Configure(..., YourDbContext dbContext){
...
// no need to modify the code above this, just append the fill dictionary code
foreach(var item in dbContext.TableName.ToList()){
StaticDictionary.MyDictionary.Add(...);
}
}
In your controllers, you could access StaticDictionary without DI.
public IActionResult Index{
var something = StaticDictionary.MyDictionary["Something"];
return View();
}

How to use WebApplicationFactory in .net6 (without speakable entry point)

In ASP.NET Core 6 default template moves everything from Sturtup.cs into Program.cs, and uses top-level statements in Program.cs, so there's no more (speakable) Program class ether.
That looks awesome, but now, I need to test all of this. WebApplicationFactory<T> still expects me to pass entry-point-class, but I cannot do this (due to it's name now being unspeakable).
How integration tests are expected to be configured in ASP.NET Core 6?
Note that if you are trying to use xUnit and its IClassFixture<T> pattern, you will run into problems if you just use the InternalsVisibleTo approach. Specifically, you'll get something like this:
"Inconsistent accessibility: base class WebApplicationFactory<Program> is less accessible than class CustomWebApplicationFactory."
Of course you can solve this by making CustomWebApplicationFactory internal but it only moves the problem as now your unit test class will give the same error. When you try to change it there, you will find that xUnit requires that tests have a public constructor (not an internal one) and you'll be blocked.
The solution that avoids all of this and allows you to still use IClassFixture<Program> is to make the Program class public. You can obviously do this by getting rid of the magic no class version of Program.cs, but if you don't want to completely change that file you can just add this line:
public partial class Program { } // so you can reference it from tests
Of course once it's public you can use it from your test project and everything works.
As an aside, the reason why you typically want to prefer using IClassFixture is that it allows you to set up your WebApplicationFactory just once in the test class constructor, and grab an HttpClient instance from it that you can store as a field. This allows all of your tests to be shorter since they only need to reference the client instance, not the factory.
Example:
public class HomePage_Get : IClassFixture<CustomWebApplicationFactory>
{
private readonly HttpClient _client = new HttpClient();
public HomePage_Get(CustomWebApplicationFactory factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task IncludesWelcome()
{
HttpResponseMessage response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
string stringResponse = await response.Content.ReadAsStringAsync();
Assert.Contains("Welcome.", stringResponse);
}
}
Finally note that Damian Edwards' MinimalAPIPlayground was updated to use this approach after we discussed the issue. See this commit
The problem is was solved on ASP.NET Core RC1, but as of now (September 20, 2021) the docs are incomplete.
The compiler generates a Program class behind the scenes that can be used with WebApplicationFactory<>. The class isn't public though so the InternalsVisibleTo project setting should be used.
Damien Edwards' Minimal API sample uses the latest nightly bits. The test web app class is declared as :
internal class PlaygroundApplication : WebApplicationFactory<Program>
{
private readonly string _environment;
public PlaygroundApplication(string environment = "Development")
{
_environment = environment;
}
protected override IHost CreateHost(IHostBuilder builder)
{
...
In the application project file,InternalsVisibleTo is used to make the Program class visible to the test project:
<ItemGroup>
<InternalsVisibleTo Include="MinimalApiPlayground.Tests" />
</ItemGroup>
RC1 is feature complete and, judging by previous major versions, it will probably be the first version to have a Go Live license, which means it's supported in production.
I tried
<InternalsVisibleTo Include="MinimalApiPlayground.Tests" />
but no cigar! Removed it and added a partial class to program.cs
#pragma warning disable CA1050 // Declare types in namespaces
public partial class Program
{
}
#pragma warning restore CA1050 // Declare types in namespaces
amazingly it worked.

Testing an injected config with spring without spring test runner

Given the following class:
public class ClassToBeTest{
${property.utility}
private String property;
private Utility utility;
public ClassToBeTested(Utility utility){
this.utility = utility;
}
public void doSomething(){
utility.doSomething(property);
}
}
We want to Mock Utility (we are using Mockito for instance) and verify that utility.doSomething is called passing the property parameter.
We don't really care about the config value (we have other tests for that). But we don't want to open the class with a method like (or passing the property by constructor).
public void setPropertyForTest(String property){
this.property = property;
}
Is there other ways to check that this private property (with no value) is passed to Utility?
Thanks a lot.
If you just want to set the private field, Spring (not Sprint) provides support for that in the spring-test module. See the setField() variants in ReflectionTestUtils.
Regards,
Sam

mvc4 bundling strongly typed bundles

So MVC 4 introduces script and style bundling. Which allows for this:
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/mobile").Include(
"~/Scripts/jquery.mobile-*"));
then used in a razor view like this:
#Scripts.Render("~/bundles/mobile")
My question is why do I have to type "~/bundles/mobile"? Is there a way get intellisence to have a strongly typed object to pick up on? Otherwise I have to go look it up to make sure I call it the same thing.
I would like to do something like this: (I know this won't compile this way, it's just an example)
public static void RegisterBundles(BundleCollection bundles)
{
Bundle mobile = new Bundle("mobile");
mobile.AddFile("w/e")
bundles.Add(mobile);
//in page:
#Scripts.Render(BundleConfig.mobile)
or something to that affect.
Edit: the answer so simple. As #Hao Kung points out #Styles.Render simply takes a url string path. I created a class to hold the pathes.
public class bundles
{
#region Javascript
public static string scripts = "~/bundles/scripts";
...
#endregion
#region CSS
public static string css = "~/Content/css";
public static string jqueryUi = "~/Content/themes/base/css";
...
#endregion
}
in any page then you simply do
#Styles.Render(bundles.jqueryUi)
there you have it. A little extra effort on your part, but at least it's strongly typed now.
The Render Scripts/Styles Render helpers are not limited to rendering references to bundles, they resolve any urls, so the only way for the helper to detect that you mean to reference a bundle, is by passing in the virtual path of the bundle.