TagHelpers not working when assembly is dynamically loaded in ASP.NET core 2.1 - razor

I'm using ASP.NET core 2.1. I load all the assemblies that have the Views dynamically from a plugins folder. I use the following code for that. The Views get loaded correctly.
services.AddMvc().
AddRazorPagesOptions(o => o.AllowAreas = true).
SetCompatibilityVersion(CompatibilityVersion.Version_2_1).
ConfigureApplicationPartManager(ConfigureApplicationParts);
private void ConfigureApplicationParts(ApplicationPartManager apm)
{
var pluginsPath = Path.Combine(_env.WebRootPath, "Plugins");
var assemblyFiles = Directory.GetFiles(pluginsPath, "*.dll", SearchOption.AllDirectories);
foreach (var assemblyFile in assemblyFiles)
{
var assembly = Assembly.LoadFile(assemblyFile);
if (assemblyFile.EndsWith(".Views.dll"))
{
apm.ApplicationParts.Add(new CompiledRazorAssemblyPart(assembly));
}
else
{
apm.ApplicationParts.Add(new AssemblyPart(assembly));
}
}
}
The views have some custom taghelpers.
The _ViewImports.cshtml file looks like
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#addTagHelper *, MyTagHelpers
The problem is that the custom tag helpers don't get loaded and gives an error:
Error: Could not load file or assembly MyTagHelpers
The reason I get the error may be the Razor View Engine may be looking for the DLL in the bin folder of the main app and it can't find the DLL and gives this error.
What should I do in the startup to say the taghelpers are available in a DLL and can be loaded from there? Should I use TagHelperFeatureProvider to do it?
UPDATE: I moved the tag helpers to a separate DLL called MyTagHelpers.Common and dropped in the plugins folder. I'm not getting any assembly not found error anymore, but the tag helpers are not working.

After 2 days trying to resolve this - please note - the 'assembly name' is the compiled (assembled?) .DLL name which would normally match the project name which may not match the namespace name/prefix!
So if your project name is not the same as the namespace as mine was, then the #addTagHelper reference is the project name which is being used to create the compiled .DLL - see your build output to check.
And therefore, this is also usually the same as the prefix for your .csproj file which is why the official documentation says to create a new app.

Related

How to use localization in Razor Class Library in Asp.Net Core

I have tried to create the Razor Class Library with Asp.Net Core in following project structure:
I have used in my web application these settings for localization in Startup class:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddViewLocalization(
LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = "Resources"; })
.AddDataAnnotationsLocalization();
services.Configure<RequestLocalizationOptions>(
opts =>
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("en")
};
opts.DefaultRequestCulture = new RequestCulture("en");
opts.SupportedCultures = supportedCultures;
opts.SupportedUICultures = supportedCultures;
});
....
var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
In Index.cshtml:
#using Microsoft.AspNetCore.Mvc.Localization
#inject IViewLocalizer Localizer
<h1>#Localizer["Title"]</h1>
Unfortunately, the result is only string "Title". I can't load these resx files from Razor Class Library.
How can I use the localization in Razor Class Library like above?
UPDATE: This is very similiar use case - https://github.com/aspnet/Localization/issues/328 - that provides some example.
I haven't tried the accepted answer and based on the comments, it seems the OP didn't get it to work. I implemented a pattern similar to the View/Page locator pattern that MVC/Razor Pages uses namely, that resources can be provided in a RCL or separate assembly and use ViewLocalizer and it'll just find the matching resource string from the highest precedence resource. You can read my implementation and see if it might work for you.
https://terryaney.wordpress.com/2021/01/04/migrating-to-net-core-overridable-localization-in-razor-class-libraries/
You appear to have forgotten to configure localization correctly using AddLocalization
Using details provided from documentation
Reference Globalization and localization in ASP.NET Core
Configure localization
Localization is configured in the ConfigureServices method:
services.AddLocalization(options => options.ResourcesPath = "Resources"); //<<< This is required
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
AddLocalization Adds the localization services to the services container. The code above also sets the resources path to "Resources".
AddViewLocalization Adds support for localized view files.
AddDataAnnotationsLocalization Adds support for localized DataAnnotations validation messages through IStringLocalizer abstractions.
Localization middleware
The current culture on a request is set in the localization Middleware. The localization middleware is enabled in the Configure method. The localization middleware must be configured before any middleware which might check the request culture (for example, app.UseMvcWithDefaultRoute()).
var supportedCultures = new[] {
new CultureInfo("en-US"),
new CultureInfo("en")
};
app.UseRequestLocalization(new RequestLocalizationOptions{
DefaultRequestCulture = new RequestCulture("en"),
// Formatting numbers, dates, etc.
SupportedCultures = supportedCultures,
// UI strings that we have localized.
SupportedUICultures = supportedCultures;
});
//...other middleware
app.UseMvcWithDefaultRoute();
The path to the resource file shown in the example image follows the path naming convention given that
you are using the ResourcesPath option which was set to "Resources". This should allow the view to find the resource file in the relative path to the "Resources" folder.
An alternative is to not use the ResourcesPath option, and place the .resx file in the same folder as the view, following the naming convention of course.
Base on additional details provided it was indicated that the UI project would be packaged as a nuget package.
Then have the resources files packaged into the nuget package and have them unpacked to the resources folder of the target project when when installed.
The resources need to be in the site root to be available to the view, so you then need to reference all the files in your .nuspec:
<?xml version="1.0"?>
<package>
<metadata>...
</metadata>
<files>
<!-- Add all resource files -->
<file src="Resources\**\*.resx" target="content\Resources" />
</files>
</package>
Reference Creating NuGet packages

