Reflection in Portable class library - reflection

I want to dynamically load a dll in a portable class library. A code equivalent to that posted below. "AssemblyName.GetAssemblyName" does not seem to be supported in a portable class library. I need an equivalent code to the below that loads assembly based on name and path. I do not know the name of the assembly at compile time.
public static object GetAssembly(string assemblyPath, string typeName)
{
AssemblyName assamblyName = AssemblyName.GetAssemblyName(assemblyPath);
Assembly assembly = Assembly.Load(assamblyName);
Type type = assembly.GetType(typeName);
object instanceOfMyType = Activator.CreateInstance(type);
return instanceOfMyType;
}

Related

What is the object model for the project.assets.json and what library can we use to parse it from the file?

And I am not talking about Newtonsoft.Json - I want the actual object model that is tailored to project.assets.json
Is there a library for that?
After poking around the dotnet/sdk repository, I eventually found a reference to the NuGet/NuGet.Client repository, which defines a LockFile class that contains many of the same properties as a project.assets.json file.
Turns out this class is published in the NuGet.ProjectModel package. Also turns out this package also includes classes to turn this file into an object model.
Sample code that "serializes" the file contents into an object model:
using NuGet.ProjectModel;
class Program
{
static void Main()
{
string content = System.IO.File.ReadAllText("my-folder/project.assets.json");
var lockFileFormat = new LockFileFormat();
var lockFile = lockFileFormat.Parse(content, "In Memory");
}
}

Getting Information about a assembly which is present in Solution

Scenario: I want to load a assembly at run time which is present in Solution.
The belo code will not work as it is not present in Cuurent App Domain.
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Also if i will search in referenced assemblies, then also it can not be found as it is not referenced. So the below code will also not work:
public static IEnumerable<Assembly> GetAssemblies()
{
var list = new List<string>();
var stack = new Stack<Assembly>();
stack.Push(Assembly.GetEntryAssembly());
do
{
var asm = stack.Pop();
yield return asm;
foreach (var reference in asm.GetReferencedAssemblies())
if (!list.Contains(reference.FullName))
{
stack.Push(Assembly.Load(reference));
list.Add(reference.FullName);
}
}
while (stack.Count > 0);
}
Do you guyz have some suggestion ?
Sign your assembly, then in your app.config, specify the fully qualified assembly name. Call Assembly.Load(string), passing the assembly name from your app.config.
NOTE: The assembly you are attempting to load must be in a location where the runtime can find it. Your best bet is either to put it into the Global Assembly Cache, or make sure it is in the same folder as your executing assembly.

Why would System.Type.GetType("Xyz") return null if typeof(Xyz) exists?

