I created an MVC application using .Net 5.
Then I created a custom Attribute should read some settings from appsettings.json.
Here a working solution:
public class MyCustomAttribute : Attribute
{
public MyCustomAttribute(string key)
: base()
{
IConfiguration conf = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
var value = conf.GetValue<string>(key);
...
}
...
}
It works, but I do not think it is the correct solution. I try to explain why.
In the Program.cs, beforse the host is builded, the startup class is instantiated:
var builder = Host.CreateDefaultBuilder(args); builder
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
If I inject the IConfiguration to the stratup, I can get the appsettings.json values:
I think that from now, I have the appsettings.json and conseguently the configuration in memory.
So it seems really strange to me that in my custom attribute I must read the setting from the file again!
The question is: How can I read the in-memory configuration inside my custom attribute?
Is my consideration correct?
Thank you.
EDIT
I have found another solution, but I still does not like it:
I have modified the startup.cs ctor in this way:
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
this._environment = environment;
this._configuration = configuration;
AppDomain.CurrentDomain.SetData("conf", this._configuration);
}
And the ctor of MyCustomAttribute in this way:
public class MyCustomAttribute : Attribute
{
public MyCustomAttribute(string key)
: base()
{
var conf = AppDomain.CurrentDomain.GetData("conf") as IConfiguration;
var value = conf.GetValue<string>(key);
...
}
...
}
Also this solution works. But I hope something out-of-the-box in .Net 5 exists. I would expect the normal behavior when the configuration is read, would be something similar to my solution.
Related
Program.cs
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
string foo = builder.Configuration.GetValue<string>("foo"); // Is null. Shoudn't be.
public partial class Program{}
Test project
public class MyWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((context, configBuilder) =>
{
configBuilder.AddInMemoryCollection(
(new Dictionary<string, string?>
{
["foo"] = "bar"
}).AsEnumerable());
});
}
}
public class Test
{
private readonly HttpClient _client;
public Test(MyWebApplicationFactory<Program> factory)
{
_client = factory.WithWebHostBuilder().CreateClient();
But the new settings are never added -- when I debug the test foo is always null.
I don't see how the settings can added, either, because Program creates a new builder that never goes anywhere near the WebApplicationFactory.
I think this might be something to do with my Program needing to read settings in order to configure services, but the settings not being updated by the test system until after the services have been configured?
Can it be made to work?
I want to read a simple connection string from the appsettings.json file in F#
"ConnectionStrings": {
"myConnectionString": "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;"
}
In C# I could use the read the connection string with an object from a class implementing the Iconfiguration provided in the constructor. But in the F# web API with .net 6, I couldn't find anything that made me read the connection string section or an other key in from the AppSettings.json file
Here is an example how I could read from the Appsettings.json in C# .net 6 Web API. This is what I want to do.
public class TestModel : PageModel
{
// requires using Microsoft.Extensions.Configuration;
private readonly IConfiguration _configuration;
public TestModel(IConfiguration configuration)
{
_configuration = configuration;
}
public void Connect()
{
var connectionString = _config.GetConnectionString("myConnectionString");
// Do Something with the string
}
}
The F# implementation of the Web API project is very similar to the C# one. In order to use the configuration in one of your classes you just need to inject the IConfiguration parameter to the constructor of your class. The default bootstrap code takes care of setting up the dependency injection for the configuration framework.
I created a sample Web API project for F# and injected the IConfiguration to the controller:
namespace WebApiTest.Controllers
open System
open System.Collections.Generic
open System.Linq
open System.Threading.Tasks
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.Logging
open Microsoft.Extensions.Logging
open WebApiTest
open Microsoft.Extensions.Configuration
[<ApiController>]
[<Route("[controller]")>]
type WeatherForecastController (logger : ILogger<WeatherForecastController>, configuration: IConfiguration) =
inherit ControllerBase()
let summaries =
[|
"Freezing"
"Bracing"
"Chilly"
"Cool"
"Mild"
"Warm"
"Balmy"
"Hot"
"Sweltering"
"Scorching"
|]
[<HttpGet>]
member _.Get() =
let cstr = configuration.GetConnectionString("MyConnectionString")
logger.LogInformation($"Connection string: {cstr}")
let rng = System.Random()
[|
for index in 0..4 ->
{ Date = DateTime.Now.AddDays(float index)
TemperatureC = rng.Next(-20,55)
Summary = summaries.[rng.Next(summaries.Length)] }
|]
As you see, the configuration parameter injected in the constructor is accessible as a private member variable in the Get() method. I just log the connection string to illustrate that the code works as expected.
Here's my appsettings.json:
{
"ConnectionStrings": {
"MyConnectionString": "SampleConnectionString"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
I am having issue using Simple Injector with WebAPI project that gets created default with VS 2015.
I am having the AccountController having the below constructor
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
In order to register these I used the below code in Simple Injector
// Create the container.
var apiContainer = new Container();
apiContainer.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
apiContainer.Options.ConstructorResolutionBehavior = new ConstructorBehavior();
//register the classes that we are going to use for dependency injection
apiContainer.Register<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>(new ApplicationDbContext()),Lifestyle.Scoped);
apiContainer.Register<IDataProtector>(() => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider().Create("ASP.NET Identity"),Lifestyle.Transient);
apiContainer.Register<ISecureDataFormat<AuthenticationTicket>, SecureDataFormat<AuthenticationTicket>>(Lifestyle.Transient);
apiContainer.Register<ITextEncoder, Base64UrlTextEncoder>(Lifestyle.Scoped);
apiContainer.Register<IDataSerializer<AuthenticationTicket>, TicketSerializer>(Lifestyle.Scoped);
//apiContainer.RegisterCommonClasses();
//register the webapi controller
apiContainer.RegisterWebApiControllers(configuration);
but after this I am getting the warning message that says
[Disposable Transient Component] ApplicationUserManager is registered as transient, but implements IDisposable.
Can someone Please help me with this how to resolve this ? With Default Web api project with VS 2015 it adds Account controller and that use ApplicationUserManager and has below details
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
Another issue I am getting as below
The constructor of type HttpConfiguration contains the parameter with name 'routes' and type HttpRouteCollection that is not registered. Please ensure HttpRouteCollection is registered, or change the constructor of HttpConfiguration.
This is with the HelpController as it uses the below details:
public HelpController()
: this(GlobalConfiguration.Configuration)
{
}
public HelpController(HttpConfiguration config)
{
Configuration = config;
}
I'm migrating an AEM 6.1 application to AEM 6.3. Since Felix annotations (org.apache.felix.scr.annotations.*) are deprecated, I decided to migrate my components to the OSGi annotations (org.osgi.service.component.annotations.*).
Once I figured out how it works, it is pretty easy. But there is one case I don't know how to handle: Properties with propertyPriavte = true.
The old implementation looks like this:
#Component(metatype = true)
#Service(Servlet.class)
#Properties({
#Property(name = "sling.servlet.selectors", value = "overlay", propertyPrivate = true),
})
public class OverlayServletImpl extends OverlayServlet {
...
}
The property sling.servlet.selectors would not be configurable in the Configuration Manager at the AEM console, but it would be configurable due to a config file, right? So, I still need to define this property.
For other properties I changed my implementation like this:
// OverlayServletImpl
#Component(
service = Servlet.class,
configurationPid = "my.package.path.OverlayServletImpl"
)
#Designate(
ocd = OverlayServletImplConfiguration.class
)
public class OverlayServletImpl extends OverlayServlet {
...
}
// Configuration
#ObjectClassDefinition(name = "Overlay Servlet")
public #interface OverlayServletImplConfiguration {
String sling_servlet_selectors() default "overlay";
...
}
Now, I have the property sling.servlet.selectors, but it is also available in Configuration Manager and it'S value can be changed there. But I don't want that.
How can I do that? Is this possible with the OSGi annotations?
Thank you and best regards!
It looks like this might be possible if you use the #Component annotation to specify your private properties.
#Component(service = Servlet.class,
property =
{ SLING_SERVLET_RESOURCE_TYPES + "=aemhtlexamples/structure/page",
SLING_SERVLET_METHODS + "=GET",
SLING_SERVLET_EXTENSIONS + "=html",
SLING_SERVLET_SELECTORS + "=hello" })
public class SimpleServlet extends SlingSafeMethodsServlet {
#Override
protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp)
throws ServletException, IOException {
final Resource resource = req.getResource();
resp.getOutputStream().println(resource.toString());
resp.getOutputStream().println("This content is generated by the SimpleServlet");
}
}
Source: https://github.com/heervisscher/htl-examples/blob/master/core/src/main/java/com/adobe/examples/htl/core/servlets/SimpleServlet.java
As far as I know this is not possible. Every property you define can be overridden by config.
I've added Help pages Nuget package to create documentation for my Web API but it doesn't work for me, no API methods are shown.
I uncommented line :
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
I checked box XML documentation file and set path to App_Data/XmlDocument.xml
I don't use Glimpse as many solutions here write about it.
I even installed nuget package for help pages with authorization but it doesn't help
What is wrong with this? If I start empty project than it is working fine, but this API is too big to start all over again.
In case you are using OWIN as middleware (just like me), you may be initializing a new HttpConfiguration inside it´s startup method. The problem is that the HelpController and the HelpPageConfig are using GlobalConfiguration.Configuration, which seems to be wrong. What helped me:
Step 1: make the startup HttpConfiguration a static field
[assembly: OwinStartup(typeof(MyProject.API.Startup))]
namespace MyProject.API
{
public class Startup
{
//new
public static HttpConfiguration HttpCfg;
//
public void Configuration(IAppBuilder app)
{
HttpCfg = new HttpConfiguration();
WebApiConfig.Register(HttpCfg);
app.UseWebApi(HttpCfg);
AreaRegistration.RegisterAllAreas();
}
}
}
Step 2: go to HelpPageAreaRegistration and edit the RegisterArea method like this
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"HelpPage_Default",
"Help/{action}/{apiId}",
new { controller = "Help", action = "Index", apiId = UrlParameter.Optional });
//old
//HelpPageConfig.Register(GlobalConfiguration.Configuration);
//new
HelpPageConfig.Register(Startup.HttpCfg);
}
Step 3: go to HelpController and edit the standard constructor like this
//old
//public HelpController() : this(GlobalConfiguration.Configuration){ }
//new
public HelpController() : this(Startup.HttpCfg){ }
I hope this helps and isn't too late ;)