I'm having trouble configuring localization in my asp.net 7.0 MVC project.
Configuration:
.AddLocalization(opts => opts.ResourcesPath = "Resources")
then
CultureInfo[] supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("bg-BG")
};
mvcBuilder
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
mvcBuilder
.Services
.Configure<RequestLocalizationOptions>(options =>
{
options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
This is called before
.AddRazorPages();
And at the end
app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
I have installed Microsoft.Extensions.Localization nuget.
I have two resource files in folder Resources
Controllers.HomeController.bg-BG.resx
Controllers.HomeController.en-US.resx
In both resources there is entry "title" with some values
Injected IStringLocalizer into HomeController but everytime it returns only "title", used it like this:
this.stringLocalizer["title"].Value
After hours of trial and error I really can't seem to find what's the problem.
You appear to be mixing up two different localization techniques. The .resx resource files is the classic .NET technique for localization while the IStringLocalizer based approach has been added in .NET Core. Fortunately, it is still perfectly fine to utilize .resx resources .NET Core Views/Pages.
Add a using statement at the top of your view. Make sure that the namespace matches the actual C# namespace of your resource. The example below assumes that the default namespace of the project is WebApp and the .resx files live inside a Resources folder:
#using WebApp.Resources
Referencing a resource string inside your view is straight forward:
<h2>#Controllers_HomeController.title</h2>
NOTE: The underscore in Controllers_HomeController is caused your use of a . in the resource filename, which would cause problems with the strongly typed generated class inside the corresponding Controllers_HomeController.Designer.cs
The problem was something to do with the namespaces, changed the default namespace and it works now.
Related
I have a Class Library that I'm converting to a .Net Standard 2 class library in order to also use in ASP.Net Core 2.0 projects.
The library has always read from a config file items such as SMTP settings, connection strings etc.
In Web Projects it finds these values in web.config.
In Console/WinForms it finds these values in app.config.
Is there an equivalent config file for .Net Core 2.0 projects that "just works" like the previous examples?
I assume the answer is no, but looking for best way to handle this given the library is used across the organization, so maintaining backwards compatibility is important.
Turns out System.Configuration.ConfigurationManager was added back in .NETStandard 2.0.
Just pull it in from nuget and compile the .NETStandard 2.0 class library project.
Then, the library will work across projects using standard config files:
Net Core 2.0 projects use app.config
Web projects work from web.config
Console and Windows apps work with app.config
.Net Core revised configuration approach greatly.
You don't call ConfigurationManager.AppSettings["someSetting"] anymore whenever you need value for some setting. Instead you load configuration on application startup with ConfigurationBuilder. There could be multiple configuration sources (json or/and xml configuration file, environment variables, command line, Azure Key Vault, ...).
Then you build your configuration and pass strongly typed setting objects wrapped into IOption<T> to consuming classes.
Here is a basic idea of how it works:
// Application boostrapping
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("AppSettings.json");
var configuration = configurationBuilder.Build();
// IServiceCollection services
services.AddOptions();
services.Configure<SomeSettings>(configuration.GetSection("SomeSection"));
// Strongly typed settings
public class SomeSettings
{
public string SomeHost { get; set; }
public int SomePort { get; set; }
}
// Settings consumer
public class SomeClient : ISomeClient
{
public SomeClient(IOptions<SomeSettings> someSettings)
{
var host = someSettings.Value.SomeHost;
var port = someSettings.Value.SomePort;
}
}
// AppSettings.json
{
"SomeSection": {
"SomeHost": "localhost",
"SomePort": 25
}
}
For more details check article Configure an ASP.NET Core App.
I'm afraid that it will be difficult (trying to avoid word 'impossible') to maintain backward compatibility.
I am building an application in ASP.NET Core 2.0 and I am having problems with EntityFramework Migrations.
I have my DbContext in a separate project (SolutionName\ProjectNamePrefix.Data) and therefore I created an implementation for the IDesignTimeDbContextFactory interface.
I wanted to use different connection strings for different environments and I need appsettings.json for that.
So after a quick search I found that I can create a new IConfigurationRoot object inside the CreateDbContext function as shown here:
https://codingblast.com/entityframework-core-idesigntimedbcontextfactory/
I added that and then for testing, tried to run dotnet ef migrations list -c MyContext from the Data project root folder.
Then I got the following error:
The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'C:\dev\*SolutionName*\*ProjectNamePrefix*.Data\bin\Debug\netcoreapp2.0\appsettings.json'.
So, basically, I tried 3 options for getting the correct root path:
Directory.GetCurrentDirectory();
env.ContentRootPath; (IHostingEnvironment object, I found a way to get it here: https://github.com/aspnet/Home/issues/2194)
AppDomain.CurrentDomain.BaseDirectory;
and all of them returned the same ..\bin\debug\netcoreapp2.0\ path. When I run the Data project from VS, then the two first options give me the correct project root folder.
Is there a way to get the correct project content root folder?
Because when I added --verbose to the EF command, it logged out a row:
Using content root 'C:\dev\FitsMeIdentity\FitsMeIdentity.Data\'.
So I understand that EF somehow knows the project root but all the options mentioned above return the path for the already built application.
The only option I found that works is that I change Copy output to root folder to Copy always but found from here: https://www.benday.com/2017/02/17/ef-core-migrations-without-hard-coding-a-connection-string-using-idbcontextfactory/ that it's not a good idea.
At first I even thought about creating a Constructor for the IDesignTimeDbContextFactory implementation which gets IOptions as a parameter but that didn't work, had the same problem as explained here:
Injecting Env Conn String into .NET Core 2.0 w/EF Core DbContext in different class lib than Startup prj & implementing IDesignTimeDbContextFactory
A little late, but here is the solution for those who hate hard-coding connections strings:
internal class MigrationDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", false)
.Build();
string connectionString = configuration.GetConnectionString("DefaultConnection");
DbContextOptionsBuilder<AppDbContext> optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
optionsBuilder.UseMySql(connectionString,
ServerVersion.AutoDetect(connectionString),
mySqlOptions =>
mySqlOptions.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null));
return new AppDbContext(optionsBuilder.Options);
}
}
No. You can't do this, and more to the point: you're not supposed to do this. The whole entire point of IDesignTimeDbContextFactory is that it's a way to get a DbContext instance from in a context where there is no ASP.NET Core framework to work with, i.e. from a class library. If you're running migrations from an ASP.NET Core project, you don't need it, and if you're not, none of the configuration stuff is available.
Additionally, it's only to be used for development, hence the "DesignTime" part of the name. As a result, there's no need for stuff like switching between connection strings for different environments. Just hard-code the connection string as the docs detail.
I have an ASP .NET 5 RC1 website to which I am trying to add localization.
Based on the information found I did the following
In the ConfigureService in Startup.cs:
Enable Localization and setting the ResourcePath to "Resources"
Enable View Localization and Enable Data Annotations Localization
//check http://damienbod.com/2015/10/21/asp-net-5-mvc-6-localization/
services.AddLocalization(options => options.ResourcesPath = "Resources");
// Add MVC services to the services container.
//check http://blogs.msdn.com/b/webdev/archive/2015/10/15/announcing-availability-of-asp-net-5-beta8.aspx
services.AddMvc().AddViewLocalization().AddDataAnnotationsLocalization();
In the Configure method fin Startup.cs
Setup the list of supported cultures
Enable Request Localization
//check http://www.jerriepelser.com/blog/setting-thread-culture-aspnet5
//check http://damienbod.com/2015/10/21/asp-net-5-mvc-6-localization/
List<CultureInfo> supportedCultures = new List<CultureInfo>()
{
new CultureInfo("en"),
new CultureInfo("es")
};
var requestLocalizationOptions = new RequestLocalizationOptions()
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
app.UseRequestLocalization(requestLocalizationOptions, new RequestCulture(new CultureInfo("es")));
Create a Resources folder under the project
Create the Resources for the Controller. With the convention {Project}.{Controllers}.{ControllerClassName}.{culture}.resx
Create the Resources for the Views. With the convention Views.{ViewFolder}.{ViewName}.cshtml.{culture}.resx
Use the IHtmlLocalizer in the controller, and access the item. In this case localizer["Title"], which is found and works just fine. However when the culture is set to "es" it is not found and just falls back to the default resource.
private IHtmlLocalizer<HomeController> _htmlLocalizer;
public HomeController(IOptions<PTIWebPortal.Configuration.PTIWebPortalConfiguration> pConfiguration,
ILoggerFactory factory, IHtmlLocalizer<HomeController> localizer) : base(pConfiguration, factory)
{
this._htmlLocalizer = localizer;
}
The same is happens for the views, it only works with the default resource, but not with the others.
Any ideas on how to fix it?
There are a lot of known issues of things not working for localization as of rc1.
Some of the known issues are tooling related, so some of the localization things work if you run the app from the command line instead of launching from visual studio.
But even from the command line a lot of things that were supposed to work do not work.
There has been quite a bit of work after rc1 and a lot of localization issues have been fixed and closed recently, so things should be much better after rc2 is released sometime in February.
I want to separate my MVC project into several projects
So first of all, I've created two projects Front and Views
The Front project is a web application that contains controllers and models
The Views project is a class library project that will contains only the views
My question is how can I make controllers call views located in the Views project
I have controllers like this one:
public ActionResult Default()
{
return this.View();
}
For including controllers you need to change your route registrations to tell them where to look for the controllers:
routes.MapRoute(name: "Default", url: "{controller}/{action}/{id}",
namespaces: new[] {"[Namespace of the Project that contains your controllers]"},
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional});
For including views, create custom ViewEngine:
public class CustomViewEngine: RazorViewEngine
{
public CustomViewEngine()
{
MasterLocationFormats = new string[]
{
"~/bin/Views/{1}/{0}.cshtml",
"~/bin/Views/{1}/{0}.vbhtml",
"~/bin/Views/Shared/{0}.cshtml",
"~/bin/Views/Shared/{0}.vbhtml"
};
ViewLocationFormats = new string[]
{
"~/bin/Areas/{2}/Views/{1}/{0}.cshtml",
"~/bin/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/bin/Areas/{2}/Views/Shared/{0}.cshtml",
"~/bin/Areas/{2}/Views/Shared/{0}.vbhtml"
};
.
.
.
}
}
protected void Application_Start()
{
ViewEngines.Engines.Add(new CustomViewEngine());
For more information look at the default implementation of RazorViewEngin.
Here some good articles:
A Custom View Engine with Dynamic View Location
Using controllers from an external assembly in ASP.NET Web API
How to call controllers in external assemblies in an ASP.NET MVC application
How do I implement a custom RazorViewEngine to find views in non-standard locations?
Views in separate assemblies in ASP.NET MVC
MVC does not compile views into DLL's, but instead references them as files from the root of your site directory. The location, by convention is ~/Views and a search path is followed. This is more or less hard coded into the default view engines.
Because Views are files, when you break them into a separate project, they won't exist in your primary web application project. Thus, the view engine can't find them. When you compile the app, any projects referenced will only copy the DLL's (and potentially a few other things, like pdb's, etc.)
Now, there are ways to work around this, but to be honest, they're usually more trouble than they're worth. You can look into "Portable Areas" in the mvc contrib project, but these are not well supported and there's been talk of replacing them with NuGet packaging.
You can also follow #mo.esmp's advice, and create a custom view engine, but you'll still need to figure out ways to copy the Views somewhere the site can access them upon build and/or deploy.
My suggestion would be to NOT break out projects in the manner you describe. I don't see any value in it. If your project becomes so large, I would instead separate your code into areas, and keep all your area code and data together.
What value is there in separating items that are clearly dependent upon each other into separate assemblies who's only purpose is to collect things based on their purpose? I see some value in separating models into their own project, since models can be used by more than one assembly. Controllers and views, however, are only ever used by the MVC primary site.
You can precompile your views - that way they are included in the dll and you can reference them from another project.
How to do it:
Move the views to another project
Install Razor Generator extension in Visual Studio
Change Custom Tool to RazorGenerator for those
views
Add RazorGenerator.Mvc NuGet package to the view project
Reference view project from your main project
That's it!
Although you'll need to do something with your models, either put them together with views or have a third project for them - otherwise you'll have a circular dependency.
Another drawback is that everyone who will be working with the views will need that Razor Generator extension.
The way this works is basically you make Visual Studio generate .cs files from your views in design time and those are a part of the compiled dll, same as any other piece of code.
I'm working on an ASP.NET host for a WCF service. The service references a C++/CLI wrapper library, which itself references an unmanaged DLL. Based on this question I've embedded the unmanaged DLL in the ASP.NET DLL. I then extract it like this:
string[] dlls = new [] { "myDLL.dll", "myDLLD.dll" };
Assembly assembly = Assembly.GetExecutingAssembly();
string location = Path.GetDirectoryName(assembly.Location);
Dictionary<string, Stream> streams =
(from dll in dlls
select new KeyValuePair<string, Stream>(
dll, assembly.GetManifestResourceStream(typeof(Global), dll)))
.ToDictionary(p => p.Key, p => p.Value);
foreach (KeyValuePair<string, Stream> stream in streams)
{
using (FileStream file = new FileStream(Path.Combine(location, stream.Key),
FileMode.Create))
{
stream.Value.CopyTo(file);
}
}
I've tried putting this code in Application_Start() in Global.asax.cs and in AppInitialize() in the App_Code folder, but in both cases I get a yellow screen of death about how the wrapper DLL or one of its dependencies could not be loaded before a breakpoint is hit in either function. The only way I can hit a breakpoint is by placing the unmanaged DLL somewhere in the system path (e.g. C:\Windows\system), but this obviously defeats the purpose of embedding the DLLs in the first place. How can I get the DLL where it needs to be before ASP starts looking?
Apparently ASP.NET's eager loading mechanism was the problem. Because the managed wrapper was copied to the output directory, ASP found it and tried to link to the unmanaged DLL on startup, even though it didn't exist yet. To solve the problem, I used the /DELAYLOAD linker option on the C++/CLI DLL and a LoadLibrary() P/Invoke in Application_Start() in combination with the embedded DLL extraction shown above.