I have come across a strange behaviour in my (huge) .NET 4 project. At some point in the code, I am referring to a fully qualified type, say:
System.Type type = typeof (Foo.Bar.Xyz);
later on, I do this:
System.Type type = System.Type.GetType ("Foo.Bar.Xyz");
and I get back null. I cannot make sense of why this is happening, because my type name is correct, and I have checked with other types and they get resolved properly. Moreover, the following LINQ query finds the type:
var types = from assembly in System.AppDomain.CurrentDomain.GetAssemblies ()
from assemblyType in assembly.GetTypes ()
where assemblyType.FullName == typeName
select assemblyType;
System.Type type = types.FirstOrDefault ();
Are there any reasons why System.Type.GetType could fail?
I have finally had to resort to this piece of code instead of GetType:
System.Type MyGetType(string typeName)
{
System.Type type = System.Type.GetType (typeName);
if (type == null)
{
var types = from assembly in System.AppDomain.CurrentDomain.GetAssemblies ()
from assemblyType in assembly.GetTypes ()
where assemblyType.FullName == typeName
select assemblyType;
type = types.FirstOrDefault ();
}
return type;
}
If you just give a class name (which does need to be fully-qualified in terms of the namespace, of course) Type.GetType(string) will only look in the currently executing assembly and mscorlib. If you want to get types from any other assembly, you need to specify the absolutely full name including the assembly information. As François says, Type.AssemblyQualifiedName is a good way of seeing this. Here's an example:
using System;
using System.Windows.Forms;
class Test
{
static void Main()
{
string name = typeof(Form).AssemblyQualifiedName;
Console.WriteLine(name);
Type type = Type.GetType(name);
Console.WriteLine(type);
}
}
Output:
System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
System.Windows.Forms.Form
Note that if you're using a strongly named assembly (like Form in this case) you must include all the assembly information - versioning, public key token etc.
If you're using a non-strongly-named assembly, it's easier - something like:
Foo.Bar.Baz, MyCompany.MyAssembly
for a type called Baz in namespace Foo.Bar, in assembly MyCompany.MyAssembly. Note the absence of ".dll" at the end - that's part of the filename, but not the assembly name.
You should also be aware of the differences between C# names and CLR names for things like nested classes and generics. For example, typeof(List<>.Enumerator) has a name of System.Collections.Generic.List`1+Enumerator[T]. The generics side is tricky to work out, but the nested type bit is easy - it's just represented with a "+" instead of the "." you'd use in C#.
As far as I know GetType looks for "Xyz" in an assembly named Foo.Bar.dll and I'm assuming it doesn't exist.
GetType relies on your passing the exact path to Xyz in the assembly.
Assembly and namespace don't have to be related.
Try System.Type type = System.Type.GetType(typeof(Foo.Bar.Xyz).AssemblyQualifiedName) and see if that works.
The reason you find it with your LINQ example is that you are using GetAssemblies which obtains the assemblies that have been loaded into the current execution context and thus has the details it needs to find all the types within the assemblies.
From the MSDN documentation (my emphasis):
If typeName includes the namespace but not the assembly name, this method searches only the calling object's assembly and Mscorlib.dll, in that order. If typeName is fully qualified with the partial or complete assembly name, this method searches in the specified assembly. If the assembly has a strong name, a complete assembly name is required.
I just stumbled upon a similar problem and want to leave this here
First of all your can specify the AssemblyName in the string
var type = System.Type.GetType("Foo.Bar.Xyz, Assembly.Name");
However this only works for assemblies without a strong name. The explaination is already in Simons answer If the assembly has a strong name, a complete assembly name is required.
My problem was I had to resolve a System.Dictionary<?,?> from a string at runtime. For a Dictionary<int, string> this might be easy but what about a Dictionary<int, Image>?
this would result in
var typeName = "System.Collections.Generic.Dictionary`2[System.Int32, [System.Drawing.Image, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]]";
But I don't want to write the strong name. Especially because I don't want to include the versions since I am planning to target multiple frameworks with my code.
So here is my solution
privat statice void Main()
{
var typeName = "System.Collections.Generic.Dictionary`2[System.Int32, [System.Drawing.Image, System.Drawing]]";
var type = Type.GetType(typeName, ResolveAssembly, ResolveType);
}
private static Assembly ResolveAssembly(AssemblyName assemblyName)
{
if (assemblyName.Name.Equals(assemblyName.FullName))
return Assembly.LoadWithPartialName(assemblyName.Name);
return Assembly.Load(assemblyName);
}
private static Type ResolveType(Assembly assembly, string typeName, bool ignoreCase)
{
return assembly != null
? assembly.GetType(typeName, false, ignoreCase)
: Type.GetType(typeName, false, ignoreCase);
}
Type.GetType(...) has an overload which acceps a func for assembly and type resolving which in neat. Assembly.LoadWithPartialName is deprecated but if it's dropped in the future I could think of an replacement (iterate all assemblies in the current AppDomain and compare the partial names).

Override a resource from standard assembly in ASP.NET

