Modify property serialization/deserialization in WinForms Form1.resx file - c#

I have a custom class and made the same serialization as System.Drawing.Image, by implementing ISerializable interface and two methods:
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) to give the serializer my data in the format I need.
A constructor in my class: protected MyClass(SerializationInfo info, StreamingContext context)
[Serializable]
[TypeConverter(typeof(MyClassTypeConverter))]
[Editor(typeof(MyClassEditor), typeof(UITypeEditor))]
public class MyClass : MarshalByRefObject, ISerializable, ICloneable, IDisposable
A have added my class as a property to a custom button -> MyButton.MyClass
The problem is that at design time when I set MyClass the serializer writes the assembly name and version in the RESX file of the form before my custom serialized object. Here is how it looks like:
<assembly alias="MyAssembly" name="MyAssembly, Version=2020.1.1, Culture=neutral, PublicKeyToken=XXX" />
<data name="myButton1.MyClass" type="MyAssembly.MyClass, MyAssembly">
<value>Some long text data</data>
</data>
After a new release my clients started having this exception:
System.InvalidCastException: '[A]MyAssembly.MyClass cannot be cast to [B]MyAssembly.MyClass. Type A originates from 'MyAssembly, Version=2020.1.1, Culture=neutral, PublicKeyToken=XXX' in the context 'Default' at location 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\...
Here is the designer file:
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form2));
this.myButton1 = new MyAssembly.MyButton();
this.SuspendLayout();
//
// myButton1
//
this.myButton1.Location = new System.Drawing.Point(5, 5);
this.myButton1.Name = "myButton1";
this.myButton1.Size = new System.Drawing.Size(200, 25);
this.myButton1.MyClass = ((MyAssembly.MyClass)(resources.GetObject("myButton1.MyClass")));
this.myButton1.TabIndex = 0;
this.myButton1.Text = "myButton1";
The assembly where type MyClass is located is already loaded, because MyButton is located in the same assembly. So I do not need the RESX file to contain information about the assembly.
Currently I cannot find a solution on my own and I am thinking of two options:
How can I prevent serializing the <assembly alias="MyAssembly"...> in the resx file of the form?
Can I load MyClass from the resx file without loading the described assembly?

Related

Load libraries from app.config

