The following program works only with the classes which are located within the main project. Would you be so kind to advise or correct the code so that it becomes possible to use the classes from other projects. I added reference of the ClassLibrary1 project to the Example01 project.
The error which I am getting is
Unhandled Exception: System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at Example01.Program.InstantiateObject(String assemblyName, String fullName) in c:\Projects\Example01\Example01\Program.cs:line 59
object obj = Activator.CreateInstance(objectToInstantiate);
at Example01.Program.RandomizeList[TClass](Int32 count, String assemblyName) in c:\Projects\Example01\Example01\Program.cs:line 34
randomizedList.Add(
at Example01.Program.Main(String[] args) in c:\Projects\Example01\Example01\Program.cs:line 18
List<Class02> randomizedList03 = RandomizeList<Class02>();
Here is my code with the reference what works and what fails:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ClassLibrary1.Classes;
namespace Example01
{
class Program
{
static void Main(string[] args)
{
// This works
List<Class01> randomizedList01 = RandomizeList<Class01>();
// This works
List<Test01> randomizedList02 = RandomizeList<Test01>();
// This fails
List<Class02> randomizedList03 = RandomizeList<Class02>();
Console.ReadKey();
}
private static List<TClass> RandomizeList<TClass>(int count = 10, string assemblyName = "")
{
if (assemblyName.Length == 0)
assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
var listOfSubClasses = SubClasses<TClass>();
var randomizedList = new List<TClass>();
var rand = new Random();
count = 10;
for (int i = 0; i < count; i++)
randomizedList.Add(
(TClass)
InstantiateObject(assemblyName,
listOfSubClasses.ElementAt(rand.Next(listOfSubClasses.Count()))
.FullName));
return new List<TClass>(randomizedList as IEnumerable<TClass>);
}
// Enumerate all subclasses for the specified class
public static IEnumerable<Type> SubClasses<TClass>()
{
var subclasses =
(from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where type.IsSubclassOf(typeof(TClass))
select type).ToList();
return subclasses;
}
private static object InstantiateObject(string assemblyName, string fullName)
{
Assembly аsm = Assembly.Load(assemblyName);
// Get the assembly metadata
Type objectToInstantiate = аsm.GetType(fullName);
// Create object on the fly
object obj = Activator.CreateInstance(objectToInstantiate);
return obj;
}
}
#region Sample Classes
public class Class01
{
public string Name { get; set; }
public int Age { get; set; }
}
public class SubClass0101 : Class01
{
public int Length { get; set; }
}
public class SubClass0102 : Class01
{
public int Length { get; set; }
}
public class SubClass0103 : Class01
{
public int Length { get; set; }
}
#endregion
}
Many thanks in advance!!!
Your problem is this line:
assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
You need to enumerate all the referenced assemblies, not just the current executing assembly, so probably need to use GetReferencedAssemblies() on the result of GetExecutingAssembly() as your main project is the executing assembly and your referenced project would be a referenced assembly.
Related
I have a peculiar situation. In a legacy system no longer used we have base64 values stored that we now need to access.
By converting the base64 value to a string I can see that the base64 value contains my properties needed like this.
The problem is that I can't deserialize neither the byte array or the string to a anonymous type object or dynamic. This is because I don't have access to the binaries that this object is using. In this example it is shown as ConsoleApp2.
First try:
public static object FromByteArray(byte[] data)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(data))
{
object obj = bf.Deserialize(ms);
return obj;
}
}
Source:
https://stackoverflow.com/a/33022788/3850405
System.Runtime.Serialization.SerializationException: 'Unable to find
assembly 'ConsoleApp2, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'.'
Given that you normally can access properties from a plain Object Class I tried to set every assembly to System.Object with a SerializationBinder.
object o = new { A = "1", B = 2 };
public static object FromByteArray(byte[] data)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(data))
{
bf.Binder = new PreMergeToMergedDeserializationBinder();
object obj = bf.Deserialize(ms);
return obj;
}
}
}
sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
var systemObjectAssembly = "System.Object, System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
return Type.GetType(systemObjectAssembly);
}
}
Source:
https://stackoverflow.com/a/9012089/3850405
This prevents any runtime errors but everything that shows up looks like an empty object:
If I try to list properties using the code below it is of course empty as well.
Type myType = myObject.GetType();
List<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
What can I do to deserialize this base64 string and access the properties? Preferably I would not like to create a complete class hierarchy since the original object is quite large.
Runnable example program:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
object o = new { A = "1", B = 2 };
var base64String = "AAEAAAD/////AQAAAAAAAAAMAgAAAEJDb25zb2xlQXBwMiwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwFAQAAABhDb25zb2xlQXBwMi5FeGFtcGxlTW9kZWwCAAAAHDxNeVByb3BlcnR5QT5rX19CYWNraW5nRmllbGQcPE15UHJvcGVydHlCPmtfX0JhY2tpbmdGaWVsZAEACAIAAAAGAwAAAAtNeVRlc3RWYWx1ZXsAAAAL";
byte[] byteArray = Convert.FromBase64String(base64String);
string objectInfo = System.Text.Encoding.UTF8.GetString(byteArray);
var myObject = FromByteArray(byteArray);
Type myType = myObject.GetType();
List<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
}
public static object FromByteArray(byte[] data)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(data))
{
bf.Binder = new PreMergeToMergedDeserializationBinder();
object obj = bf.Deserialize(ms);
return obj;
}
}
}
sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
var systemObjectAssembly = "System.Object, System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
return Type.GetType(systemObjectAssembly);
}
}
}
Guess how the original object could have been stored in the first place:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp2
{
[Serializable]
public class ExampleModel
{
public string MyPropertyA { get; set; }
public int MyPropertyB { get; set; }
}
class Program
{
static void Main(string[] args)
{
var t = new ExampleModel();
t.MyPropertyA = "MyTestValue";
t.MyPropertyB = 123;
var byteArray = ToByteArray<ExampleModel>(t);
var base64String = Convert.ToBase64String(byteArray);
}
public static byte[] ToByteArray<T>(T obj)
{
if (obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
}
}
This is far from ideal but if you only need small amounts of data it is possible.
I started by looking at the string generated:
\0\u0001\0\0\0����\u0001\0\0\0\0\0\0\0\f\u0002\0\0\0BConsoleApp2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\u0005\u0001\0\0\0\u0018ConsoleApp2.ExampleModel\u0002\0\0\0\u001c<MyPropertyA>k__BackingField\u001c<MyPropertyB>k__BackingField\u0001\0\b\u0002\0\0\0\u0006\u0003\0\0\0\vMyTestValue{\0\0\0\v
From there I could see the values ConsoleApp2.ExampleModel with MyPropertyA and MyPropertyB. We could look at hex for MemberTypeInfo but in my case I used object for every property to save time.
If you want to know more about how the binary format of serialized .NET objects look like and how can it can be interpreted correctly I recommend this thread:
https://stackoverflow.com/a/30176566/3850405
I then created a namespace and a class with the properties for ConsoleApp2.ExampleModel.
namespace ConsoleApp2
{
[Serializable]
public class ExampleModel
{
public object MyPropertyA { get; set; }
public object MyPropertyB { get; set; }
}
}
After that I used the dynamic SerializationBinder from the source below:
sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize = null;
// For each assemblyName/typeName that you want to deserialize to
// a different type, set typeToDeserialize to the desired type.
String exeAssembly = Assembly.GetExecutingAssembly().FullName;
// The following line of code returns the type.
typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
typeName, exeAssembly));
return typeToDeserialize;
}
}
https://stackoverflow.com/a/9012089/3850405
After doing that I could get the values I needed.
In the real example I only matched the actual properties that I needed and ignored the rest.
Complete example:
using ConsoleApp2;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp2
{
[Serializable]
public class ExampleModel
{
public object MyPropertyA { get; set; }
public object MyPropertyB { get; set; }
}
}
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
object o = new { A = "1", B = 2 };
var base64String = "AAEAAAD/////AQAAAAAAAAAMAgAAAEJDb25zb2xlQXBwMiwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwFAQAAABhDb25zb2xlQXBwMi5FeGFtcGxlTW9kZWwCAAAAHDxNeVByb3BlcnR5QT5rX19CYWNraW5nRmllbGQcPE15UHJvcGVydHlCPmtfX0JhY2tpbmdGaWVsZAEACAIAAAAGAwAAAAtNeVRlc3RWYWx1ZXsAAAAL";
byte[] byteArray = Convert.FromBase64String(base64String);
string objectInfo = System.Text.Encoding.UTF8.GetString(byteArray);
var myObject = FromByteArray<ExampleModel>(byteArray);
Type myType = myObject.GetType();
List<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
}
public static T FromByteArray<T>(byte[] data)
{
if (data == null)
return default(T);
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(data))
{
bf.Binder = new PreMergeToMergedDeserializationBinder();
object obj = bf.Deserialize(ms);
return (T)obj;
}
}
}
sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize = null;
// For each assemblyName/typeName that you want to deserialize to
// a different type, set typeToDeserialize to the desired type.
String exeAssembly = Assembly.GetExecutingAssembly().FullName;
// The following line of code returns the type.
typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
typeName, exeAssembly));
return typeToDeserialize;
}
}
}
A few lessons learnt:
Values like ConsoleApp2.ExampleModel_someItems without a k__BackingField is probably a field without get and a set methods declared like this:
public object _someItems;
A value like System.Collections.Generic.List`1[[ConsoleApp2.ExampleModelListItem, ConsoleApp2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] needs to be handled individually in SerializationBinder. I did it like this:
if (typeName.Contains("System.Collections.Generic.List") && typeName.Contains("ExampleModelListItem"))
{
var t = new List<ExampleModelListItem>();
typeToDeserialize = t.GetType();
}
If you try to do something like this:
if (typeName.Contains("System.Collections.Generic.List"))
{
var t = new List<object>();
typeToDeserialize = t.GetType();
}
It will result in a exception similar to:
'Object of type 'System.Collections.Generic.List`1[System.Object]' cannot be converted to type 'System.Collections.Generic.List`1[ConsoleApp2.ExampleModelListItem]'.'
Something similar to ConsoleApp2.ExampleModel+EnumTypes means a nested class or enum.
Solved like this:
namespace ConsoleApp2
{
[Serializable]
public class ExampleModel
{
public object MyPropertyA { get; set; }
public enum EnumTypes
{
a
}
}
}
Source:
https://stackoverflow.com/a/2443261/3850405
I have this very basic class library that has inherited from System.Attribute. I also signed it as an assembly so the dll can be used in another program.
namespace BearData
{
public class BearData : Attribute
{
private string[] array1;
private string bear = "Bear";
private int weight;
public BearData(string bear)
{
this.bear = bear;
}
public string Bear
{
get
{
return bear;
}
set
{
bear = value;
}
}
public int Weight
{
get
{
return weight;
}
set
{
weight = value;
}
}
public string[] BearTypes()
{
array1 = new string[8];
array1[0] = "Brown/Grizzly";
array1[1] = "Polar";
array1[2] = "Asian Black";
array1[3] = "American Black";
array1[4] = "Sun";
array1[5] = "Sloth";
array1[6] = "Spectacled";
array1[7] = "Giant Panda";
return array1;
}
}
}
Here it is used in a basic console application. However due to my professor's cryptic, vague, and enigmatic nature, i'm at a stand still on getting this to work.I get an error from this line:
bearAttribute = (BearData.BearData)attrs[0];
"An unhandled exception of type 'System.IndexOutOfRangeException'
occurred in Assigntment5_Console.exe" is the exact error.
I guess my specific question what is causing that error?
But also more generally, Is this a good/proper way to use attributes when theyre from an outside library? It seems strange to me that there's random arrays thrown in here and that i'm casting an array to the attribute class?
By the way. This is how my professor wrote the code for an attribute class isolated in a single Visual Studio instance. He also had an example of class library dll exporting and I was left to my own device to figure out how to combine the 2.
using BearData;
namespace Assigntment5_Console
{
class Program
{
[BearData.BearData("Bear", Weight = 1000)]
static void Main(string[] args)
{
MemberInfo attributeInfo;
attributeInfo = typeof(BearData.BearData);
object[] attrs = attributeInfo.GetCustomAttributes(false);
//for (int i = 0; i < attrs.Length; i++)
//{
// Console.WriteLine(attrs[i]);
//}
BearData.BearData bearAttribute;
bearAttribute = (BearData.BearData)attrs[0];
Console.WriteLine("Animal: " + bearAttribute.Bear + "\nAverage Weight: " + bearAttribute.Weight);
Console.ReadLine();
}
}
}
You have defined the BearData attribute on the Program.Main() method, so you should be looking for the attribute there
The following code should fix your problem
namespace Assigntment5_Console
{
class Program
{
[BearData.BearData("Bear", Weight = 1000)]
static void Main(string[] args)
{
MethodBase method = MethodBase.GetCurrentMethod();
object[] attrs = method.GetCustomAttributes(typeof(BearData.BearData), true);
BearData.BearData bearAttribute;
bearAttribute = (BearData.BearData)attrs[0];
Console.WriteLine("Animal: " + bearAttribute.Bear + "\nAverage Weight: " + bearAttribute.Weight);
Console.ReadLine();
}
}
}
How can I get the full qualified name of a method call with Roslyn?
For example,
Request.QueryString, comes from System.Web.UI, how am I able to detect that?
How about class declaration within same project but different namespaces?
As well as function call from other classes of the same project.
Appreciate any form of help, thanks!
You should create Compilation for all SyntaxTree for your all project files. After it you can use symbol info for any node:
static string Code =
#"namespace TestNamespace
{
public class Test
{
public int A { get; set; }
public int B { get; set; }
public Test(int a, int b)
{
A = a;
B = b;
}
}
}";
static void Main(string[] args)
{
var syntaxTree = CSharpSyntaxTree.ParseText(Code);
var syntaxTrees = new SyntaxTree[] { syntaxTree }; // Add SyntaxTree array from project files.
var compilation = CSharpCompilation.Create("tempAssembly", syntaxTrees);
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var caretPosition = 46;
var symbol = SymbolFinder.FindSymbolAtPositionAsync(semanticModel, caretPosition, new AdhocWorkspace()).Result;
var fullName = symbol.ToString(); // fullName is "TestNamespace.Test"
}
i try to use CompileAssemblyFromSource to change 1 value at my main class.
But when i compile i get error "Could not load file or assembly or one of its dependencies" and this only happens when i try change static value of other class. But if i return some output or wrote anything at Console from this FooClass than all work's fine. But how can i change value of other class?
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
You can't find the type because you have compilation error in your code.You can't access the classes in your current code in this manner. You should at least reference the current assembly in your in-memory assembly.
UPDATE
You have two issues in your code. First, you have to make the class Program public. Then you should specify the full name of type in GetType method.
This code works fine:
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
public class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("stringToCode.FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
I need to get some JSON output in a .NET 2.0 C# script. The goal is to use one method to output all the JSON feeds I need. All the models have the same id and name properties so I have about 15 namespaces that have the same parts here. In short: since I'm use castle I can call the function like:
/public/get_place_tags.castle
/public/get_place_types.castle
/public/get_place_whichEver.castle
Which in castle is calling each method, ie: the get_place_tags(){} but I want to not have to work where I can call one method to get output from each type like this:
/public/get_json.castle?wantedtype=place_types
Does anyone know how to fix this?
namespace campusMap.Controllers
{
[Layout("home")]
public class PublicController : BaseController
{
/* works and returns */
public void get_pace_type()
{
CancelView();
CancelLayout();
place_types[] types = ActiveRecordBase<place_types>.FindAll();
List<JsonAutoComplete> type_list = new List<JsonAutoComplete>();
foreach (place_types place_type in types)
{
JsonAutoComplete obj = new JsonAutoComplete();
obj.id = place_type.place_type_id;
obj.label = place_type.name;
obj.value = place_type.name;
type_list.Add(obj);
}
string json = JsonConvert.SerializeObject(type_list);
RenderText(json);
}
/* can;t ever find the namespace */
public void get_json(string wantedtype)
{
CancelView();
CancelLayout();
Type t = Type.GetType(wantedtype);
t[] all_tag = ActiveRecordBase<t>.FindAll();
List<JsonAutoComplete> tag_list = new List<JsonAutoComplete>();
foreach (t tag in all_tag)
{
JsonAutoComplete obj = new JsonAutoComplete();
obj.id = tag.id;
obj.label = tag.name;
obj.value = tag.name;
tag_list.Add(obj);
}
string json = JsonConvert.SerializeObject(tag_list);
RenderText(json);
}
}
}
[EDIT]-- (Newest Idea) Runtime type creation.. This I think is the cleanest idea on a way to get it to work...-----
So the goal is really to have at runtime a type used.. right.. so I thought this would work.
http://www.java2s.com/Code/CSharp/Development-Class/Illustratesruntimetypecreation.htm
and based of that here is the method so far. I'm still having issues getting t to get past the error
"The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?)" .. not sure where I'm going wrong here. Can't seem to get any of it to work lol..
public void get_json(String TYPE)
{
CancelView();
CancelLayout();
if (String.IsNullOrEmpty(TYPE))
{
TYPE = "place_types";
}
// get the current appdomain
AppDomain ad = AppDomain.CurrentDomain;
// create a new dynamic assembly
AssemblyName an = new AssemblyName();
an.Name = "DynamicRandomAssembly";
AssemblyBuilder ab = ad.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
// create a new module to hold code in the assembly
ModuleBuilder mb = ab.DefineDynamicModule("RandomModule");
// create a type in the module
TypeBuilder tb = mb.DefineType(TYPE, TypeAttributes.Public);
// finish creating the type and make it available
Type t = tb.CreateType();
t[] all_tag = ActiveRecordBase<t>.FindAll();
List<JsonAutoComplete> tag_list = new List<JsonAutoComplete>();
foreach (t tag in all_tag)
{
JsonAutoComplete obj = new JsonAutoComplete();
obj.id = tag.id;
obj.label = tag.name;
obj.value = tag.name;
tag_list.Add(obj);
}
RenderText(JsonConvert.SerializeObject(tag_list));
}
[EDIT]-- (Older idea) Eval code-----
So this attempt to make this happen is to use reflection and stuff to do a eval of sorts based on this http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=11939
namespace EvalCSCode
{
/// <summary>
/// Interface that can be run over the remote AppDomain boundary.
/// </summary>
public interface IRemoteInterface
{
object Invoke(string lcMethod, object[] Parameters);
}
/// <summary>
/// Factory class to create objects exposing IRemoteInterface
/// </summary>
public class RemoteLoaderFactory : MarshalByRefObject
{
private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;
public RemoteLoaderFactory() { }
/// <summary> Factory method to create an instance of the type whose name is specified,
/// using the named assembly file and the constructor that best matches the specified parameters. </summary>
/// <param name="assemblyFile"> The name of a file that contains an assembly where the type named typeName is sought. </param>
/// <param name="typeName"> The name of the preferred type. </param>
/// <param name="constructArgs"> An array of arguments that match in number, order, and type the parameters of the constructor to invoke, or null for default constructor. </param>
/// <returns> The return value is the created object represented as ILiveInterface. </returns>
public IRemoteInterface Create(string assemblyFile, string typeName, object[] constructArgs)
{
return (IRemoteInterface)Activator.CreateInstanceFrom(
assemblyFile, typeName, false, bfi, null, constructArgs,
null, null, null).Unwrap();
}
}
}
#endregion
namespace campusMap.Controllers
{
public class JsonAutoComplete
{
private int Id;
[JsonProperty]
public int id
{
get { return Id; }
set { Id = value; }
}
private string Label;
[JsonProperty]
public string label
{
get { return Label; }
set { Label = value; }
}
private string Value;
[JsonProperty]
public string value
{
get { return Value; }
set { Value = value; }
}
}
[Layout("home")]
public class publicController : BaseController
{
#region JSON OUTPUT
/* works and returns */
public void get_pace_type()
{
CancelView();
CancelLayout();
place_types[] types = ActiveRecordBase<place_types>.FindAll();
List<JsonAutoComplete> type_list = new List<JsonAutoComplete>();
foreach (place_types place_type in types)
{
JsonAutoComplete obj = new JsonAutoComplete();
obj.id = place_type.id;
obj.label = place_type.name;
obj.value = place_type.name;
type_list.Add(obj);
}
string json = JsonConvert.SerializeObject(type_list);
RenderText(json);
}
/* how I think it'll work to have a dynmaic type */
public void get_json(string type)
{
CancelView();
CancelLayout();
/*t[] all_tag = ActiveRecordBase<t>.FindAll();
List<JsonAutoComplete> tag_list = new List<JsonAutoComplete>();
foreach (t tag in all_tag)
{
JsonAutoComplete obj = new JsonAutoComplete();
obj.id = tag.id;
obj.label = tag.name;
obj.value = tag.name;
tag_list.Add(obj);
}*/
StringBuilder jsonobj = new StringBuilder("");
jsonobj.Append(""+type+"[] all_tag = ActiveRecordBase<"+type+">.FindAll();\n");
jsonobj.Append("List<JsonAutoComplete> tag_list = new List<JsonAutoComplete>();{\n");
jsonobj.Append("foreach ("+type+" tag in all_tag){\n");
jsonobj.Append("JsonAutoComplete obj = new JsonAutoComplete();\n");
jsonobj.Append("obj.id = tag.id;\n");
jsonobj.Append("obj.label = tag.name;\n");
jsonobj.Append("obj.value = tag.name;\n");
jsonobj.Append("tag_list.Add(obj);\n");
jsonobj.Append("}\n");
CSharpCodeProvider c = new CSharpCodeProvider();
ICodeCompiler icc = c.CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("system.dll");
cp.ReferencedAssemblies.Add("Newtonsoft.Json.Net20.dll");
cp.ReferencedAssemblies.Add("Castle.ActiveRecord.dll");
cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;
StringBuilder sb = new StringBuilder("");
sb.Append("namespace CSCodeEvaler{ \n");
sb.Append("public class CSCodeEvaler{ \n");
sb.Append("public object EvalCode(){\n");
sb.Append("return " + jsonobj + "; \n");
sb.Append("} \n");
sb.Append("} \n");
sb.Append("}\n");
CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
System.Reflection.Assembly a = cr.CompiledAssembly;
object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");
Type t = o.GetType();
MethodInfo mi = t.GetMethod("EvalCode");
object s = mi.Invoke(o, null);
string json = JsonConvert.SerializeObject(s);
RenderText(json);
}/**/
#endregion
}
I know it was suggested that the using is not needed.. I know I don't know them off the top and maybe that is level showing.. But here they are for what I think will work just above.
using System;
using System.Collections;
using System.Collections.Generic;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Queries;
using Castle.MonoRail.Framework;
using Castle.MonoRail.ActiveRecordSupport;
using campusMap.Models;
using MonoRailHelper;
using System.IO;
using System.Net;
using System.Web;
using NHibernate.Expression;
using System.Xml;
using System.Xml.XPath;
using System.Text.RegularExpressions;
using System.Text;
using System.Net.Sockets;
using System.Web.Mail;
using campusMap.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Utilities;
using Newtonsoft.Json.Linq;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.IO;
using System.Threading;
using System.Reflection;
OK, this is just a suggestion, and I know nothing about castle but it seems to me that you are wanting a custom route.
This is untested and of course you will have to tweak it but have a look at your Global.asax RegisterRoutes method and add this above you default route
routes.MapRoute(
"TypeRoute", // Route name
"Public/{wantedtype}", // URL with parameters
new { controller = "Public", action = "get_json", wantedtype = UrlParameter.Optional } // Parameter defaults
);
something like this might get what you are after.
you might need to fiddle with the action so it matches your castle framework
EDIT
I had some more time to look at this. Using the route I described above and the following method on the controller I got a good result
public void get_json(string wantedtype)
{
Type t = Type.GetType(wantedtype);
// t.Name = "Int32" when "System.Int32 is used as the wanted type
}
http://.../Public/System.Int32 works,
this url doesn't work http://.../Public/Int32
Now if you wanted to use the short version you could use something like MEF or AutoFAC or Castle to do a lookup in your container for the type you are after.
So my answer is use a good route to get the key for the wanted type then build a factory to manage the type mappings and produce an instance of what you are after.
I just had a look at the ActiveRecord API Doco Here
There is a method FindAll(Type targetType) : Array
have you tried replacing
Type t = Type.GetType(wantedtype);
t[] all_tag = ActiveRecordBase<t>.FindAll();
// blah blah blah
with
Type t = Type.GetType(wantedtype);
dynamic all_tag = ActiveRecordBase.FindAll(t);
RenderText(JsonConvert.SerializeObject(all_tag ));
completely untested but see what you think
I tired and tried and tired to so one of these ways. Me and an co-worker came up this with solution. I made 2 files.
Ijson_autocomplete.cs
using System;
namespace campusMap.Models
{
public interface Ijson_autocomplete
{
int id { get; set; }
string name { get; set; }
String get_json_data();
}
}
json_autocomplete.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Castle.ActiveRecord;
using System.Collections.Generic;
using System.Data.SqlTypes;
using Newtonsoft.Json;
using Newtonsoft.Json.Utilities;
using Newtonsoft.Json.Linq;
namespace campusMap.Models
{
public class JsonAutoComplete
{
private int Id;
[JsonProperty]
public int id
{
get { return Id; }
set { Id = value; }
}
private string Label;
[JsonProperty]
public string label
{
get { return Label; }
set { Label = value; }
}
private string Value;
[JsonProperty]
public string value
{
get { return Value; }
set { Value = value; }
}
}
public class json_autocomplete<t> where t : campusMap.Models.Ijson_autocomplete
{
virtual public String get_json_data()
{
t[] all_tag = ActiveRecordBase<t>.FindAll();
List<JsonAutoComplete> tag_list = new List<JsonAutoComplete>();
foreach (t tag in all_tag)
{
JsonAutoComplete obj = new JsonAutoComplete();
obj.id = tag.id;
obj.label = tag.name;
obj.value = tag.name;
tag_list.Add(obj);
}
return JsonConvert.SerializeObject(tag_list);
}
}
}
then when I called the function from the url this is what it ended up as
public void get_json(String TYPE)
{
CancelView();
CancelLayout();
Type t = Type.GetType("campusMap.Models."+TYPE);
Ijson_autocomplete theclass = (Ijson_autocomplete)Activator.CreateInstance(t);
RenderText(theclass.get_json_data());
}
and one off the models
namespace campusMap.Models
{
[ActiveRecord(Lazy=true)]
public class place_types : json_autocomplete<place_types>, campusMap.Models.Ijson_autocomplete
{
private int place_type_id;
[PrimaryKey("place_type_id")]
virtual public int id
{
get { return place_type_id; }
set { place_type_id = value; }
}
private string Name;
[Property]
virtual public string name
{
get { return Name; }
set { Name = value; }
}
private string Attr;
[Property]
virtual public string attr
{
get { return Attr; }
set { Attr = value; }
}
private IList<place> places;
[HasAndBelongsToMany(typeof(place), Lazy = true, Table = "place_to_place_models", ColumnKey = "place_model_id", ColumnRef = "place_id", Inverse = true, NotFoundBehaviour = NotFoundBehaviour.Ignore)]
virtual public IList<place> Places
{
get { return places; }
set { places = value; }
}
}
}
now when you call
/public/get_json.castle?wantedtype=place_types
or
/public/get_json.castle?wantedtype=place_tags
You will get the json output of each model. Tada! :D Thank you everyone for helping.