I want to override a string from a System.ComponentModel.DataAnnotations for an ASP.NET project. Do I need to make a satellite assembly, messing with custom build tasks, al.exe etc.? Even if yes, I couldn't find how to convert .resx to .resources to feed it to al.exe. And if no, where to put the .resx. and how to name it?
UPD: To make it clear: I wanted to use a custom resource string instead of one from the default resource from the assembly. I didn't want to make changes in the every place that uses that string. After all, the resources exist just for overriding them.
Phil Haack has an excellent article Localizing ASP.Net MVC Validation which specifically guides you through overriding your strings. This article applies more to DataAnnotations than it does ASP.net MVC. So, this will help however your are using DataAnnotattions.
Below I have listed the simplest steps to add Localized Resources in Visual Studio.
Open the Project Properties dialog.
Select the Resources tab.
Click to create a new default
resources file.
This will create two files in your Properties folder.
Resources.resx
Resources.Designer.cs
When Resources.resx has
opened, change it's Access Modifier
to Public.
Add your strings.
To add additional resource files for specific cultures you will need to.
Right click your Project in the
Solution Explorer.
Select Add -> New Item -> Resource
File.
Name it Resources.en-us.resx.
(replace 'en-us' with appropriate
code)
Click Add
Drag it into the Properties folder.
Open Resources.en-us.resx and change it's Access Modifier
to Public.
Add your strings.
Repeat for each Culture you need to
support.
During the build VS will convert the .resx files to .resource files and build wrapper classes for you. You can then access via the namespace YourAssembly.Properties.Resources.
With this using statement.
using YourAssembly.Properties;
You can decorate with attributes like this:
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "MyStringName")]
Note: I used the Properties folder for consistency. To use the App_GlobalResources move your .resx files there and change your using statement to match the directory name. Like this:
using YourAssembly.App_GlobalResources;
Edit: The closest that you can get to Strongly Typed resource names would be to do something like this:
public class ResourceNames
{
public const string EmailRequired = "EmailRequired";
}
You can then decorate with attributes like this.
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = ResourceNames.EmailRequired)]
To enable automatic client culture detection add the globalizationsection to the web.config file.
<configuration>
<system.web>
<globalization enableClientBasedCulture="true" culture="auto:en-us" uiCulture="auto:en-us"/>
</system.web>
<configuration>
Here I have enabled a client based culture and set the culture and the uiculture to "auto" with a default of "en-us".
Creating Separate Satellite Assemblies:
The MSDN Creating Satellite Assemblies article will help as well.
If you are new to satellite assemblies make sure you read Packaging and Deploying Resources.
When creating satellite assemblies in the past, I have found it useful to use VS build events. These are the steps I would take.
Create a separate Class Library project in my solution.
Create or Add my .resx files to this project.
Add a Post-Build Event to the Project Properties dialog. (Like the one below)
Sample VS Post-Build Script:
set RESGEN="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\resgen.exe"
set LINKER="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\al.exe"
set ASSEMBLY=$(TargetName)
set SOURCEDIR=$(ProjectDir)
Set OUTDIR=$(TargetDir)
REM Build Default Culture Resources (en)
%RESGEN% %SOURCEDIR%en\%ASSEMBLY%.en.resx %SOURCEDIR%en\%ASSEMBLY%.resources
REM Embed Default Culture
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%%ASSEMBLY%.resources.dll
REM Embed English Culture
IF NOT EXIST %OUTDIR%en\ MKDIR $%OUTDIR%en\
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%en\%ASSEMBLY%.resources.dll
REM These are just a byproduct of using the project build event to run the resource build script
IF EXIST %OUTDIR%%ASSEMBLY%.dll DEL %OUTDIR%%ASSEMBLY%.dll
IF EXIST %OUTDIR%%ASSEMBLY%.pdb DEL %OUTDIR%%ASSEMBLY%.pdb
If you would prefer not to use ResGen.exe to convert your .resx files, you could do something like this.
using System;
using System.Collections;
using System.IO;
using System.Resources;
namespace ResXConverter
{
public class ResxToResource
{
public void Convert(string resxPath, string resourcePath)
{
using (ResXResourceReader resxReader = new ResXResourceReader(resxPath))
using (IResourceWriter resWriter = new ResourceWriter(
new FileStream(resourcePath, FileMode.Create, FileAccess.Write)))
{
foreach (DictionaryEntry entry in resxReader)
{
resWriter.AddResource(entry.Key.ToString(), entry.Value);
}
resWriter.Generate();
resWriter.Close();
}
}
}
}
One of the potential draw backs to doing the conversion this way is the need to reference the System.Windows.Forms.dll. You will still need to use Assembly Linker.
Edit: As wRAR has reminded us if you are signing your assemblies your keys must match.
While this is strange, especially for people familiar with open source localization technologies, one cannot build a satellite assembly for any system assembly or even a 3rd-party signed one:
If your main assembly uses strong
naming, satellite assemblies must be
signed with the same private key as
the main assembly. If the
public/private key pair does not match
between the main and satellite
assemblies, your resources will not be
loaded.
Whether the same is possible automatically, but without a satellite assembly, is unknown, though I doubt that.
If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in DataAnnotations validation messages. This epic hack works for us.
Go to "Microsoft .NET Framework 4.6.1 Language Pack" download page https://www.microsoft.com/en-us/download/details.aspx?id=49977
Select language and download
Extract NDP461-KB3102436-x86-x64-AllOS-{LANG}.exe with 7-Zip
Extract CAB file x64-Windows10.0-KB3102502-x64.cab with 7-Zip
Locate "msil_system.componentmod..notations.resources_...."
... in which you'll find "system.componentmodel.dataannotations.resources.dll"
Open .resources.dll with ILSpy, locate Resources and click Save button above String Table to save as System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.{LANGUAGE}.resources
Add to your project under say a "Resources"
Ensure the files Build Action property of the resources files is set to "Embedded Resource"
Then in a PreStart method of your project you overwrite the System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.resourceMan private static field (told you it was a hack) with the ones you have in your project.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
[assembly: WebActivator.PreApplicationStartMethod(typeof(ResourceManagerUtil), nameof(ResourceManagerUtil.PreStart))]
class ResourceManagerUtil
{
public static void PreStart()
{
initDataAnnotationsResourceManager();
}
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
static void initDataAnnotationsResourceManager()
{
var embeddedResourceNamespace = "<YourProjectDefaultNamespace>.<FolderYouSavedResourcesFilesIn>";
var dataAnnotationsResourcesName = "System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources";
var thisAssembly = typeof(ResourceManagerUtil).Assembly;
var dataAnnotationsAssembly = typeof(System.ComponentModel.DataAnnotations.ValidationAttribute).Assembly;
var resourceManager = new ResourceManager(embeddedResourceNamespace + "." + dataAnnotationsResourcesName, thisAssembly);
// Set internal field `DataAnnotationsResources.resourceMan`
var dataAnnotationsResourcesType = dataAnnotationsAssembly.GetType(dataAnnotationsResourcesName);
var resmanProp = dataAnnotationsResourcesType.GetField("resourceMan", BindingFlags.NonPublic | BindingFlags.Static);
resmanProp.SetValue(null, resourceManager);
}
}
Assuming that you want to override the default error message strings in the validation attributes, you can do that by setting the ErrorMessageResourceName and the ErrorMessageResourceType properties like this:
[Required(ErrorMessageResourceName = "Required_Username", ErrorMessageResourceType = typeof(MyResourceFile)]
public string Username { get; set; }
You can create a resource file called MyResourceFile.resx that contains Required_Username with the error message you want.
Hope this helps.
I want to provide an answer with the same idea as by Duncan Smart, but for .NET Core 2.2 instead of .NET Framework 4.x.
Here it is.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
public static class ResourceManagerHack
{
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
public static void OverrideComponentModelAnnotationsResourceManager()
{
EnsureAssemblyIsLoaded();
FieldInfo resourceManagerFieldInfo = GetResourceManagerFieldInfo();
ResourceManager resourceManager = GetNewResourceManager();
resourceManagerFieldInfo.SetValue(null, resourceManager);
}
private static FieldInfo GetResourceManagerFieldInfo()
{
var srAssembly = AppDomain.CurrentDomain
.GetAssemblies()
.First(assembly => assembly.FullName.StartsWith("System.ComponentModel.Annotations,"));
var srType = srAssembly.GetType("System.SR");
return srType.GetField("s_resourceManager", BindingFlags.Static | BindingFlags.NonPublic);
}
internal static ResourceManager GetNewResourceManager()
{
return new ResourceManager($"{typeof(<YourResource>).Namespace}.Strings", typeof(<YourResource>).Assembly);
}
private static void EnsureAssemblyIsLoaded()
{
var _ = typeof(System.ComponentModel.DataAnnotations.RequiredAttribute);
}
}
And I call this like so:
public static void Main(string[] args)
{
ResourceManagerHack.OverrideComponentModelAnnotationsResourceManager();
CreateWebHostBuilder(args).Build().Run();
}
Furthermore, I created a ~/Resources/<YourResource>.resx file and populated it with the default values and changed them at will. Lastly I created a public empty class <YourResource>.

How do I create an instance of a class in an ASP.NET application

How do you go about creating an instance of an object when given the class name as a string in an ASP.NET v2 application? For example, I've got a class called SystemLog defined in the app_code section of the application. The class is defined within the Reports namespace. To create an instance of the object, I do something like this:
Dim MyObject As New Global.Reports.SystemLog
However, I want to create this object using a string to define the type. The type name is stored in a SQL database as a string. I thinks it's probably something to do with Activator.CreateInstance(AssemblyName, TypeName) but what I don't know is what to pass in those strings. What is the assembly name of an ASP.NET web app?
Help!
Thanks, Rob.
PS. I don't want a hard coded Select statement :-)
string typeName = "Your Type Name Here";
Type t = Type.GetType(typeName);
object o = Activator.CreateInstance(t);
This will give you an instanciated type. If will be up to you to cast it to the correct type and call your appropriate methods.
If you need to create a type that doesn't have a parameterless constructor there is an overload on CreateInstance that takes a params of objects to pass to a constructor. More info at this MSDN article.
The following is able to create type, even if it's from another assembly:
public object CreateInstance(string typeName) {
var type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.FirstOrDefault(t => t.FullName == typeName);
return type.CreateInstance();
}
You can use this to get it from a particular assembly:
Assembly assembly = Assembly.Load("myAssembly");
Type ObjectType = assembly.GetType("Type name here");
then.....object o = Activator.CreateInstance(ObjectType);

Resources