i created a dll contains a class named PersonVM like what you see below. and its working ...
public ActionResult Index()
{
using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
{
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "Per.dll";
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, "public class PersonVM{ " + "public int id{get;set;}" +
"public string Name{get;set;}" + "public string LName{get;set;}" + " }");
}
Assembly assembly = Assembly.LoadFrom("Per.dll");
var type = assembly.GetType("PersonVM");
var d = type.GetProperties();
object obj = Activator.CreateInstance(type, true);
return View(obj);
}
but this code is working just one time in my index controller.
for example its not changing my dll class in here:
public ActionResult Conf()
{
using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
{
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "Per.dll";
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, "public class PersonVM{ " + "public int id{get;set;}" +
"public string Name{get;set;}" + "public string LName{get;set;}" + "public string LNamee2 { get; set; }" + "public string L4 { get; set; }" + " }");
}
Assembly assembly = Assembly.LoadFrom("Per.dll");
var type = assembly.GetType("PersonVM");
object obj = Activator.CreateInstance(type, true);
List<ClassInfoVM> model = obj.GetType().GetProperties()
.Select(T => new ClassInfoVM()
{
PropName = T.Name,
TypeOfProp = T.PropertyType.Name
}).ToList();
return View(model);
}
there is no thing about any error.. it just doesn't changing my dll class...the dll class PersonVM is just contains the properties which i was set it, first time in Index
You can't load the same named DLL in a app domain twice using Assembly.LoadFrom.
See the Remarks section of the Assembly.LoadFrom function on the MSDN:
The LoadFrom method has the following disadvantages. Consider using
Load instead.
If an assembly with the same identity is already loaded, LoadFrom returns the loaded assembly even if a different path was specified.
One possible solution is let CSharpCodeProvider generate a random name for the assembly and load that, however if I where you I would seriously consider if you really need those classes to be built at runtime. Just build them at design time and give them two different names. Perhaps even make the version conf Conf dervive from the version in Index
Related
I create a Servicebus-Namespace using AzureNative on Pulumi:
public void CreateNamespace(string namespaceName, SkuName skuname, SkuTier tier)
{
var namespace = new Namespace(namespaceName, new NamespaceArgs
{
Location = _resourceGroup.Location,
NamespaceName = namespaceName,
ResourceGroupName = _resourceGroup.Name,
Sku = new Pulumi.AzureNative.ServiceBus.Inputs.SBSkuArgs
{
Name = skuname,
Tier = tier
}
});
}
The Servicebus Namespace is created correctly. After creating the Servicebus-Namespace I need to retrieve the ConnectionString for this resource. Either for the automatically created RootManageSharedAccessKey or alternatively by creating a specific additional policy for that task.
Within the Azure Portal I can retrieve the Key by navigating through
Settings/Shared access policies/Policy/ and copying the Primary access key from there.
I did not find any property or function within the AzureNative.ServiceBus - Namespace that seem to lead to that key. Any way to retrieve that property?
I solved it by creating a new NamespaceRule and return ListNamespaceKeys-Properties:
var namespaceRule = new NamespaceAuthorizationRule(rulename, new NamespaceAuthorizationRuleArgs
{
AuthorizationRuleName = rulename,
NamespaceName = namespace.Name,
ResourceGroupName = _resourceGroup.Name,
Rights = new[]
{
AccessRights.Listen,
AccessRights.Send
}
});
var nameSpaceKeys = Output
.Tuple(namespace.Name, namespaceRule.Name)
.Apply(t => ListNamespaceKeys.InvokeAsync(new ListNamespaceKeysArgs
{
NamespaceName = t.Item1,
AuthorizationRuleName = t.Item2,
ResourceGroupName = _resourceGroup.GetResourceName()
}));
Now NamespaceKeys contains all the required Properties like PrimaryConnectionString etc.
I am working on an application that should compile and debug C# code on the fly.
A simplified version of the code is included below.
What should be changed in this code to run the generated method step by step and get the state of the variables x and y after each step?
If everything should be changed that is okay, I am happy with any constructive response.
EDIT: to clarify: what I want to do is have my code debug the code that is generated with reflection, not the debug function in Visual Studio.
string code =
#"
namespace MyNameSpace
{
public class MyClass
{
public static int MyMethod()
{
var x = 3;
var y = 4;
return x * y;
}
}
}";
string namespaceName = "MyNameSpace";
string className = "MyClass";
string methodName = "MyMethod";
string language = "csharp";
string classFullname = namespaceName + "." + className;
CodeDomProvider provider = CodeDomProvider.CreateProvider(language);
CompilerParameters parameters = new CompilerParameters();
CompilerResults results;
parameters.OutputAssembly = "Compiler";
parameters.CompilerOptions = "/t:library";
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
parameters.IncludeDebugInformation = true;
results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count != 0)
{
throw new Exception("Code compilation errors occurred.");
}
var instance = results.CompiledAssembly.CreateInstance(classFullname, false);
// TODO run the method step by step and get the state after each step
This configuration may help you:
parameters.GenerateInMemory = false; //default
parameters.TempFiles = new
TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true);
parameters.IncludeDebugInformation = true;
parameters.TempFiles.KeepFiles = true
To debug the generated code you will need the pdb files. To have those while debugging your application, just have to tell the compiler where to save the temporary files. For this you can just add the following line to your parameters:
parameters.TempFiles = new TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true);
You can then step into the Invokation of your targeted method. The Code could look like this:
var method = instance?.GetType().GetMethod(methodName);
method?.Invoke(instance, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture);
If you want the Debugger to automatically stop, when entering your "MyMethod", you can modify your string likes this:
string code =
#"using System.Diagnostics;
namespace MyNameSpace
{
public class MyClass
{
public int MyMethod()
{
Debugger.Break();
var x = 3;
var y = 4;
return x * y;
}
}
}";
Elchido pointed out in the comments that maybe I should look for an interpreter. After a bit of searching I came across CSI: A Simple C# Interpreter.
https://www.codeproject.com/Articles/10212/CSI-A-Simple-C-Interpreter
After investigating, my conclusion is that it is possible to use either and interpreter or the Codedom compiler to create debugger-like functionality, but it takes a significant effort.
The solution that I am working on involves splitting the code into separate statements and put all variables in an array.
The 'MyMethod' function is split into parts:
public static object Line1()
{
return 3;
}
public static object Line2()
{
return 4;
}
public static object Line3(object x, object y)
{
return x*y;
}
After compiling the code using Codedom compiler, I can do the following:
Dictionary<string, object> vars = new Dictionary<string, object>();
List<MethodInfo> lines = new List<MethodInfo>();
lines.Add(type.GetMethod("Line1"));
lines.Add(type.GetMethod("Line2"));
lines.Add(type.GetMethod("Line3"));
vars["x"] = lines[0].Invoke(instance, new object[] { });
vars["y"] = lines[1].Invoke(instance, new object[] { });
vars["#return"] = lines[2].Invoke(instance, new object[] { vars["x"], vars["y"] });
Note that this is not a working solution yet, a lot of work still has to be done to convert the 'MyMethod code' into separate lines and extract the variables. I will post an update when I have more/better code.
Click just left side of your code it will mark red dot that is called break point.After that when your code execute at the point it will break at the point and you can debug step by step bt pressing F10 key.
I'm currently implementing an elementary solution to load the Services in an Asp.Net Core by reflection instead of having to pass every single type.
To have some wiggle-room, I created a static helper returning me the assembly-types using the new core-reflection types:
internal static class ReflectionTypeHelper
{
private static readonly Assembly _currentAssembly = typeof(ServiceContainerInitializer).GetTypeInfo().Assembly;
internal static IReadOnlyCollection<Type> ScanAssembliesForTypes(Func<Type, bool> predicate)
{
var result = new List<Type>();
var appAssemblies = GetApplicationAssemblies();
foreach (var ass in appAssemblies)
{
var typesFromAssembly = ass.GetTypes().Where(predicate);
result.AddRange(typesFromAssembly);
}
return result;
}
private static IEnumerable<Assembly> GetApplicationAssemblies()
{
var consideredFileExtensions = new[]
{
".dll",
".exe"
};
var result = new List<Assembly>();
var namespaceStartingPart = GetNamespaceStartingPart();
var assemblyPath = GetPath();
IEnumerable<string> assemblyFiles = Directory.GetFiles(assemblyPath);
var fileInfos = assemblyFiles.Select(f => new FileInfo(f));
fileInfos = fileInfos.Where(f => f.Name.StartsWith(namespaceStartingPart) && consideredFileExtensions.Contains(f.Extension.ToLower()));
// Net.Core can't load the Services for some reason, so we exclude it at the moment
//fileInfos = fileInfos.Where(f => f.Name.IndexOf("Services", StringComparison.OrdinalIgnoreCase) == -1);
foreach (var fi in fileInfos)
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(fi.FullName);
result.Add(assembly);
}
return result;
}
private static string GetNamespaceStartingPart()
{
var fullNamespace = _currentAssembly.FullName;
var splittedNamespace = fullNamespace.Split('.');
var result = string.Concat(splittedNamespace[0], ".", splittedNamespace[1]);
return result;
}
private static string GetPath()
{
var codeBase = _currentAssembly.CodeBase;
var uri = new UriBuilder(codeBase);
var result = Uri.UnescapeDataString(uri.Path);
result = Path.GetDirectoryName(result);
return result;
}
}
As you can probably see in the code-comment, I can't load the "Services"-Assembly, which I creted from the "ASP.NET Core Web Application (.Net Core)" - Project template.
Unfortunately, the exception is quite generic
Could not load file or assembly 'Argusnet.Pis.Services,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Also, the file is there as expected.
I did find some hints on GitHub-Issues regarding this topic, but they're all solved in the Release Candidates.
Interesting enough, all other assemblies work as you'd expect, so there has to be something specific regarding this assembly-types?
Edit: Screenshot of the exception:
One of the reason why it's failing to load might be the mismatch in target processor architecture selected during compiling. Argusnet.Pis.Services might be compiled with x86 configuration and the client application trying to load might be built using the x64 option during compiling Or the other way around. Make sure both projects have the same option(x86 or x64) set before building them. Otherwise try building them with Any CPU option.
In some cases in our project we have to ignore current culture of application
and get resources in English.
Basically, we do it like for all languages
<label>#ModelEntities.Properties.Resource.Gender</label>
How is it possible to get in English only?
I assume we have somehow created some reference to Resource.resx ?
I have used
ResourceManager rm = new ResourceManager("Resource.Strings",
Assembly.GetAssembly(typeof(ModelEntities.Properties.Resource)));
var s = rm.GetString("Age");
But it seems like not working.
Thank you!
I found the working solution.
First of all our resource files are ending like
Resource.resx
and not like Resources.resx
And in second we have resources in a separate assembly: ModelEntities
So the working code is following
var resourceManager = new ResourceManager(typeof(ModelEntities.Properties.Resource));
var genderString = resourceManager.GetString("Gender", ci);
You can use the resource manager class click here for more info about the class
System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("en-GB");
ResourceManager resourceManager = new ResourceManager("YourResource", Assembly.GetExecutingAssembly());
string bodyResource = resourceManager.GetString("yourText", ci);
A simple way:
YorResourceName.ResourceManager.GetString("ResourceKeyName", System.Globalization.CultureInfo.GetCultureInfo("en-US"))
Folder structure:
MyApplicationSolution
/Resources
/Resource.resx
public static string GetLocalisedRes(string resourceName = "", string resourceNameKey = "")
{
string translate = string.Empty;
string baseName = "YourNameSpace.Resources." + resourceName + "";
try
{
Type resType = Type.GetType(baseName);
ResourceManager rm = new ResourceManager(baseName, resType.Assembly);
translate = rm.GetString(resourceNameKey);
}
catch {
translate = string.Empty;
}
return translate;
}
Usage
var age = GetLocalisedRes("Resource", "Age");
This working for me
I have program with all DLLs hidden inside of one executable file. I am developing plugin support for this program.
All DLL in exe are stored in as Embedded Resources. Following code is used to access these embeded resources:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resourceName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(x => x.EndsWith(new AssemblyName(args.Name).Name + ".dll"));
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream == null)
return null;
var assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Plugins are loaded in separate domain for possibility to unload them:
public static T CreateInstanceInNewAppDomain<T>(bool shadowCopy = true)
{
var cdSetupInf = AppDomain.CurrentDomain.SetupInformation;
var separateDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null,
new AppDomainSetup()
{
LoaderOptimization = LoaderOptimization.MultiDomain,
ApplicationBase = cdSetupInf.ApplicationBase,
PrivateBinPath = cdSetupInf.PrivateBinPath,
ShadowCopyFiles = shadowCopy ? "true" : "false"
});
var type = typeof(T);
return (T)separateDomain.CreateInstanceFromAndUnwrap(type.Assembly.Location, type.FullName);
}
The most important part of previous code is call „CreateInstanceFromAndUnwrap“ for creation object on separate domain. This part of code would work well if DLL containing type would be on HDD. Since it is loaded from Embedded Resources the assemblyFile parameter (represented as type.Assembly.Location) is empty. Therefore I am not able to create instance of my IPluginAccessor which I am using for accessing functionality in plugins. Is there any way how can create instance of type in separate domain without need to specify assemblyName? It would be great if there would be something like AppDomain.CreateInstanceFromAndUnwrap(Type type). If I looked well, there is only Activator.CreateInstance(Type type) but this works for current domain and not one I specify.
Thanks a lot
I think you can use Callback which will be executed in the new AppDomain.
Define an helper class:
class AnotherAppDomainPluginBuilder<T> : MarshalByRefObject
where T : new()
{
T Result { get; set; }
public void Create()
{
LoadRelevantDlls();
this.Result = new T();
}
void LoadRelevantDlls()
{
// load whatever DLLs in whatever way you would like to
}
}
Then instead of:
return (T)separateDomain.CreateInstanceFromAndUnwrap(type.Assembly.Location, type.FullName);
Write:
AnotherAppDomainPluginBuilder<T> builder =
(T)separateDomain.CreateInstanceFromAndUnwrap(
Assembly.CurrentAssembly,
typeof(AnotherAppDomainPluginBuilder<T> ));
separateDomain.DoCallback(builder.Create);
return builder.Result;