Cakephp 3: React/zmq library namespace

I am working on on the basic tutorial on using ratchet mentioned here http://socketo.me/docs/push.
I have created a test setup for the tutorial that works flawlessly. However, when I am trying to integrate the setup with CakePHP 3 I am running into problems. The ratchet and ZMQ servers are independent just the way mentioned in the tutorial. Only the following piece of code needs to move into CakePHP 3 controllers:
$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($entryData));
This code basically submits new data to ZMQ queue for forwarding to ratchet. If I place the same 4 lines in a plain PHP file outside CakePHP 3 codebase it works. When I place the same four lines inside APP\Controller\SamplesController it says the class APP\Controller\ZMQContext not found.
CakePHP 3 docs mention that vendor libraries installed via composer will be automatically available through autoloader. I have installed React\ZMQ library via following command:
php composer require react/zmq
I have tried accessing the class via following namespaces but none of them have worked:
ZMQContext ( Class 'App\Controller\ZMQContext' not found )
\ZMQContext ( Class 'App\Controller\ZMQ' not found )
React\ZMQ\ZMQContext ( Class 'App\Controller\React\ZMQ\ZMQContext' not found )
\React\ZMQ\ZMQContext ( Class 'React\ZMQ\ZMQContext' not found )
Probably missing out on some namespace concept in PHP but my understanding is that if ZMQContext is available in a normal PHP file through global namespace, then it should also be available within CakePHP 3 via \ZMQContext.
I have following questions:
How can I push data to ZMQ Queue within my CakePHP 3 APP ?
APP::path() & APP::classname() seems to work only for classes within the CakePHP 3 application. How to check path for a particular vendor library class ?
How to autoload vendor library classes correctly ? (I do not wish to require/require_once them as it will needed to be done for each controller that wants to publish data via ratchet)
Is the assumption about accessing global namespace via \CLASSNAME wrong ?
My second attempt at accessing vendor library class at \ZMQContext resolved to App\Controller\ZMQ. How is that possible when it should have attempted within root namespace ?
ZMQContext is not part of react/zmq library so does it mean it part of default php bindings for libzmq ?
This was a simple typo problem:
$context = new \ZMQContext();
$socket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($entryData));
The second namespace specification in second line was missing.

Design time instantiation issues when accessing xml file using XDocument.Load