Say, for example, I have many methods for calculating the square root of a number.
One developer gives me his own .dll (maths1.dll), another one gives me his too (maths2.dll) and maybe a third one (maths3.dll).
All of them contains the same class, implementing the same interface.
Assembly 1 Maths1.dll
public class Maths : IMaths {
public static string Author = "Author1";
public static SquareRoot(int number) {
// Implementation method 1
}
}
Assembly 2 Maths2.dll
public class Maths : IMaths {
public static string Author = "Author2";
public static SquareRoot(int number) {
// Implementation method 2
}
}
etc. etc.
And I have a console application wich must be aware of all the dlls dynamically at runtime.
Looking for .dll files in code is undesirable.
// DON'T WANT THIS
DirectoryInfo di = new DirectoryInfo("bin");
FileInfo[] fi = di.GetFiles("*.dll");
My idea is to manage them from the app.config file with a custom configuration section.
<configuration>
<configSections>
<section name="MathsLibraries" type="MyMathsLibrariesSectionClass, ApplicationAssembly" />
</configSections>
<MathsLibraries>
<Library author="Author1" type="MathsClass, Maths1Assembly" /><!-- Maths1.dll -->
<Library author="Author2" type="MathsClass, Maths2Assembly" /><!-- Maths2.dll -->
<Library author="Author3" type="MathsClass, Maths3Assembly" /><!-- Maths3.dll -->
</MathsLibraries>
</configuration>
Considering I will manually copy the library file Maths1.dll to my application's bin folder.Then, the only thing I would have to do is, add a line to my app.config file in the MathsLibraries section.
I need an example code for the console application's Main, presenting the user all the dynamically linked .dll's and allowing him to calculate the square root of a number with the chosen library.
// NOT WORKING CODE, JUST IDEA OF WHAT IS NEEDED
public static void Main(string[] args) {
// Show the user the linked libraries
MathsLibraries libs = MathsLibraries.GetSection();
Console.WriteLine("Available libraries:");
foreach (MathLibrary lib in libs.Libraries) {
Console.WriteLine(lib.Author);
}
// Ask for the library to use
Console.Write("Which do you want to use?");
selected_option = Console.Read();
IMaths selected_library;
// since we don't know wich class would be,
// declare a variable using the interface we know they al implement.
// Assign the right class to the variable
if (selected_option == '1') {
selected_library = Assembly1.Maths;
} else if (selected_option == '2') {
selected_library = Assembly2.Maths;
}
// other options...
// Invoke the SquareRoot method of the dynamically loaded class
float sqr_result = selected_library.SquareRoot(100);
Console.WriteLine("Result is {0}", sqr_result);
Console.WriteLine("Press Enter key to exit");
Console.Read();
}
Please, can any one help me in this task of loading assemblies from app.config.
Detailed code would be appreciated.
Thanks!
Assuming they all implement the same interface (actually the same one, declared in the same assembly, not just the same definition in individual namespaces), you could use dependency injection like ms unity, which can be managed in config file, to register all implementations of this interface, create concrete implementations of all at run time, and execute them.
EDIT
Wrote a sample app, I'll post the meat here, and will provide a link to git hub or something when I get it uploaded.
I have an interface, IMasterInterface, and 3 implementations in separate assemblies 'UnityContainer.MasterImplementation', 'Satellite1.Implementation1' and 'Satellite2.Implementation2'. UnityConfiguration is a console app, and I have referenced unity using NuGet. For convenience, I have configured the build paths of all 3 assemblies to the same Build directory for Debug, so the 2 satellite assemblies are available to the console app.
IMasterInterface has a single method GetResult(): string.
Edit web config with the following:
<configuration>
<configSections>
<section name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration, Version=3.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<unity>
<typeAliases>
<typeAlias alias="IMasterInterface" type="UnityInjection.IMasterInterface, UnityInjection" />
<typeAlias alias="MasterImp" type="UnityInjection.MasterImplementation, UnityInjection" />
<typeAlias alias="SatelliteOneImplementation" type="Satellite1.Implementation1, Satellite1" />
<typeAlias alias="SatelliteTwoImplementation" type="Satellite2.Implementation2, Satellite2" />
</typeAliases>
<containers>
<container name="containerOne">
<types>
<type type="IMasterInterface" mapTo="MasterImp" name="Master" />
<type type="IMasterInterface" mapTo="SatelliteOneImplementation" name="One" />
<type type="IMasterInterface" mapTo="SatelliteTwoImplementation" name="Two" />
</types>
</container>
</containers>
</unity>
</configuration>
Configure the container
//Set up the dependency container
IUnityContainer myContainer = new UnityContainer();
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Configure(myContainer, "containerOne");
Resolve All implementations
//create all implementations of out interface
var implementations = myContainer.ResolveAll<IMasterInterface>();
//call the method we are interested in for all implementations
foreach (var implementation in implementations)
{
Console.WriteLine(implementation.GetResult());
}
Resolve a specific named implementation
//Get a particular one
var specific = myContainer.Resolve<IMasterInterface>("Master");
Console.WriteLine(specific.GetResult());
You can use reflection to load selected library and create instance of required type.
var assembly = Assembly.LoadFrom("selected_math_library.dll");
var types = assembly.GetTypes();
var mathType = (from type in types
where type.GetInterface("IMath") != null && !type.IsAbstract
select type).ToList();
if (mathType.Count > 0)
{
IMath math = (IMath)Activator.CreateInstance(mathType);
// call methods from math
}
Possible duplicate of C# - Correct Way to Load Assembly, Find Class and Call Run() Method
var asm = Assembly.LoadFile(#"YourMathAssembly.dll");
var type = asm.GetType("Maths");
var sqrRoot = Activator.CreateInstance(Maths) as IMaths;
if (sqrRoot == null)
throw new Exception("broke");
sqrRoot .SquareRoot(100);

What mistake am I making when serializing?

This leads to serialization exception at runtime. It's just a demo project to test the best way to do this. I included the main method and the class which im trying to serialize.
Ignore: I really cant add more detail, i've described the problem, attached the code, this "please add more details" thing is the stupidest thing ever. Let me post it already.
Data toSend = new Data();
toSend.Output();
///SERIALIZE
BinaryFormatter formatter = new BinaryFormatter();
Stream streamOut = File.OpenWrite("file");
formatter.Serialize(streamOut, toSend);
streamOut.Close();
Console.WriteLine("----------------------------");
///DESERIALIZE
Stream streamIn = File.OpenRead("file");
Object received = formatter.Deserialize(streamIn);
Data toReceive = (Data)received;
toReceive.Output();
class Data : ISerializable
{
int integerData;
string stringData;
bool booleanData;
int shouldnotbeserialized;
public Data()
{
integerData = 1;
stringData = "Hello";
booleanData = true;
shouldnotbeserialized = 55;
}
//To deserialize
public Data(SerializationInfo info, StreamingContext context)
{
integerData = info.GetInt32("theint");
stringData = info.GetString("thestring");
booleanData = info.GetBoolean("thebool");
}
public void Output()
{
Console.WriteLine(integerData);
Console.WriteLine(stringData);
Console.WriteLine(booleanData);
Console.WriteLine(shouldnotbeserialized);
}
//Implemented method to serialize
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("thestring", stringData);
info.AddValue("theint", integerData);
info.AddValue("thebool", booleanData);
}
}
Exception message:
Type 'SerializationDemo.Data' in Assembly 'SerializationDemo,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked
as serializable.
The answer is given to you in the exception message:
Type 'SerializationDemo.Data' in Assembly 'SerializationDemo,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked
as serializable.
You need to mark your class with the Serializable attribute.
[Serializable()]
class Data : ISerializable
From the context it seems like you are going to be transmitting the object down a network (deduced due to the local variable names toSend, toReceive). You need to be aware that, if you where for example, using a client-server model, an object serialized and sent from the server software will not be deserializable from the client software by default.
This is because the fundamental characteristic of binary serialization is that it preserves type system fidelity. Type system fidelity denotes that type information is not lost in the serialization process. This means that when you serialize an object, is is not only the "object's state" written to the data stream, but also the name of the class and the containing assembly. Even if you defined the class Data in the assembly that is going to deserialize the data, the deserialize method will fail (throw an exception) because the deserializer will be looking for the type Program1.Namespace.Data not Program2.Namespace.Data. To remedy this you can define your data messages in a mutual assembly (class library) or by defining a SerializationBinder that will allow you to basically trick the deserialize method into thinking you are still in the same assembly.

