Satellite Assembly, ASP.NET language not changing - asp.net

I'm converting a project to use a satellite assembly.
I created a new class library (named "Resources"). All of the default resources are at the root level *.resx.
Then there is a folder for each culture.
"en/"
with *.en.resx
en-GB/
with *.en-GB.resx
I changed the .resx files Access Modifier to "Public" and changed these settings.
BuildAction: EmbeddedResource
CopyToOutputDirectory: CopyAlways
I made sure the .resx designers *.Designer.cs use the "Resources" namespace.
I added the Class library to an ASP.NET MVC app and in global.asax.cs I have the culture being set as needed.
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
I tried adding the al.exe and Resgen.exe commands to the post-build event in the Resources class library.
"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\Resgen.exe"
en-GB/MyResourceFile.en-GB.resx "C:\Program Files\Microsoft
SDKs\Windows\v7.1\Bin\al.exe" /t:lib
/embed:en-GB/MyResourceFile.en-GB.resources /culture:en-gb
/out:Resources.resources.dll
Looking inside the MVC apps bin/ folder there are folders for each language with 3 files:
MyResources.en-GB.Designer.cs, MyResources.en-GB.resx,
Resources.resources.dll
The root level only has Resources.dll and Resources.resources.dll but the default language works fine. The second *.resources.dll is due to the post-build event.
When I change the CurrentUICulture it doesn't change the UI language. Before moving the resources out of the App_GlobalResources/ to an external assembly it worked fine (Razor example).
#Resources.MyResources.StringIdentifier
Looking at the Resource .dlls using .NET Reflector these are the differences.
Satellite Resources: (Project: Resources)
Resources.en_GB.MyResources.en-GB.resources
App_GlobalResources: (Project MVCApp)
Namespace.MVCApp.App_GlobalResources.MyResources.en-GB.resources
Edit:
After setting the CurrentCulture and UICulture in the global.asax.cs, I tested manually retrieving a ResourceManager which actually worked and retrieved strings in the expected language.
System.Resources.ResourceManager gStrings =
new System.Resources.ResourceManager("Resources.MyResources", System.Reflection.Assembly.Load("Resources")); // Resources=Project Name
string test = gStrings.GetString("TestString"); // WORKS IN GLOBAL.ASAX.CS!!!
I tried the same test in an MVC Controller, but it doesn't work. So the only place the ResourceManager can be retrieved is the Global.asax.cs.
Oddly enough the same test at the UI level in razor would only return the default cultures text! The cultureInfo is correct, but the ResourceManager only returns the default language in the ASP.NET MVC Razor UI. (This is strictly for testing)
#{
System.Globalization.CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentUICulture;
System.Resources.ResourceManager gStrings =
new System.Resources.ResourceManager("Resources.MyResource", System.Reflection.Assembly.Load("Resources"));
string s = gStrings.GetString("Test"); // DOESN'T WORK IN UI
}
TLDR version:
Converting from Resources (.resx) from App_GlobalResources to satellite assemblies.
The issue at hand.
I can manually call a ResourceManager for the satellite assembly, using the CurrentUICulture language in global.asax and it works as expected, however the same test fails from the UI side and fails in a MVC controller.
The default language works fine, so why doesn't the UI switch languages?

