I have a library which I use in both an ASP.NET app and a .NET Core app.
In both apps, I need to load settings from web.config(asp) in a virtual directory /CMSContent/Settings/web.config and appsettings.json(core).
I set an enviromentvariable in both apps named SystemType to WebForms(asp) and .NET Core (core), and build a function which reads data in the config file.
public static string SolutionDB()
{
string SystemType = System.Environment.GetEnvironmentVariable("SystemType", EnvironmentVariableTarget.Process);
switch (SystemType)
{
case "NetCore":
using (System.IO.StreamReader sr = new System.IO.StreamReader("appsettings.json", Encoding.UTF8))
{
var json = sr.ReadToEnd();
}
return "ComitoCMS_1";
case "WebForms":
System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration("/CMSContent/Settings/");
return config.AppSettings.Settings["SolutionDB"].Value;
break;
default:
return string.empty;
}
return string.empty;
}
When accessing the function from .net core it always returns the error:
TypeLoadException: Could not load type 'System.Web.Configuration.WebConfigurationManager' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
Even though the code doesn't get into the case "WebForms".
Is there any other way to read either web.config when running the asp.net app and from appsettings.json when running the .net core app
I would suggest to create library with an abstraction. For example ConfigurationValueProvider class.
public abstract class ConfigurationValueProvider
{
public abstract GetValue(string key);
}
Then create another two libvraries. One with implementation for .NET Core and second with implementation for WebForms.
NET Core
public class AppSettingsValueProvider : ConfigurationValueProvider
{
public override GetValue(string key)
{
// Load value for NET Core apps
}
}
WebForms
public class WebConfigValueProvider : ConfigurationValueProvider
{
public override GetValue(string key)
{
// Load value for WebForms apps
}
}
Each project type should reference just the one it is supposed to be used.
It is an idea how to do it. You should change it according to your needs.
Related
I am trying to create a simple GET API endpoint which will return the JSON of the current environment appsettings.json file, for example for the development environment it will return the contents of appsettings.Development.json file and for production environment it will return the contents of appsettings.Production.json file.
I don't know a nice way to dump the entire config file. What I know though is ways to read single config values through the injected config["Key"] or read a section through
config.GetSection("SectionName").Get<MyCustomSectionClass>()
approach. These options are not feasible as the file is big and the content may change.
This is an ASP.NET Core 6 Core Web API application, created through the default Visual Studio template.
If you want to return entire appsettings.xxx.json file, You can try this simple demo. I not sure if there is a better method, But this method works well in my project.
using Newtonsoft.Json;
private readonly IWebHostEnvironment _env;
public WeatherForecastController(IWebHostEnvironment env)
{
_env = env;
}
[HttpGet]
public IActionResult Get()
{
if (_env.IsDevelopment())
{
var path = Path.Combine(_env.ContentRootPath, "appsettings.Development.json");
string json = System.IO.File.ReadAllText(path);
object jsonObject = JsonConvert.DeserializeObject(json);
return Ok(jsonObject);
}else if (_env.IsProduction())
{
//read from appsettings.Production.json
return Ok();
}
else
{
//..........
return Ok();
}
}
Context
I am currently working on a .net Core 3.1 API that will replace an old .NET Framework and Delphi back-end. The API needs to supports globalization and localization to translate error messages and a few data values.
The localization is passed in through the route for example:
http://localhost/en-US/controller/action/params. So connected apps can quickly switch from localization.
The API has a Resources folder with the following files:
Resources:
- SharedResources.resx default and fallback language file nl-NL)
- SharedResources.en-US.resx
- SharedResources.de-DE.resx
In the Startup.cs I use installers to seperate concerns.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Extensions method that execute the install function on all classes that implement
// IInstaller interface
services.InstallServicesInAssembly(Configuration);
}
}
And I made a specific installer for the localization:
public class LocalizationInstaller : IInstaller
{
public void InstallServices(IServiceCollection services, IConfiguration configuration)
{
var localizationConfig = new LocalizationConfig();
configuration.GetSection(nameof(LocalizationConfig)).Bind(localizationConfig);
services.AddSingleton(localizationConfig);
services.AddLocalization(options => options.ResourcesPath = LocalizationConfig.ResourcePath);
services.Configure<RequestLocalizationOptions>(options =>
{
options.DefaultRequestCulture = LocalizationConfig.GetDefaultRequestCulture();
options.SupportedCultures = LocalizationConfig.GetSupportedCultures();
options.SupportedUICultures = LocalizationConfig.GetSupportedCultures();
options.RequestCultureProviders = new[] { new RouteDataRequestCultureProvider { Options = options } };
});
services.Configure<RouteOptions>(options =>
{
options.ConstraintMap.Add("culture", typeof(LanguageRouteConstraint));
});
}
The Data of LocalizationConfig is from the appsettings.json file.
"LocalizationConfig": {
"ResourcePath": "Resources",
"SupportedCultures": [ "nl-NL", "en-US", "de-DE" ],
"DefaultRequestCulture": "nl-NL"
}
The problem
[HttpGet("{version}/{culture}/Index")]
public IActionResult Index()
{
var culture = $"CurrentCulture{CultureInfo.CurrentCulture.Name},CurrentUICulture{CultureInfo.CurrentUICulture.Name}";
var value = _localizer["HelloWorld"].Value;
return Ok(value);
}
When I make a request to the API with the culture being nl-NL or a random value it returns the default value in the SharedResources.resx like it is supposed to do.
For example:http://localhost/1/nl-NL/Controller/Index returns Hallo Wereld!.
But when I make a request with the culture being either en-US or de-DE it still returns the value from the SharedResources.rex file instead of the SharedResources.en-US.resx file or SharedResources.de-DE.resx file
while the culture from my variable var culture is being set to en-US or de-DE.
Notes
When I name my SharedResoures.en-US.resx to SharedResources.en.resx it does seem to work and the application finds the translation files. But this does not solve my issue that I want to specify the region code so that i can support both en-GB and en-US.
The Question
Why can the application find the file without the region code and not the file that does contain the region code?
Microsoft docs: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-3.1
I'll start out with the questions first and follow up with context:
Is there a version of Unity.Interception available that is compatible with .NET Core?
Is there a .NET Core compatible alternative to Unity.Interception?
I am looking at using the Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptor to short-circuit calls to certain interfaces (example code below), but it seems that the suggested NuGet package, Unity.Interception 4.0.1, is not compatible with .NET Core.
I have made an attempt to shoe-horn in the usage of Unity.Interception 4.0.1, as the code snippets used works fine in classic .NET; but as mentioned I am running into problems with .NET Core:
Install-Package : Package Unity.Interception 4.0.1 is not compatible with netcoreapp1.1 (.NETCoreApp,Version=v1.1). Package Unity.Interception 4.0.1 supports: net45 (.NETFramework,Version=v4.5
)
I tried to circumvent this by adding net451 to the PackageTargetFallback list:
<PackageTargetFallback>$(PackageTargetFallback);net451;dnxcore50;portable-net451+win8</PackageTargetFallback>
This allowed me to install the package, but it then complains something fierce about needing a reference to mscorlib:
Error CS0012
The type 'Type' is defined in an assembly that is not referenced. You must add a reference to assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
I'm not going to attempt to reference in the Classic .NET framework into a .NET Core application, so I'm pretty much at a dead end here.
Example code:
public class Interceptions
{
public static object CreateCustomInterceptedProxy(Type type)
{
var interceptor = new InterfaceInterceptor();
var proxy = interceptor.CreateProxy(type, null);
var interceptionBehavior = new CustomInterceptionBehavior();
proxy.AddInterceptionBehavior(interceptionBehavior);
return proxy;
}
}
public class CustomInterceptionBehavior : IInterceptionBehavior
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
object response = null; // replace with stuff that builds an actual object
return input.CreateMethodReturn(response, new object[] { });
}
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public bool WillExecute => true;
}
I know the question is for around a month ago, but I thought it might be useful for other developers as well (because It's been a nightmare for me).
I have forked out Unity project and ported that to .Net Core 2.0. You can find it under this repository:
https://github.com/Chavoshi/Unity.NetCore
And also these are the nuget packages:
https://www.nuget.org/packages/Unity.NetCore/
https://www.nuget.org/packages/Unity.Interception.NetCore/
P.S: The only part which I was not able to port is TransparentProxyInterception that uses .Net Remoting which is totally discontinued in .Net Core.
Unfortunately you have to use 3rd party libraries like:
Unity fork: https://www.nuget.org/packages/Unity.Interception.NetCore/
Dora Interception: https://www.nuget.org/packages/Dora.Interception/ it has a detailed usage documentation here.
It seems Castle.Core's DynamicProxy is what I needed:
using Castle.DynamicProxy;
public class CustomInterceptor : IInterceptor
{
public static object CreateCustomInterceptedProxy(Type type)
{
var proxyGenerator = new ProxyGenerator();
var interceptor = new Interceptor();
var proxy = proxyGenerator.CreateInterfaceProxyWithoutTarget(type, interceptor);
return proxy;
}
}
public class CustomInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
object returnValue; // Do stuff to populate return value
invocation.ReturnValue = returnValue;
}
}
I am creating a website using Orchard CMS and I have an external .NET project written with Ninject for dependency injection which I would like to use together with a module within Orchard CMS. I know that Orchard uses Autofac for dependency injection and this is causing me problems since I never worked with DI before.
I have created an Autofac module, UserModule, which registers the a source, UserRegistrationSource, like this:
UserModule.cs
public class UserModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterSource(new UserRegistrationSource());
}
}
UserRegistrationSource.cs
public class UserRegistrationSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents
{
get { return false; }
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var serviceWithType = service as IServiceWithType;
if (serviceWithType == null)
yield break;
var serviceType = serviceWithType.ServiceType;
if (!serviceType.IsInterface || !typeof(IUserServices).IsAssignableFrom(serviceType) || serviceType != typeof(IUserServices))
yield break;
var registrationBuilder = // something...
yield return registrationBuilder.CreateRegistration();
}
}
UserServices.cs
public interface IUserServices : IDependency
{
void Add(string email, string password);
}
public class UserServices : IUserServices
{
private readonly EFMembershipManager _manager;
public UserServices(EFMembershipManager manager)
{
_manager = manager;
}
public void Add(string email, string password)
{
_manager.createUser(email, password);
}
}
EFMembershipManager.cs constructor
public EFMembershipManager(ServerRepository db,
ServerRepositoryMembershipProvider membershipProvider,
string testUsername,
string serverUsername)
{
...
}
EFMembershipManager is a class from the external project which uses Ninject for DI's and uses ServerRepository and ServerRepositoryMembershipProvider whom also are injected using Ninject.
And now I'm stuck...
Should UserRegistrationSource take the Ninject container (kernel) as a constructor argument and try to find the IUserServices service and then mediate the resolves to the Ninject kernel and return an empty Enumerable so that Autofac doesn't try to resolve anything related to IUserServices or is this the wrong approach?
Autofac supports registration sources (and more on registration sources here). A registration source is a service that the container will consult when trying to resolve a type. The source can respond, either with a means to build the type, or an empty list which indicates that the source is not able to provide the requested type.
In your case, a registration source could be implemented that will try to resolve the requested type from your Ninject container.
I'm not too familiar with Orchard but I'm guessing that it uses configuration files to configure Autofac. My suggestion is that you create a simple Autofac module that registers your registration source implementation, and that you configure Orchard to load the module from config.
I have a MVC project that i use structuremap for DI. I scan for a directory for all types of product and i register these in structuremap, so if some one adds some assembly in that directory i need that structuremap rescan that directory and add only the Products that are not register.
public static IContainer Initialize() {
ObjectFactory.Initialize(x => x.Scan(scan =>
{
scan.AssembliesFromPath(#"D:\Modules\");
scan.AssemblyContainingType(typeof (Product));
scan.AddAllTypesOf<Product>().NameBy(f=> f.Name);
scan.With(new ProductConvention());
}));
return ObjectFactory.Container;
}
public class ProductConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (type.BaseType == null) return;
if (type.BaseType.Equals(typeof(Product)))
{
registry.AddType(typeof (Product), type);
}
}
}
How can i do that in structuremap THX.
You would need to use a FileSystemWatcher to see when new files are added, then call
ObjectFactory.Configure(x => x.Scan(...));
Yes, you can use a FileSystemWatcher on a web server unless you are running in medium trust. You need to know when files are added, otherwise you don't know when to tell StructureMap to scan for new types.