How to load a custom .config file with Configuration

I'm not sure I am going about this right, but I am trying to write a custom configuration file for an asp.NET web project. I want to make it clear this is not a windows form, because half the stuff I find is only for those. I am trying to read and write to this file to change a couple of application settings.
I wrote this huge class using this tutorial. Here's a simplified version:
namespace Tedski.Configuration {
public class TedskiSection : ConfigurationSection {
private static ConfigurationProperty s_propName;
private static ConfigurationPropertyCollection s_properties;
static TedskiSection() {
s_propName = new ConfigurationProperty(
"name",
typeof(string),
null,
ConfigurationPropertyOptions.IsRequired
);
s_properties = new ConfigurationPropertyCollection();
s_properties.Add(s_propName);
}
protected override ConfigurationPropertyCollection Properties {
get { return s_properties; }
}
[ConfigurationProperty("name")]
public string Name {
get {
return (string)base[s_propName];
}
set {
base[s_propName] = value;
}
}
}
}
I am now not sure where to define my configuration. I can put this in my Web.config file like this:
<configuration>
<configSections>
<section name="Tedski" type="Tedski.Configuration.TedskiSection" />
</configSections>
<Tedski name="Ted" />
</configuration>
and everything loads up fine with this:
TedskiSection section = ConfigurationManager.GetSection("Tedski") as TedskiSection;
Console.WriteLine(section.Name); //produces "Ted"
However, I need to be able to load this up with the Configuration object, in order to be able to call Configuration.Save(). I can't seem to load up that specific section and save the Web.config (from what I understand this is dangerous). Another solution I'm trying out is creating a separate .config file (Tedski.config) with the same XML syntax as defined above.
I tried using this answer to load up Tedski.config, but I get an error:
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = Server.MapPath("~/Tedski.config");
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigUserLevel.None);
TedskiSection section = config.GetSection("Tedski") as TedskiSection; //fails
ConfigurationErrorsException "An error occurred creating the
configuration section handler for Tedski: Could not load type
'Tedski.Configuration.TedskiSection' from assembly
'System.Configuration, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a'
If I got this to load, I could then modify section.Name and call config.Save(), but I'm stuck here.
In your type property, you have to tell it which assembly contains your Tedski.Configuration.TedskiSection. For example:
<section name="Tedski" type="Tedski.Configuration.TedskiSection, TedskiAssemblyName" />
Replace "TedskiAssemblyName" there with the name of the assembly that contains the class.