If have recently set up a project where the internaltionalization works correctly more or less like you describe.
https://github.com/Jacco/Perpetuality
I switch the language per HTTP request in OnAuthorize in the controller I use as base:
using Perpetuality.Data;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace Perpetuality.Controllers
{
public partial class BaseController : Controller
{
private CultureInfo GetCultureInfo(AuthorizationContext filterContext)
{
switch ((string)filterContext.RouteData.Values["language"])
{
case "nl":
return CultureInfo.CreateSpecificCulture("nl-NL");
case "en":
return CultureInfo.CreateSpecificCulture("en-US");
case "pt":
return CultureInfo.CreateSpecificCulture("pt-PT");
case "de":
return CultureInfo.CreateSpecificCulture("de-DE");
case "es":
return CultureInfo.CreateSpecificCulture("es-ES");
case "fr":
return CultureInfo.CreateSpecificCulture("fr-FR");
case "it":
return CultureInfo.CreateSpecificCulture("it-IT");
case "jp":
return CultureInfo.CreateSpecificCulture("ja-JP");
default:
return CultureInfo.CreateSpecificCulture("en-US");
}
}
protected override void OnAuthorization(AuthorizationContext filterContext)
{
Thread.CurrentThread.CurrentCulture = GetCultureInfo(filterContext);
Thread.CurrentThread.CurrentUICulture = GetCultureInfo(filterContext);
base.OnAuthorization(filterContext);
}
}
}
In my project the RESX files are specified differently than in yours:
BuildAction: EmbeddedResource
CopyToOutputDirectory: DoNotCopy
In my vies I access the strings like this:
#Resources.Home.Index.FacebookButtonCaption
I did setup nothing post or pre build.
In my project the language is always on the url. That was a user can switch language if no appropriate language was initially chosen.

Related

XUnit Net Core Web API Integration Test: "The ConnectionString property has not been initialized."

Just trying to build an Integration Test project for a NET Core Web API.
So I've followed a few examples, including this one (https://dotnetcorecentral.com/blog/asp-net-core-web-api-integration-testing-with-xunit/) and naturally, I run into issues. When I run the simple GET test I get an exception:
"System.InvalidOperationException : The ConnectionString property has not been initialized."
Any help would be appreciated.
For server = new TestServer(new WebHostBuilder().UseStartup<Startup>());, you need to manually configure the appsettings.json path like
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseContentRoot(#"D:\Edward\SourceCode\AspNetCore\Tests\IntegrationTestMVC")
// This is the path for project which needs to be test
.UseStartup<Startup>()
);
For a convenience way, I would suggest you try Basic tests with the default WebApplicationFactory.
The WebApplicationFactory constructor infers the app content root path by searching for a WebApplicationFactoryContentRootAttribute on the assembly containing the integration tests with a key equal to the TEntryPoint assembly System.Reflection.Assembly.FullName. In case an attribute with the correct key isn't found, WebApplicationFactory falls back to searching for a solution file (*.sln) and appends the TEntryPoint assembly name to the solution directory. The app root directory (the content root path) is used to discover views and content files.
Reference:How the test infrastructure infers the app content root path
I had to override CreateHostBuilder in my derived WebApplicationFactory in order to add the configuration for the connection string (since it was read from user secrets).
public class CustomApplicationFactory : WebApplicationFactory<Sedab.MemberAuth.Startup>
{
protected override IHostBuilder CreateHostBuilder()
{
var initialData = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("ConnectionStrings:DefaultConnection", "test")
};
return base.CreateHostBuilder().ConfigureHostConfiguration(config => config.AddInMemoryCollection(initialData));
}
}

Extensible ASP.NET Core 2 application

I need to create an ASP.NET Core 2 application that can be extensible.
An extension, is a project that reference the main assembly and can extends it adding new controllers, models (with EF migrations), views, razor pages, etc.
Because the extensions need use the main application base classes like base controller, base model or view/page layout, the main application cannot reference the module project (avoid circular references).
I'm not sure how can I achieve this, but the idea is an installation of the main project, can add new functionality simple putting the modules DLL (or by online market in the main application, that download the DLL).
In my research, I found Applications parts, but my problem here is I need specify the part assembly in Startup class, and I need in installed the capacity of install modules without doing any changes in the code.
Some modules, need be extensible too, for example, an accounting module, need to connect with bank, and it have an interface that defines the methods of working with the bank, for example:
public interface IBankingOperation
{
public async void PayInvoiceAsync(Invoice invoice);
// More methods...
}
Then, differents projects can reference the banking assembly and provide implementation for differents banks.
In the main application, this modules can be installed like other modules, but checking the base module is intalled, for example, I can install the Santander module like other module, but only if banking module is installed (module dependency).
In conclusion, I need to create a modular ASP.NET Core 2 application, but the main assembly cannot reference the modules, the modules must reference the main assembly. The modules can contain Controllers, Models, Views, Pages, Etc.
In the main web app you would call a class which loads the extensions in the memory
ModuleManager.LoadModules(Path.Combine(_env.ContentRootPath, "Modules"));
this is the load modules function
public static void LoadModules(string modulesFolder)
{
var appsFolders = new DirectoryInfo(modulesFolder).GetDirectories();
foreach (var appFolder in appsFolders)
{
var binFolder = new DirectoryInfo(Path.Combine(appFolder.FullName, "bin"));
if (!binFolder.Exists)
{
continue;
}
var assemblies = AssemblyProvider.GetAssemblies(binFolder.FullName);
foreach (var assembly in assemblies)
{
var iModuleClass = assembly.GetTypes().FirstOrDefault(type => type.GetInterfaces().Contains(typeof(IModule))
&& type.GetConstructor(Type.EmptyTypes) != null);
if (iModuleClass != null)
{
var module = Activator.CreateInstance(iModuleClass) as IModule;
module.ModuleFolder = appFolder;
Modules.Add(module);
Assemblies.Add(assembly);
break;
}
}
}
}
then you should have an interface which should be implemented by each module the class which implement this interface should do the work of registering services and database models and all staff needed and you will load them as follows
public static IEnumerable<IExtensionRegister> GetModuleRegistrars()
{
lock (Modules)
{
return Modules.Select(item => item.Registrar).Where(item=>item!=null).ToList();
}
}