In my windows store app using the Visual Studio 2012 designer I want to be able to load some model objects for the designer. I've done this plenty of times before where I supply a xaml file using the ms-appx:/// uri without error. However, for this project I need to be able to instantiate a class and have it convert raw xml of a different format into my model objects.
I'm using the following xaml to instantiate my class for the designer:
d:DataContext="{Binding Source={d:DesignInstance Type=model:Walkthroughs, IsDesignTimeCreatable=True}}"
In my Walkthroughs class had code that did this initially:
public Walkthroughs()
{
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
AppDataLoader.LoadWalkthroughs(this, XDocument.Load("ms-appx:///SampleData/walkthroughs.xml"));
}
I first ran into an issue where the XDocument.Load did not understand the ms-appx:/// uri so I modified my code to something very simplistic:
AppDataLoader.LoadWalkthroughs(this, XDocument.Load(#"C:\walkthroughs.xml"));
Now I get access to path '' is denied.
I've tried several directories as well to no avail. I'm even running Visual Studio as an Administrator. If I remove the prefix altogether I get the following error:
Could not find file 'C:\Users\{me}\AppData\Local\Microsoft\VisualStudio\11.0\Designer\ShadowCache\omxyijbu.m4y\yofsmg1x.avh\walkthroughs.xml'.
Has anyone been able to load files from the file system when the designer instantiates objects?
Thanks,
-jeff
XDocument.Load(string uri) seems to have problems with loading Project resources from ms-appx:/
Regarding your second approach: Direct access to "C:" is not permitted. Ther is only a handful of special folders that you can access. Check out my workaround for this (my xml file is within the Assets folder of my project:
var storageFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
storageFolder = await storageFolder.GetFolderAsync("Assets");
var xmlFile = await storageFolder.GetFileAsync("data.xml");
var stream = await xmlFile.OpenReadAsync();
var rdr = new StreamReader(stream.AsStream(), System.Text.Encoding.GetEncoding("ISO-8859-1")); //needed if you have "ä,ß..." in your xml file
var doc = XDocument.Load(rdr);

Monodroid: Where should I put configuration settings?

From Miguel de Icaza:
We use a library profile that is better suited for mobile devices, so we removed features that are not necessary (like the entire System.Configuration stack, just like Silverlight does).
After years of .NET development, I'm accustomed to storing configuration settings in web.config and app.config files.
When using Mono for Android, where should I put my configuration settings?
If it matters, I'd like to store different configuration settings for different build configurations as well.
I would probably recommend using shared preferences and compilation symbols to manage different configurations. Below is an example of how you can use a preferences file to add or change keys based on the compilation symbols. Additionally, you could create a separate preferences file that is only available for a particular configuration. Because these keys are not available on all configurations, make sure to always perform checks for them before using.
var prefs = this.GetSharedPreferences("Config File Name", FileCreationMode.Private);
var editor = prefs.Edit();
#if MonoRelease
editor.PutString("MyKey", "My Release Value");
editor.PutString("ReleaseKey", "My Release Value");
#else
editor.PutString("MyKey", "My Debug Value");
editor.PutString("DebugKey", "My Debug Value");
#endif
editor.PutString("CommonKey", "Common Value");
editor.Commit();
We have had exactly the same problem in our current project.
My first impulse was to put the configuration in a sqlite key-value table but then my internal customer reminded me the main reason for a configuration file - it should support simple editing.
So instead we created an XML file and put it there:
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
And access it using these properties:
public string this[string key]
{
get
{
var document = XDocument.Load(ConfigurationFilePath);
var values = from n in document.Root.Elements()
where n.Name == key
select n.Value;
if(values.Any())
{
return values.First();
}
return null;
}
set
{
var document = XDocument.Load(ConfigurationFilePath);
var values = from n in document.Root.Elements()
where n.Name == key
select n;
if(values.Any())
{
values.First().Value = value;
}
else
{
document.Root.Add(new XElement(key, value));
}
document.Save(ConfigurationFilePath);
}
}
}
via a singleton class we call Configuration so for .NET developers it is very similar to using the app.config files. Might not be the most efficient solution but it gets the job done.
there's a Xamarin centric AppSetting reader: https://www.nuget.org/packages/PCLAppConfig
pretty useful for continuous delivery (so a deployment server such as octopus allows to alter your config file for each environment with values stored on the cd server)
there's a Xamarin centric AppSetting reader available at https://www.nuget.org/packages/PCLAppConfig
it is pretty useful for continuous delivery;
use as per below:
1) Add the nuget package reference to your pcl and platforms projects.
2) Add a app.config file on your PCL project, then as a linked file on all your platform projects. For android, make sure to set the build action to 'AndroidAsset', for UWP set the build action to 'Content'. Add you settings keys/values: <add key="config.text" value="hello from app.settings!" />
3) Initialize the ConfigurationManager.AppSettings on each of your platform project, just after the 'Xamarin.Forms.Forms.Init' statement, that's on AppDelegate in iOS, MainActivity.cs in Android, App in UWP/Windows 8.1/WP 8.1:
ConfigurationManager.Initialise(PCLAppConfig.FileSystemStream.PortableStream.Current);
3) Read your settings : ConfigurationManager.AppSettings["config.text"];
ITNOA
Maybe PCLAppConfig is help you to create and read from app.config in Xamarin.Forms PCL Project or other Xamarin projects.
For different configuration in different build mode such as release and debug you can use Configuration Transform on app.config.