Issue with assembly createinstance and crystal report

I load an assembly
private System.Reflection.Assembly;
object myData;
myAssembly = System.Reflection.Assembly.LoadFile("C:\\CrystalDecisions.CrystalReports.Engine.dll");
then i create an instance.
myData=myAssembly.CreateInstance("CrystalDecisions.CrystalReports.Engine.ReportDocument", true);
this myData always returns null, any thought why it is returning null where as myAssembly has the assembly information and its public key token?
try something like the following below..
// dynamically load assembly from file Test.dll
Assembly myData = Assembly.LoadFile(#"C:\CrystalDecisions.CrystalReports.Engine.dll");
// get type of class Calculator from just loaded assembly
Type myData = myData.GetType("CrystalDecisions.CrystalReports")
// create instance of class Calculator
object myDataInstance = Activator.CreateInstance(myData);
Reflection Examples C#

C# deserialization using reflection

I have a .dll that deserializes a class. When I call this .dll from a project or not using reflection it works fine. When I call the .dll using reflection I get an error on the line that deserializes. I know this is due isolation that happens when I use reflection to load an assembly. Wondering if anyone has any fix or an idea of how to implement this? BTW, the serialization works just fine, it's just the deserialization that doesnt work.
I tried both binary and xml, here's the code:
static public object SerializeLoad(string sFilename)
{
try
{
List<ElementTodo> _object = null;//ElementTodo _object = null;
Stream stream = File.Open(sFilename, FileMode.Open);
//BinaryFormatter bformatter = new BinaryFormatter();
XmlSerializer bformatter = new XmlSerializer(typeof(ElementTodo), "ToDo");
//_object = (_object.GetType())bformatter.Deserialize(stream);
_object = (List<ElementTodo>)bformatter.Deserialize(stream);
stream.Close();
return _object;
}
catch(Exception e)
{
string error = e.Message;
return null;
}
}
The generated XML is as follows:
<
?xml version="1.0"?>
<ArrayOfElementTodo xmlns:xsi="w3.org/2001/XMLSchema-instance"; xmlns:xsd="w3.org/2001/XMLSchema"; xmlns="ToDo">
<ElementTodo Title="a" content="aa" isDone="false" />
<ElementTodo Title="b" content="bb" isDone="false" />
<ElementTodo Title="c" content="cc" isDone="false" />
<ElementTodo Title="d" content="dd" isDone="false" />
</ArrayOfElementTodo>
I assume that ElementTodo is in an assembly that both your code and the assembly loaded using reflection have access to? What you have to be careful of is that your loaded assembly is using the same dependent assembly and doesn't load a new copy. Otherwise you wind up with fun errors like 'Object X (of type ElementTodo) is not of type ElementTodo', since two copies of the types are loaded. It's hard to say for sure that this is your issue without more information on the specific error, however.
If this is your problem, you can address it by forcing assemblies to resolve to the version that's already loaded using something like this:
In your startup code somewhere:
//This is required because we load assemblies at runtime
//If this is not used, there can be problems when Reflecting over Types
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Implementation:
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return AppDomain.CurrentDomain.GetAssemblies().
FirstOrDefault(assembly => assembly.FullName == args.Name);
}

Categories