This question already has answers here:
Ignore a property during xml serialization but not during deserialization
(2 answers)
Memory Leak using StreamReader and XmlSerializer
(6 answers)
How to add XmlInclude attribute dynamically
(4 answers)
Closed 4 years ago.
I have a simple class as a test:
public class TEST
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
}
I want to serialize this class to XML, but when I do; I want to specify at runtime how I want to serialize it. Only PropertyOne, only PropertyTwo or both.
I figured out you can achieve it normally by using the [XmlIgnore] attribute on the properties that you don't want to serialize. I then tried the method explained here:
How to add property-level Attribute to the TypeDescriptor at runtime?
But for some reason it simply doesn't work. Here's my code:
TEST testInstance = new TEST();
testInstance.PropertyOne = "One";
testInstance.PropertyTwo = "Two";
SetAttributesToObjectOnProperty(testInstance, "PropertyOne");
using (StringWriter mainStringWriter = new StringWriter())
{
XmlSerializer mainXMLSerializer = new XmlSerializer(testInstance.GetType());
mainXMLSerializer.Serialize(mainStringWriter, testInstance);
string theXMLToWrite = mainStringWriter.ToString();
}
public static void SetAttributesToObjectOnProperty(object classInstanceToCheck, string propertyName)
{
if (propertyName != null && classInstanceToCheck != null)
{
// Prepare the Property Overriding Type Descriptor
PropertyOverridingTypeDescriptor mainPropertyOverridingTypeDescriptor = new PropertyOverridingTypeDescriptor(TypeDescriptor.GetProvider(classInstanceToCheck).GetTypeDescriptor(classInstanceToCheck));
// Iterate through properies in the supplied Object / Type
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(classInstanceToCheck))
{
if (propertyDescriptor.Name == propertyName)
{
System.Xml.Serialization.XmlIgnoreAttribute theXMLIgnoreAttributToAdd = new System.Xml.Serialization.XmlIgnoreAttribute();
PropertyDescriptor newPropertyDescriptor = TypeDescriptor.CreateProperty(classInstanceToCheck.GetType(), propertyDescriptor, theXMLIgnoreAttributToAdd);
// Set the new PropertyOverridingTypeDescriptor to override that property
mainPropertyOverridingTypeDescriptor.OverrideProperty(newPropertyDescriptor);
break;
}
}
// Add the new descriptor provider that will return our descriptor instead of default
TypeDescriptor.AddProvider(new TypeDescriptorOverridingProvider(mainPropertyOverridingTypeDescriptor), classInstanceToCheck);
}
}
public static void SetAttributesToObjectOnProperty(object classInstanceToCheck, string propertyName)
{
try
{
if (propertyName != null)
{
// Prepare the Property Overriding Type Descriptor
PropertyOverridingTypeDescriptor mainPropertyOverridingTypeDescriptor = new PropertyOverridingTypeDescriptor(TypeDescriptor.GetProvider(classInstanceToCheck).GetTypeDescriptor(classInstanceToCheck));
// Iterate through properies in the supplied Object / Type
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(classInstanceToCheck))
{
if (propertyDescriptor.Name == propertyName)
{
System.Xml.Serialization.XmlIgnoreAttribute theXMLIgnoreAttributToAdd = new System.Xml.Serialization.XmlIgnoreAttribute();
PropertyDescriptor newPropertyDescriptor = TypeDescriptor.CreateProperty(classInstanceToCheck.GetType(), propertyDescriptor, theXMLIgnoreAttributToAdd);
// Set the new PropertyOverridingTypeDescriptor to override that property
mainPropertyOverridingTypeDescriptor.OverrideProperty(newPropertyDescriptor);
break;
}
}
// Add the new descriptor provider that will return our descriptor instead of default
TypeDescriptor.AddProvider(new TypeDescriptorOverridingProvider(mainPropertyOverridingTypeDescriptor), classInstanceToCheck);
}
}
catch (Exception ex)
{
MessageBox.Show("Error at 'SetAttributesToObjectOnProperty'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public class PropertyOverridingTypeDescriptor : CustomTypeDescriptor
{
// Fields
private readonly Dictionary<string, PropertyDescriptor> OverridePropertyDescriptors = new Dictionary<string, PropertyDescriptor>();
// Constructor
public PropertyOverridingTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { }
// Methods
public void OverrideProperty(PropertyDescriptor thePropertyDescriptor)
{
try
{
OverridePropertyDescriptors[thePropertyDescriptor.Name] = thePropertyDescriptor;
}
catch (Exception ex)
{
MessageBox.Show("Error at 'OverrideProperty'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public override object GetPropertyOwner(PropertyDescriptor thePropertyDescriptor)
{
try
{
object toReturn = base.GetPropertyOwner(thePropertyDescriptor);
if (toReturn == null)
{
return this;
}
return toReturn;
}
catch (Exception ex)
{
MessageBox.Show("Error at 'GetPropertyOwner'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
}
public PropertyDescriptorCollection GetPropertiesMain(PropertyDescriptorCollection thePropertyDescriptorCollection)
{
try
{
List<PropertyDescriptor> mainPropertyDescriptorList = new List<PropertyDescriptor>(thePropertyDescriptorCollection.Count + 1);
foreach (PropertyDescriptor propertyDescriptor in thePropertyDescriptorCollection)
{
if (OverridePropertyDescriptors.ContainsKey(propertyDescriptor.Name))
{
mainPropertyDescriptorList.Add(OverridePropertyDescriptors[propertyDescriptor.Name]);
}
else
{
mainPropertyDescriptorList.Add(propertyDescriptor);
}
}
PropertyDescriptorCollection toReturn = new PropertyDescriptorCollection(mainPropertyDescriptorList.ToArray());
return toReturn;
}
catch (Exception ex)
{
MessageBox.Show("Error at 'GetPropertiesMain'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
}
public override PropertyDescriptorCollection GetProperties()
{
try
{
return GetPropertiesMain(base.GetProperties());
}
catch (Exception ex)
{
MessageBox.Show("Error at 'GetProperties'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
try
{
return GetPropertiesMain(base.GetProperties(attributes));
}
catch (Exception ex)
{
MessageBox.Show("Error at 'GetProperties'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
}
}
public class TypeDescriptorOverridingProvider : TypeDescriptionProvider
{
// Fields
private readonly ICustomTypeDescriptor MainCustomTypeDescriptor;
// Constructor
public TypeDescriptorOverridingProvider(ICustomTypeDescriptor theCustomTypeDescriptor)
{
try
{
this.MainCustomTypeDescriptor = theCustomTypeDescriptor;
}
catch (Exception ex)
{
MessageBox.Show("Error at Constructor 'TypeDescriptorOverridingProvider'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// Methods
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
try
{
return MainCustomTypeDescriptor;
}
catch (Exception ex)
{
MessageBox.Show("Error at 'GetTypeDescriptor'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
}
}
There are no exceptions, it just still serializes both.
I don't know if I'm not using this method correctly or if I'm missing something else here.
I would like to use this method on other bigger classifications and try use other XML Attributes as well.
Related
This code works by taking a function as a parameter and using try-catch to log the result of the function.
When I change the functionDecorator here, I want the changes to be applied automatically to functionDecorator<T> or functionDecorator<T1, T2> without copying the entire code as it is now
These functions that do almost the same thing Is there a way to manage each other's behavior in one place, including even try-catch statements?
public void funtionDecorator(Func<bool> Func, string successText = "success", string failText = "fail", string errorText = "error")
{
try
{
if (Func())
{
txtStatusBar.Text = successText;
}
else
{
txtStatusBar.Text = failText;
}
}
catch(Exception ex)
{
txtStatusBar.Text = errorText + Func.Method.Name + ex.ToString();
}
}
// ex : funtionDecorator<DataTable>(useDataFuntion, dt);
public void funtionDecorator<T>(Func<T, bool> Func, T type, string successText = "success", string failText = "fail", string errorText = "error")
{
try
{
if (Func(type))
{
txtStatusBar.Text = successText;
}
else
{
txtStatusBar.Text = failText;
}
}
catch (Exception ex)
{
txtStatusBar.Text = errorText + Func.Method.Name + ex.ToString();
}
}
// ex : funtionDecorator<DataTable, Double>(useDataFuntion, dt, value);
public void funtionDecorator<T1, T2>(Func<T1, T2, bool> Func, T1 type1, T2 type2, string successText = "success", string failText = "fail", string errorText = "error")
{
try
{
if (Func(type1, type2))
{
txtStatusBar.Text = successText;
}
else
{
txtStatusBar.Text = failText;
}
}
catch (Exception ex)
{
txtStatusBar.Text = errorText + Func.Method.Name + ex.ToString();
}
}
I tried to handle it dynamically using dynamic, but the function type was not converted to dynamic, so I couldn't find a way.
This function connects to postgres database and returns Dataset.
Two things i want to understant
If i get an error how can i return it ?
Is this the best way to return Dataset ?
string strODBCDriverName = "DSN=Postgres_32";
public DataSet SelectDataSet(string sql, bool isProcedure, Dictionary<string, object> parameters = null) {
using (OdbcConnection odbcConnection = new OdbcConnection(strODBCDriverName))
{
odbcConnection.Open();
using (OdbcCommand odbcCommand = new OdbcCommand(sql, odbcConnection))
{
if (isProcedure) odbcCommand.CommandType = CommandType.StoredProcedure;
else odbcCommand.CommandType = CommandType.Text;
if (parameters != null)
foreach (KeyValuePair<string, object> parameter in parameters)
odbcCommand.Parameters.AddWithValue(parameter.Key, parameter.Value);
using (OdbcDataAdapter adapter = new OdbcDataAdapter(odbcCommand))
{
using (DataSet ds = new DataSet())
{
try
{
adapter.Fill(ds); return ds;
}
catch (Exception ex)
{
throw (ex);
}
finally
{
}
}
}
}
}
}
I like having generic Result class that can be reused:
internal class Result
{
internal bool IsFailure => !IsSuccess;
internal bool IsSuccess { get; }
internal string Error { get; }
protected Result(bool isSuccess, string error) {
IsSuccess = isSuccess;
Error = error;
}
private Result(bool isSuccess) : this(isSuccess, null) { }
internal static Result Fail(string error) => new Result(false, error);
internal static Result<T> Fail<T>(string error) =>
new Result<T>(default(T), false, error);
internal static Result Ok() => new Result(true);
internal static Result<T> Ok<T>(T value) => new Result<T>(value, true);
}
internal sealed class Result<T> : Result
{
internal T Value { get; }
internal Result(T value, bool isSuccess) : this(value, isSuccess, null) { }
internal Result(T value, bool isSuccess, string error) : base(isSuccess, error) {
Value = value;
}
This can be used not only DataSet, but any type.
In your case return would be Result<DataSet> and returns can become:
returns ds --> new Result.Ok(d)
throw ex --> new Result.Fail<DataSet>(ex.Message)
I think it would be great if you return null; If you need return some customized message as well means you can use out parameters for this. So that the return value will be null if any Exception occurs in this case out parameter will holds the exception details. if Dataset is populated well means outParameter will have a values "Success" or something like that. So the method signature will be changed as like the following
public static DataSet SelectDataSet(string sql, bool isProcedure, out string message, Dictionary<string, object> parameters = null)
{
// Rest of codes here
try
{
message = "Success";
adapter.Fill(ds);
return ds;
}
catch (Exception ex)
{
message = ex.Message;
return null;
}
}
And you can call this method like this:
string message = String.Empty;
DataSet resultDataset = SelectDataSet("query here", false, out message);
if (resultDataset != null)
{
Console.WriteLine(message);
// proceed with resultDataset
}
else
{
Console.WriteLine(message);
}
Here resultDataset will be null in case of any exception otherwise you can proceed with its value.
Create a class:
class DataSetWithError: DataSet
{
public Exception msg { get; set; }
}
Save error during query:
using (OdbcDataAdapter adapter = new OdbcDataAdapter(odbcCommand))
{
DataSetWithError ds = new DataSetWithError();
try
{
adapter.Fill(ds);
}
catch (Exception ex)
{
ds.msg = ex;
}
finally
{
adapter.Close();
}
return ds;
}
And result:
DataSetWithError dataSetWithError = SelectDataSet();
if (dataSetWithError.msg == null)
{
// Show data
}
else
{
MessageBox.Show(dataSetWithError.msg.ToString());
}
The user of my Visual Studio application may click a button to restore the application to the factory defaults. At the factory we configure the application and then click another button to set that configuration as the factory defaults to the present configuration settings.
However, if we save the configuration settings (XML format) to settings.settings, they are stored in our own user folder (not in the Visual Studio project folder), and the user doesn't receive them.
(settings.settings uses the default values stored at design time.)
We need to store the factory defaults in a file that is included in the executable, or distributed with it. We can write the factory defaults in a factorydefaultconfig.xml file included with the distribution, but I thought you may know of a better way of doing that.
Right now I am studying Application configuration files to see if that's what I should be using.
Yep App Settings or rolling your own is the way we've handled this in the past. Add a reference to System.Configuration to your project, and then use the following:
ConfigurationManager.AppSettings.Set("lang", "English"); //Set
string getLang = ConfigurationManager.AppSettings["lang"]; //Get
For the App.config:
<configuration>
<appSettings>
<add key="lang" value="English"/>
</appSettings>
</configuration>
Just in case you want to try this out. Here is a class I wrote called ConfigHub for doing just what you are talking about. It makes use of locks as well, to ensure you don't end up with file in use errors:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
public static class ConfigHub
{
#region State
private static string WorkingDirectoryVar = null;
private static string ConfigFileNameVar = null;
private static bool AutoRefreshVar = true;
private static bool VerboseVar = true;
private static bool SetupExecutedVar = false;
private static XmlDocument ConfigDocVar = new XmlDocument();
private static Dictionary<string, string> ConfigLookupPair = new Dictionary<string, string>();
private static Object ConfigHubLock = new Object();
private const string CommentNameVar = "#comment";
#endregion
#region Property
public static bool Verbose
{
get { return VerboseVar; }
set { VerboseVar = value; }
}
public static bool AutoRefresh
{
get { return AutoRefreshVar; }
set { AutoRefreshVar = value; }
}
public static bool SetupExecuted
{
get { return SetupExecutedVar; }
}
public static string ConfigFilePath
{
get { return WorkingDirectoryVar + #"\" + ConfigFileNameVar; }
}
public static string ConfigFileName
{
get { return ConfigFileNameVar; }
}
public static string WorkingDirectory
{
get { return WorkingDirectoryVar; }
}
#endregion
#region Setup
public static void Setup()
{
lock (ConfigHubLock)
{
//Initialize config with default
WorkingDirectoryVar = Environment.CurrentDirectory;
ConfigFileNameVar = "SCW.Config.xml";
SetupExecutedVar = true;
RefreshConfiguration();
}
}
public static void Setup(string configFileName)
{
lock (ConfigHubLock)
{
//Initialize config with specified file
WorkingDirectoryVar = Environment.CurrentDirectory;
ConfigFileNameVar = configFileName.Trim().ToLower().Replace(".xml", "") + ".xml";
SetupExecutedVar = true;
RefreshConfiguration();
}
}
#endregion
#region Merchant
public static void SetValue(string key, string value)
{
//Fail if setup hasn't been called
if (!SetupExecutedVar) throw ConfigHubException.BuildException(ConfigHubExceptionType.NotSetup, "Setup must be called before using the ConfigHub", null);
try
{
lock (ConfigHubLock)
{
//Set the value
bool foundNode = false;
foreach (XmlNode configNode in ConfigDocVar.ChildNodes[0].ChildNodes)
{
if (configNode.Name.Trim().ToLower() == key.Trim().ToLower())
{
configNode.InnerXml = value.Trim();
foundNode = true;
}
}
if (!foundNode)
{
XmlNode newNode = ConfigDocVar.CreateNode("element", key.Trim(), "");
newNode.InnerXml = value.Trim();
ConfigDocVar.ChildNodes[0].AppendChild(newNode);
}
//Save the config file
ConfigDocVar.Save(WorkingDirectoryVar + #"\" + ConfigFileNameVar);
RefreshConfiguration();
}
}
catch (Exception err)
{
throw ConfigHubException.BuildException(ConfigHubExceptionType.SetValue, "Set value failed", err);
}
}
public static string GetValue(string key)
{
//Fail if setup hasn't been called
if (!SetupExecutedVar) throw ConfigHubException.BuildException(ConfigHubExceptionType.NotSetup, "Setup must be called before using the ConfigHub", null);
if (AutoRefreshVar) RefreshConfiguration();
try
{
lock (ConfigHubLock)
{
//Get and return the value
if (AutoRefreshVar) RefreshConfiguration();
if (ConfigLookupPair.ContainsKey(key.Trim().ToLower()))
{
return ConfigLookupPair[key.Trim().ToLower()];
}
else
{
throw ConfigHubException.BuildException(ConfigHubExceptionType.NoKeyFound, "The key " + key + " was not found", null);
}
}
}
catch (Exception err)
{
throw ConfigHubException.BuildException(ConfigHubExceptionType.GetValue, "Get value failed", err);
}
}
public static void RefreshConfiguration()
{
//Fail if setup hasn't been called
if (!SetupExecutedVar) throw ConfigHubException.BuildException(ConfigHubExceptionType.NotSetup, "Setup must be called before using the ConfigHub", null);
try
{
//Load configuration from file
ConfigDocVar.Load(WorkingDirectoryVar + #"\" + ConfigFileNameVar);
List<string> duplicateCheck = new List<string>();
foreach (XmlNode configNode in ConfigDocVar.ChildNodes[0].ChildNodes)
{
if (configNode.Name.Trim().ToLower() == CommentNameVar)
{
//Ignore the Comment
}
else
{
if (duplicateCheck.Contains(configNode.Name.Trim().ToLower()))
{
//Duplicate key failure
throw ConfigHubException.BuildException(ConfigHubExceptionType.DuplicateKey, "The key " + configNode.Name.Trim() + " appears multiple times", null);
}
else
{
//Add configuration key value pair
duplicateCheck.Add(configNode.Name.Trim().ToLower());
if (!ConfigLookupPair.ContainsKey(configNode.Name.Trim().ToLower()))
{
ConfigLookupPair.Add(configNode.Name.Trim().ToLower(), configNode.InnerXml.Trim());
}
else
{
ConfigLookupPair[configNode.Name.Trim().ToLower()] = configNode.InnerXml.Trim();
}
}
}
}
}
catch (Exception err)
{
//Look form root missing and multiple roots
if (err.ToString().ToLower().Contains("root element is missing"))
{
throw ConfigHubException.BuildException(ConfigHubExceptionType.NoRootFound, "No configuration root found", err);
}
else if (err.ToString().ToLower().Contains("multiple root elements"))
{
throw ConfigHubException.BuildException(ConfigHubExceptionType.MultipleRoots, "Multiple configuration roots found", err);
}
else
{
throw ConfigHubException.BuildException(ConfigHubExceptionType.Refresh, "Refresh failed", err);
}
}
}
#endregion
}
#region Exception
public enum ConfigHubExceptionType { NotSetup, Setup, Refresh, DuplicateKey, NoKeyFound, SetValue, GetValue, NoRootFound, MultipleRoots }
public class ConfigHubException : Exception
{
public ConfigHubException(ConfigHubExceptionType errType, string message) : base("#" + errType.ToString() + "-" + message + (ConfigHub.ConfigFilePath != #"\" ? " (" + ConfigHub.ConfigFilePath + ")" : "")) { }
public ConfigHubException(ConfigHubExceptionType errType, string message, Exception innerException) : base("#" + errType.ToString() + "-" + message + (ConfigHub.ConfigFilePath != #"\" ? " (" + ConfigHub.ConfigFilePath + ")" : ""), innerException) { }
public static ConfigHubException BuildException(ConfigHubExceptionType exceptionType, string message, Exception innerException)
{
if (!ConfigHub.Verbose || innerException == null) return new ConfigHubException(exceptionType, message);
else return new ConfigHubException(exceptionType, message, innerException);
}
}
#endregion
Note. You need to build the file in your project first and remove the XML header. Like the following (Ex: MyConfigFile.xml) - NO xml tag at the top like (). Finally, make sure you set your xml config file to copy always:
<Config>
<ValueName>Value</ValueName>
</Config>
I have a requirement to dynamically load and cast an instance of a class to it's various base types using requirement. Now on reading and trying my hand on some examples, I find that either I probably don't understand all that I need to when it comes to working with classes at runtime.
I have a situation where Abstract class B implements interface A. Now Class B is a base class for class C. When I dynamically load, at runtime, the assembly that contains all 3 types, I expect that I should be able to, using Load From context, load the assembly, create an instance of class C, and cast it to type of interface A. But that does not seem to be happening at all and I would appreciate an explanation as to why. Thanks in Advance.
http://msdn.microsoft.com/en-us/library/2xkww633.aspx
http://msdn.microsoft.com/en-us/library/1009fa28.aspx
public interface ICaseOutputGenerator
{
String SampleProperty { get; set; }
void Process();
}
public abstract class CaseOutputGeneratorBase : ICaseOutputGenerator
{
public String SecondSampleProperty { get; set; }
public virtual void SecondProcessMethod()
{
}
public abstract void ThirdSampleProcessMethod();
public string SampleProperty
{
get;
set;
}
public void Process()
{
Console.WriteLine("Process in CaseOutputGeneratorBase Called");
}
}
public class TestCaseOutputGenerator : CaseOutputGeneratorBase
{
public override void ThirdSampleProcessMethod()
{
throw new NotImplementedException();
}
}
//////////////////////////////////////////////////////////////////////////////
public class TestSandBoxManager
{
public TestSandBoxManager()
{
}
public String ProcessAssemblyByFullDisplayName(String assemblyFullDisplayName)
{
String temp = String.Empty;
var casecust = GetAssemblyByFullDisplayName(assemblyFullDisplayName);
if (casecust != null)
temp = ("Cast Passed");
else
temp = ("Cast Failed");
return temp;
}
public String ProcessFile(String assemblyName, String className)
{
String temp = String.Empty;
var casecust = GetCaseOutputGeneratorObject(assemblyName, className);
if (casecust != null)
temp=("Cast Passed");
else
temp=("Cast Failed");
return temp;
}
private static object GetAssemblyByFullDisplayName(string fullName)
{
try
{
Type caseOutputGen = null;
String fullQualifiedName = String.Empty;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if ( testType.FullName != fullName)
continue;
fullQualifiedName = testType.FullName;
break;
}
if (fullQualifiedName == null)
return null;
var obj = Activator.CreateInstance(Type.GetType(fullQualifiedName));
return obj;
}
catch (Exception ex)
{
throw ex;
}
}
public String ProcessFile2(String assemblyName, String className)
{
String temp = String.Empty;
var casecust = GetCaseOutputGeneratorObjectLoadFrom(assemblyName, className);
if (casecust != null)
temp = ("Cast Passed");
else
temp = ("Cast Failed");
return temp;
}
public static ICaseOutputGenerator GetCaseOutputGeneratorObject(string assemblyName, string className)
{
ICaseOutputGenerator caseOutputGen = null;
var obj = GetObject(assemblyName, className);
if (obj != null)
caseOutputGen = (ICaseOutputGenerator)obj; // FAILS HERE
return caseOutputGen;
}
public static ICaseOutputGenerator GetCaseOutputGeneratorObjectLoadFrom(string assemblyName, string className)
{
ICaseOutputGenerator caseOutputGen = null;
try
{
var obj = GetObject2(assemblyName, className);
if (obj != null)
caseOutputGen = (ICaseOutputGenerator)obj; // FAILS HERE
}
catch (Exception ex)
{
throw ex;
}
return caseOutputGen;
}
private static object GetObject2(string fullName, string className)
{
try
{
Type caseOutputGen = null;
String fullQualifiedName = String.Empty;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if (!testType.FullName.EndsWith(className, StringComparison.InvariantCultureIgnoreCase))
continue;
caseOutputGen = testType;
fullQualifiedName = testType.FullName;
break;
}
if (caseOutputGen == null)
return null;
var obj = Activator.CreateInstanceFrom(fullName, fullQualifiedName);
return obj.Unwrap();
}
catch (Exception ex)
{
throw ex;
}
}
private static object GetObject(string fullName, string className)
{
try
{
Type caseOutputGen = null;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if (!testType.FullName.EndsWith(className, StringComparison.InvariantCultureIgnoreCase)) continue;
caseOutputGen = testType;
break;
}
if (caseOutputGen == null) return null;
var obj = Activator.CreateInstance(caseOutputGen);
return obj;
}
catch (FileNotFoundException ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
catch (Exception ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
}
}
//////////////////////////////////////////////////////////////////////////////
public Boolean testReflection2()
{
try
{
//create an instance of the testsandboxmanager
TestSandBoxManager t = new TestSandBoxManager();
String ret = t.ProcessFile2(#"...\Documents\visual studio 2012\Projects\TestSandBox\TestSandBox\bin\Debug\TestSandBox.dll", "TestCaseOutputGenerator");
Console.WriteLine(ret);
Console.ReadLine();
return true;
}
catch (Exception)
{
return false;
}
}
Most likely you have 2 ICaseOutputGenerator - one in each assembly. You can't cast object/interface to similarly named interface in another assembly even if code is identical. You can check the fact that create object implements ICaseOutputGenerator from its own assembly by looking at created object in the debugger.
If it is the case you need to figure out where you want to put ICaseOutputGenerator interface so it is coming from the same assembly for both "custom loaded assembly" and you main application. Often shared interfaces are implemented in separate assembly and linked to all "plug-in" assemblies as well as application assembly.
I think that Alexei Levenkov is spot on. You load your TestSandBox.dll twice. Once as a reference to your project and second time via Assembly.LoadFrom. As per documentation that you yourself linking this can result in unexpected behaviour. Here is a quote for you reference:
If an assembly is loaded with LoadFrom, and the probing path includes
an assembly with the same identity but a different location, an
InvalidCastException, MissingMethodException, or other unexpected
behavior can occur.
This is exactly what's happening in your case. If you change the path you are loading your assembly from to point to the same folder as your main executable, it will work fine.
I am trying to fully understand how I can simplify the following:
public ActionResult Create(string ds) {
InitializeServices(ds, "0000");
vm.Account = new Account {
PartitionKey = "0000",
RowKey = "0000",
Created = DateTime.Now,
CreatedBy = User.Identity.Name
};
}
catch (ServiceException ex) {
ModelState.Merge(ex.Errors);
}
catch (Exception e) {
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
return View("CreateEdit", vm);
}
I had a few great answers and the following was suggested:
private void HandleException(Action action) {
try {
action();
}
catch (ServiceException ex) {
ModelState.Merge(ex.Errors);
}
catch (Exception e)
{
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
}
RunAndHandleExceptions(new Action(() =>
{
//Do some computing }
));
This looks like a really great solution but I still don't understand how I can pass in my
parameters into the action. What I need to do is to pass in the following:
string ds
System.Web.Mvc.ModelState ModelState (passed as a reference)
Just
HandleException(() => someFunction(ds, ModeState));
should do it
To get the return value, you need a Func<>, not Action<>:
private TR HandleException<TR>(Func<TR> action)
{
try
{
return action();
}
catch (ServiceException ex)
{
ModelState.Merge(ex.Errors);
}
catch (Exception e)
{
Trace.Write(e);
ModelState.AddModelError("", "Database access error: " + e.Message);
}
return default(TR); // null for reference types
}
You then would use it, e.g. without an existing function:
bool result = HandleException(() =>
{
if (string.IsNullOrEmpty(ds))
return false;
// do interesting stuff that throws many kinds of exceptions :)
// Note: freely use ds and ModelState from surrounding scope,
// no need to 'pass them'
return true;
});
Have you looked at RedirectToAction?
return this.RedirectToAction(c => c.SomeAction(MyParam));
You can define an action with up to 16 parameters (no discussion, if that number is useful, please). So, sour call could look like:
private void HandleException(Action<string, System.Web.Mvc.ModelState ModelState > action) {
Edit
Here is an example with an action having parameter:
private void RunHandleException(Action<int> action)
{
action(someIntValue);
}
...
RunAndHandleExceptions((someInt) =>
{
//Do some computing
});
And here is an example with a function having a return value:
private void RunHandleException(Func<bool, int> action)
{
bool returnValue = action(someIntValue);
}
...
RunAndHandleExceptions((someInt) =>
{
//Do some computing
return true;
});