Referencing and using JScript.NET "functions only" exe assembly

1. Compiled Assembly from JSC
I've compiled what is intended to be client-side JavaScript using the JScript compiler (jsc.exe) on the server side in an attempt to make something that can be tested from a unit testing project, and maybe even something that can be debugged on the server side.
The compiled file contains only functions as follows (just for example) and it compiles fine into BitField.exe. Notice, no wrapper class or package in the source code.
------ BEGIN FILE (BitField.js) -------
function BitField(){
this.values = [];
}
// more functions ...
------- END FILE -------
jsc /fast- /out:BitField.exe Bitfield.js
Results in a BitField.exe assembly.
Success! Well, kind of ....
2. Testing Assembly / Access Point?
Secondly I've created a test project (in C#) and referenced in the BitField.exe assembly successfully. (The type of project is irrelevant but I'm providing more description to paint a full picture.)
The problem seems to be: I cannot find the namespace or a point at which I can access the BitField functions inside the BitField.exe assembly from my C# test project. The assembly doesn't seem to be a "normal".
In other words I need in C#
using ???WHAT???
Note: I don't want to use JScript "extensions", meaning keywords that won't run client-side (in a web browser), for example, class, package etc because I want the code to be clean as possible for copy & paste back into client side script environment (Regardless said "clean" code compiles fine by jsc.exe without use of those extensions). When I try to wrap the functions in package and class it starts producing compile errors so that's another reason not to use them - because they appear to make me alter my code.
Any suggestions as to how I can use the functions of the compiled JScript assembly (by having it referenced into another assembly) when there are no explicit containers in it?
Update / Proof
.NET Reflector view
After playing around with it for a while, and trying various combinations of command-line switches for jsc.exe, I'm pretty sure that what you're trying to do won't work as you'd wish it to. If you try to compile a js file that contains functions into a .Net library assembly, you get an error:
BitField.js(1,1) : error JS1234: Only type and package definitions are allowed inside a library
But, there is hope, yet! Here's what I would do...
I would keep your "clean" BitField.js file just as it is, and then create a batch file that wraps it in a JScript class and writes it out to a "dirty" js file. It's pretty clean if you think of it as part of the compilation of the code into the DLL. The code to wrap the BitField.js into BitFieldClass.js would look like this:
merge-into-class.js
var fso = new ActiveXObject("Scripting.FileSystemObject");
var ForReading = 1;
var inputFile = fso.OpenTextFile("BitField.js",ForReading, false);
var outputFile = fso.CreateTextFile("BitFieldClass.js", true);
outputFile.write("class BitFieldClass{\n");
while (!inputFile.AtEndOfStream)
{
var textLine = inputFile.ReadLine();
outputFile.write (textLine + "\n");
}
outputFile.write("}");
outputFile.close();
Then the batch file to wrap it and compile it is really simple:
compile-js.bat
cscript merge-into-class.js
jsc /t:library /out:BitFieldClass.dll bitFieldClass.js
Of course, if you wanted to do multiple files, you'd have to parameterize things a bit, but hopefully this is enough to demonstrate the idea.