Unit Test Project: "No connection string could be found in the application config file"

I had an existing MVC5 web app. I just created a new Unit Test Project and added the following code....
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SomethingApp.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SUT = SomethingApp.Services.ReportingServices; // SUT = System Under Test
namespace SomethingApp.Services.Tests
{
[TestClass]
public class GettingScoreForQuestionShould
{
[TestMethod]
public void ReturnScoreWhenGivenValidData()
{
// Arrange
int eventId = 39;
int questionId = 271;
decimal score;
// Act
score = SUT.GetScoreForQuestion(eventId, questionId);
// Assert
Assert.AreEqual("80",score);
}
}
}
When the method GetScoreForQuestion runs in the normal web app it runs perfect. But, when I run it through the test method I'm getting this error...
Message: Test method SomethingApp.Services.Tests.GettingScoreForQuestionShould.ReturnScoreWhenGivenValidData
threw exception: System.InvalidOperationException: No connection string
named 'myDbContext' could be found in the application config file.
The error is, of course, coming from the method GetScoreForQuestion, which works fine in the normal web app.
I don't understand why I need to add an application config file and this config connection string to the test project. Seems like, since I'm calling the method in the MVC project, that this has the responsibility of making the connection and doing it's thing (which it's doing in the normal course of the app). Am I mistaking something?
And, I tried adding a new application.config file and the connection string to the unit test project, but then the test method won't show up anymore in the Test Explorer after build. Any suggestions? Thanks!
UPDATE ****
Here's the code for GetScoreForQuestion (the offending method, which works in the web app fine, but not when called thru the test method)....
public static decimal GetScoreForQuestion(int eventId, int ThingyQuestionId)
{
// the following line fails with the connection issue
var ThingyResults = Db.ThingyResults.Where(e => e.EventId == eventId && e.ThingyQuestionId == ThingyQuestionId)
.AsNoTracking().ToList();
:
:
:
}
Db is declared in the same class as...
public static class ReportingServices
{
private static readonly ThingyContext Db = new ThingyContext();
When you are executing a unittest, that project is your running application. So that is where the configuration file is read from. And note that you need an app.config, not a web.config.
It looks like you may be creating a new ThingyContext within your ReportingServices class. Look into injecting an Interface so that you can substitute a mock implementation for testing purposes.
Here's some links to help get you started:
https://romiller.com/2012/02/14/testing-with-a-fake-dbcontext/
https://ardalis.com/new-is-glue

Exception running application when adding insights to application using Application Insights Status Monitor Preview

I'm playing around with the preview, and tried adding insights to a IIS web application deployed locally on my machine. It's a .Net 4.5 application running in a nothing out of the ordinary application pool. When starting the application after adding insights, I get this exception:
Could not load file or assembly 'Microsoft.ApplicationInsights.Extensions.Intercept_x64.dll' or one of its dependencies. The module was expected to contain an assembly manifest.
I tried "Enable 32-Bit Applications" to both true and false with no difference in result.
Has anyone experienced a similar error?
Unfortunately ASP.NET tries to load literally everything that is in \bin as managed assemblies
Microsoft.ApplicationInsights.Extensions.Intercept_x64.dll is not a managed assembly, but ASP.NET Web App should not fail with yellow page in this case, you would see it only in FusLogvw.
Do you use any web publishing?
Did you precompile your web site on publish?
Could you provide full stack trace of the exception?
I've just come across this issue, and after a few hours found it was due to a conflict with FluentSecurity.
It's detailed here: https://github.com/kristofferahl/FluentSecurity/issues/70
The work-around was to add the following lines just before calling SecurityConfigurator.Configure():
SecurityDoctor.Current.EventListenerScannerSetup = scan =>
{
scan.ExcludeAssembly(file => Path.GetFileName(file).Equals("Microsoft.ApplicationInsights.Extensions.Intercept_x64.dll"));
scan.ExcludeAssembly(file => Path.GetFileName(file).Equals("Microsoft.ApplicationInsights.Extensions.Intercept_x86.dll"));
};
Hope this helps somebody else.
My inner exception pointed to WebActivator. So I Uninstall-Package WebActivator -Force, added the appropriate calls in Application_Start and all was good again.
I'm still testing this but I think I've resolved this problem....
The solution is based on the same solution as the SQL Spatial Types native .dll solution; if you know this you'll see the similarity between this and that package.
Step 1
Go Create a new subdirectory in the MVC project and under this two more sub-directories; I used :
MVCRoot ---> ApplicationInsights/x86
---> ApplicationInsights/x64
Under each directory add a linked item from the package directory, this was :
../packages\Microsoft.Diagnostics.Instrumentation.Extensions.Intercept.0.12.0-build02810\lib\native\x64\Microsoft.ApplicationInsights.Extensions.Intercept_x64.dll
and
../packages\Microsoft.Diagnostics.Instrumentation.Extensions.Intercept.0.12.0-build02810\lib\native\x86\Microsoft.ApplicationInsights.Extensions.Intercept_x86.dll
respectively.
I then add this code in a file in the 'root' of the AppplicationInsights folder called loader.cs which looked like this:
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace ApplicationInsights
{
/// <summary>
/// Utility methods related to CLR Types for SQL Server
/// </summary>
internal class Utilities
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
/// <summary>
/// Loads the required native assemblies for the current architecture (x86 or x64)
/// </summary>
/// <param name="rootApplicationPath">
/// Root path of the current application. Use Server.MapPath(".") for ASP.NET applications
/// and AppDomain.CurrentDomain.BaseDirectory for desktop applications.
/// </param>
public static void LoadNativeAssemblies(string rootApplicationPath)
{
var nativeBinaryPath = IntPtr.Size > 4
? Path.Combine(rootApplicationPath, #"ApplicationInsights\x64\")
: Path.Combine(rootApplicationPath, #"ApplicationInsights\x86\");
CheckAddDllPath(nativeBinaryPath);
// LoadNativeAssembly(nativeBinaryPath,
// IntPtr.Size > 4
// ? "Microsoft.ApplicationInsights.Extensions.Intercept_x64.dll"
// : "Microsoft.ApplicationInsights.Extensions.Intercept_x86.dll");
}
public static void CheckAddDllPath(string dllPath)
{
// find path to 'bin' folder
var pathsToAdd = Path.Combine(new string[] { AppDomain.CurrentDomain.BaseDirectory, dllPath });
// get current search path from environment
var path = Environment.GetEnvironmentVariable("PATH") ?? "";
// add 'bin' folder to search path if not already present
if (!path.Split(Path.PathSeparator).Contains(pathsToAdd, StringComparer.CurrentCultureIgnoreCase))
{
path = string.Join(Path.PathSeparator.ToString(), new string[] { path, pathsToAdd });
Environment.SetEnvironmentVariable("PATH", path);
}
}
private static void LoadNativeAssembly(string nativeBinaryPath, string assemblyName)
{
var path = Path.Combine(nativeBinaryPath, assemblyName);
var ptr = LoadLibrary(path);
if (ptr == IntPtr.Zero)
{
throw new Exception(string.Format(
"Error loading {0} (ErrorCode: {1})",
assemblyName,
Marshal.GetLastWin32Error()));
}
}
}
}
I then added this to the global.asax this so:
protected override void Application_Start(object sender, EventArgs e)
{
ApplicationInsights.Utilities.LoadNativeAssemblies(Server.MapPath("~/bin"));
}
So far it seems to be passed events so far as I can tell. All come back and update this should I find a problem with what I've done.
At least the MVC application now starts :-)
UPDATE: This is not the end of the story :-(
I had to also modify the Microsoft.Diagnostics.Instrumentation.Extensions.Intercept.props file which is under the build directory of the package to make it not include the files into the bin directory.
When I was done it looked like this :
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\..\lib\native\x86\Microsoft.ApplicationInsights.Extensions.Intercept_x86.dll">
<CopyToOutputDirectory>None</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)\..\lib\native\x64\Microsoft.ApplicationInsights.Extensions.Intercept_x64.dll">
<CopyToOutputDirectory>None</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
I've had to check in this package into my source control system as I think I'm now going to be faced with the problem with Continuous Build restoring a fresh copy of the package which I don't want.
I can't wait for MS to come up with a proper fix for this.
I've just deleted everything in my /bin folder and it seemed to have resolved the issue. Not sure what happen or anything, it's a project i haven't touched in ages. But it solved it :)

asp.net MVC source files for internationalization

Based on this tutorial I'm trying to have Internationalization capability in my project.
I have these resources files for saving words in different languages:
resources.resx --> for default language (en-US)
resources.fa.resx --> for farsi language
resources.es.resx --> for spanish language
words like fa and es shows the culture.
in views I have replaced words with their equal in resource files in this way :
<a href="#" >#Resources.IranNewsStand</a>
Edit: I've implemented all of the logic based on the tutorial.but I have one view for all of the languages and in this view I'm using resources.resx . Is it a correct logic?
My question is that how my project knows to load which resource file based on the value of Thread.CurrentThread.CurrentCulture ? What did I miss?
Edit: I've implemented these steps:
1-I have a Class Library Project named Resources containing three mentioned resx files(resources.resx,resources.fa.resx,resources.es.resx).
2-Resource project is added in my mvc application as a reference.
3-controllers inherits this Base Controller :
public class BaseController : Controller
{
protected override void ExecuteCore()
{
string cultureName;
HttpCookie cultureCookie = Request.Cookies["_culture"];
if (cultureCookie != null)
cultureName = cultureCookie.Value;
else
cultureName = Request.UserLanguages[0];
cultureName = utilities.CultureHelper.GetValidCulture(cultureName);
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
base.ExecuteCore();
}
}
4-as I said in view I have used resource strings from resource.resx file that contains the default language(en-US) strings:
<p>#Resources.Resources.Home</p>
5-in view I have a link that when clicking on it,this jquery code is run:
<script type="text/javascript">
$("#btnChangeLanguage").click(function () {
$.cookie("_culture", "fa-IR", { expires: 365, path: '/' });
window.location.reload(); // reload
})
</script>
fa-IR is a culture selected by user.but when clicking on this link the language doesn't change.
Edit:Solution
I found 2 problems in my project that solving them made everything ok:
1-jQuery cookie plugin was required to have jquery code work correctely:
<script type="text/javascript" src="~/Scripts/jquery.cookie.js" ></script>
2-the ExecuteCore event in BaseController wouldn't fire and that sounds like it was a problem in asp.net MVC 4 .So based on this question I tryed to override OnActionExecuted instead.
The logic of pulling the resource based on the current culture is built into .NET itself.
There are two steps to this:
The current culture needs to be set appropriately in the request. ASP.NET has some built-in mechanisms to do this, such as what is described in the article you linked to.
Once that is set, the .NET Framework will use the request's current culture to load the appropriate resource. If the requested locale is not available, the culture neutral resource will be loaded (the "fallback").

Resources