Pass Exception and Subclasses over NamedPipe - c#

I'm trying to pass objects of type Exception (or one of it's subclasses) over a NamedPipe.
ServiceContract:
[ServiceContract]
public interface IWCFCallback
{
[OperationContract]
void sendException(Exception e);
}
it works fine when i use it like this:
_pipeproxy.sendException(new Exception("bla bla 99"));
but as soon as I pass a subclass:
_pipeproxy.sendException(new ArgumentException("fridgemaster 3000"));
I get a Exception, saying deserialisation failed.
I already read about the KnownTypes Attribute, but i can't figure out how to use it for classes not implemented by myself.
Can someone give me a hint here?

One of the "Best practices" over WCF, is Do not serialize Exception.
If your ServiceHost is throwing exception, then you are suppose to use FaultException.
One of the reason why exception is not safe to transfer, is that Exception itself is serializable, but you can derive from it, and who guarantee that your custom derived exception will be serialable.
You could pass a Data contract object with the exception stack as string and type as enum, as a work-around.

This may not be according to the best practices but you can create a DataContract that represents the Exception - something like this:
/// <summary>
/// Represents errors that occur during application execution.
/// </summary>
[DataContract]
public class ExceptionInfo
{
/// <summary>
/// Gets the type of the exception.
/// </summary>
[DataMember]
public string ExceptionType
{
get;
set;
}
/// <summary>
/// Gets a message that describes the current exception.
/// </summary>
/// <returns>
/// The error message that explains the reason for the exception, or an empty string("").
/// </returns>
[DataMember]
public string Message
{
get;
set;
}
/// <summary>
/// Gets the <see cref="T:System.Exception"/> instance that caused the current exception.
/// </summary>
/// <returns>
/// An instance of Exception that describes the error that caused the current exception. The InnerException property returns the same value as was passed into the constructor, or a null reference (Nothing in Visual Basic) if the inner exception value was not supplied to the constructor. This property is read-only.
/// </returns>
[DataMember]
public ExceptionInfo InnerException
{
get;
set;
}
/// <summary>
/// Gets a string representation of the immediate frames on the call stack.
/// </summary>
/// <returns>
/// A string that describes the immediate frames of the call stack.
/// </returns>
[DataMember]
public string StackTrace
{
get;
set;
}
/// <summary>
/// Gets or sets a link to the help file associated with this exception.
/// </summary>
/// <returns>
/// The Uniform Resource Name (URN) or Uniform Resource Locator (URL).
/// </returns>
[DataMember]
public string HelpLink
{
get;
set;
}
/// <summary>
/// Gets or sets the name of the application or the object that causes the error.
/// </summary>
[DataMember]
public string Source
{
get;
set;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionInfo"/> class.
/// </summary>
/// <param name="exception">The exception.</param>
/// <exception cref="ArgumentNullException">exception</exception>
public ExceptionInfo(Exception exception)
{
if(exception == null)
throw new ArgumentNullException("exception");
ExceptionType = exception.GetType().FullName;
HelpLink = exception.HelpLink;
Message = exception.Message;
Source = exception.Source;
StackTrace = exception.StackTrace;
if(exception.InnerException != null)
{
InnerException = new ExceptionInfo(exception.InnerException);
}
}
}
Service Contract:
[ServiceContract]
public interface IWCFCallback
{
[OperationContract]
void sendException(ExceptionInfo e);
}
Usage:
try
{
// .....
}
catch (Exception ex)
{
var info = new ExceptionInfo(ex);
// do something....
}

Related

Avoid non-readonly static fields - Immutability NDepend

I am using NDepend for code analysis and got this warning:
https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1901#!
This rule warns about static fields that are not declared as read-only.
In Object-Oriented-Programming the natural artifact to hold states that can be modified is instance fields. Such mutable static fields create confusion about the expected state at runtime and impairs the code testability since the same mutable state is re-used for each test.
My code is as follows:
using Cosmonaut;
using Microsoft.Azure.Documents.Client;
using System.Configuration;
using LuloWebApi.Entities;
namespace LuloWebApi.Components
{
/// <summary>
/// Main class that encapsulates the creation of instances to connecto to Cosmos DB
/// </summary>
public sealed class CosmosStoreHolder
{
/// <summary>
/// Property to be initiated only once in the constructor (singleton)
/// </summary>
private static CosmosStoreHolder instance = null;
/// <summary>
/// To block multiple instance creation
/// </summary>
private static readonly object padlock = new object();
/// <summary>
/// CosmosStore object to get tenants information
/// </summary>
public Cosmonaut.ICosmosStore<SharepointTenant> CosmosStoreTenant { get; }
/// <summary>
/// CosmosStore object to get site collection information
/// </summary>
public Cosmonaut.ICosmosStore<SiteCollection> CosmosStoreSiteCollection { get; }
/// <summary>
/// CosmosStore object to get page templates information
/// </summary>
public Cosmonaut.ICosmosStore<PageTemplate> CosmosStorePageTemplate { get; }
/// <summary>
/// CosmosStore object to get pages information
/// </summary>
public Cosmonaut.ICosmosStore<Page> CosmosStorePage { get; }
/// <summary>
/// CosmosStore object to get roles information
/// </summary>
public Cosmonaut.ICosmosStore<Role> CosmosStoreRole { get; }
/// <summary>
/// CosmosStore object to get clients information
/// </summary>
public Cosmonaut.ICosmosStore<Client> CosmosStoreClient { get; }
/// <summary>
/// CosmosStore object to get users information
/// </summary>
public Cosmonaut.ICosmosStore<User> CosmosStoreUser { get; }
/// <summary>
/// CosmosStore object to get partners information
/// </summary>
public Cosmonaut.ICosmosStore<Partner> CosmosStorePartner { get; }
/// <summary>
/// CosmosStore object to get super administrators information
/// </summary>
public Cosmonaut.ICosmosStore<SuperAdministrator> CosmosStoreSuperAdministrator { get; }
/// <summary>
/// Constructor
/// </summary>
CosmosStoreHolder()
{
CosmosStoreSettings settings = new Cosmonaut.CosmosStoreSettings(ConfigurationManager.AppSettings["database"].ToString(),
ConfigurationManager.AppSettings["endpoint"].ToString(),
ConfigurationManager.AppSettings["authKey"].ToString());
settings.ConnectionPolicy = new ConnectionPolicy
{
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp
};
CosmosStoreTenant = new CosmosStore<SharepointTenant>(settings);
CosmosStoreSiteCollection = new CosmosStore<SiteCollection>(settings);
CosmosStorePageTemplate = new CosmosStore<PageTemplate>(settings);
CosmosStorePage = new CosmosStore<Page>(settings);
CosmosStoreRole = new CosmosStore<Role>(settings);
CosmosStoreClient = new CosmosStore<Client>(settings);
CosmosStoreUser = new CosmosStore<User>(settings);
CosmosStorePartner = new CosmosStore<Partner>(settings);
CosmosStoreSuperAdministrator = new CosmosStore<SuperAdministrator>(settings);
}
/// <summary>
/// Instance access, singleton
/// </summary>
public static CosmosStoreHolder Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new CosmosStoreHolder();
}
return instance;
}
}
}
}
}
However I am not sure how to fix this warning.
This is a guide, not a hard rule. Usually, non-readonly static fields are hard to intuit about. But in this case you're doing lazy deferred loading, so... a lock and mutate is indeed one way of achieving that, without causing it to be loaded prematurely.
So a pragmatic fix is: just ignore/override the warning
Another approach, however, is to move the field to another type where it is readonly, and rely on the deferred .cctor semantics:
public static CosmosStoreHolder Instance {
[MethodImpl(MethodImplOptions.NoInlining)]
get => DeferredHolder.Instance;
}
private static class DeferredHolder {
internal static readonly CosmosStoreHolder Instance = new CosmosStoreHolder();
}
Then you don't even need the lock semantics (.cctor deals with that for you).

How do I create an interactive Powershell instance from C#?

I have a Powershell script that requires user interaction. I can call powershell.exe from C# using System.Diagnostics.Process and pass the scripts path as a parameter but I would like the script to be an embedded resource of the project. I tried creating a Runspace (see below) and running the script but because the script requires user interaction I receive an exception.
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "mynamespace.myscriptfile.ps1";
string result = "";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
Console.WriteLine(result);
}
//Create Powershell Runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
// Create pipeline and add commands
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(result);
// Execute Script
Collection<PSObject> results = new Collection<PSObject>();
try
{
results = pipeline.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
runspace.Close();
Console.ReadKey();
Is there a way to either pass the embedded resource to powershell.exe using System.Diagnostics.Process or is there a way to Invoke the script from C# where the user can interact?
UPDATE:
It seems to me that I may be able to use an implementation of the abstract class PSHost along with using the PSHostUserInterface property correctly, I may be able to create a Runspace that takes the PSHost implementation as a parameter to use the native Powershell console. I have been trying to test the idea but I'm not quite sure how to implement the abstract class.
Below is a sample of code that I obtained from Microsoft. I am confused with a couple of things. If it matters I will be creating the Runspace in a console application with a namespace called: WebRequirements in the Program class.
private Host01 program; (Would Host01 be Program?)
PSHostUserInterface (Is this where I would dictate that I want to use a native Powershell host and if so how would I do that?)
internal class MyHost : PSHost
{
///
/// A reference to the PSHost implementation.
///
private Host01 program;
/// <summary>
/// The culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalCultureInfo =
System.Threading.Thread.CurrentThread.CurrentCulture;
/// <summary>
/// The UI culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalUICultureInfo =
System.Threading.Thread.CurrentThread.CurrentUICulture;
/// <summary>
/// The identifier of this PSHost implementation.
/// </summary>
private Guid myId = Guid.NewGuid();
/// <summary>
/// Initializes a new instance of the MyHost class. Keep
/// a reference to the host application object so that it
/// can be informed of when to exit.
/// </summary>
/// <param name="program">
/// A reference to the host application object.
/// </param>
public MyHost(Host01 program)
{
this.program = program;
}
/// <summary>
/// Return the culture information to use. This implementation
/// returns a snapshot of the culture information of the thread
/// that created this object.
/// </summary>
public override System.Globalization.CultureInfo CurrentCulture
{
get { return this.originalCultureInfo; }
}
/// <summary>
/// Return the UI culture information to use. This implementation
/// returns a snapshot of the UI culture information of the thread
/// that created this object.
/// </summary>
public override System.Globalization.CultureInfo CurrentUICulture
{
get { return this.originalUICultureInfo; }
}
/// <summary>
/// This implementation always returns the GUID allocated at
/// instantiation time.
/// </summary>
public override Guid InstanceId
{
get { return this.myId; }
}
/// <summary>
/// Return a string that contains the name of the host implementation.
/// Keep in mind that this string may be used by script writers to
/// identify when your host is being used.
/// </summary>
public override string Name
{
get { return "MySampleConsoleHostImplementation"; }
}
/// <summary>
/// This sample does not implement a PSHostUserInterface component so
/// this property simply returns null.
/// </summary>
public override PSHostUserInterface UI
{
get { return null; }
}
/// <summary>
/// Return the version object for this application. Typically this
/// should match the version resource in the application.
/// </summary>
public override Version Version
{
get { return new Version(1, 0, 0, 0); }
}
/// <summary>
/// Not implemented by this example class. The call fails with
/// a NotImplementedException exception.
/// </summary>
public override void EnterNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// Not implemented by this example class. The call fails
/// with a NotImplementedException exception.
/// </summary>
public override void ExitNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API is called before an external application process is
/// started. Typically it is used to save state so the parent can
/// restore state that has been modified by a child process (after
/// the child exits). In this example, this functionality is not
/// needed so the method returns nothing.
/// </summary>
public override void NotifyBeginApplication()
{
return;
}
/// <summary>
/// This API is called after an external application process finishes.
/// Typically it is used to restore state that a child process may
/// have altered. In this example, this functionality is not
/// needed so the method returns nothing.
/// </summary>
public override void NotifyEndApplication()
{
return;
}
/// <summary>
/// Indicate to the host application that exit has
/// been requested. Pass the exit code that the host
/// application should use when exiting the process.
/// </summary>
/// <param name="exitCode">The exit code to use.</param>
public override void SetShouldExit(int exitCode)
{
this.program.ShouldExit = true;
this.program.ExitCode = exitCode;
}
}

Customise the system Exception instance to pass two parameters

I need to customise the system exception without using the user defined exception classes.
Following is my requirement.
String ErrorMessage="";
Exception e= new Exception (ErrorMessage);
then I need to throw errorMessage string with one integer parameter for the upper layer in my project.
So can someone please let me know if I can customise the system Exception instance to pass two parameters (Integer value and errorMessage string)?
Some real example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ExceptionExamples
{
public static class Error
{
[Serializable]
public class RegistryReadException : Exception
{
/// <summary>
/// Message template
/// </summary>
static string msg = "Can't parse value from Windows Registry.\nKey: {0}\nValue: {1}\nExpected type: {2}";
/// <summary>
/// Can't read value from registry
/// </summary>
/// <param name="message"></param>
public RegistryReadException(
string message)
: base(message) { }
/// <summary>
/// Can't read value from registry
/// </summary>
/// <param name="key">Key path</param>
/// <param name="value">Real value</param>
/// <param name="type">Expected type</param>
public RegistryReadException(
string key, string value, Type type)
: base( string.Format(msg, key, value, type.Name) ) { }
}
}
}
This is simply not possible.
Your only option is to create a new class that inherits from System.Exception or some other exception class and add whatever you need in there.
If you can't or won't do that, then you can't do what you want.
You can create some custom Exception classes and throw it using the same code TryCatch. But in the upper layer you can catch not by generic Exception, using your custom type.
Try
{
//Your try code Here
if(/*Something Happens*/)
{
throw new YourCustomExceptionClass("Message");
}
else
{
throw new AnotherCustomExceptionClass("Other Message");
}
}
Catch(YourCustomExceptionClass err)
{
//This will be a type of exception
}
Catch(AnotherCustomExceptionClass err)
{
//This will be another type of Exception
}

How to store multiple value types in a property?

I have an events class that I'm creating, that currently looks like the following:
public class SharePointOnErrorEventsArgs : EventArgs
{
public SharePointOnErrorEventsArgs(string message, bool showException, Exception exception)
{
Message = message;
Exception = exception;
ShowException = showException;
}
/// <summary>
/// Property to allow the storage of a more verbose and explainable error message
/// </summary>
public string Message { get; private set; }
/// <summary>
/// Object to store full exception information within
/// </summary>
public Exception Exception { get; private set; }
/// <summary>
/// Boolean value allows for verbose messages to be sent up the stack without
/// the need for displaying a full exception object, or stack trace.
/// </summary>
public bool ShowException { get; private set; }
}
Now, instead of sending true or false for showException I'd like to send one of three values Debug, Info or Error - how can I tackle something like this? I don't really want to use a string as I want to always restrict this to one of those three values, but I'm unsure how to approach this when using properties.
You can use an enum:
public enum ShowExceptionLevel
{
Debug,
Info,
Error
}
So your class will be:
public class SharePointOnErrorEventsArgs : EventArgs
{
public enum ShowExceptionLevel
{
Debug,
Info,
Error
}
public SharePointOnErrorEventsArgs(string message, ShowExceptionLevel showExceptionLevel, Exception exception)
{
Message = message;
Exception = exception;
ShowException = showException;
}
/// <summary>
/// Property to allow the storage of a more verbose and explainable error message
/// </summary>
public string Message { get; private set; }
/// <summary>
/// Object to store full exception information within
/// </summary>
public Exception Exception { get; private set; }
/// <summary>
/// Boolean value allows for verbose messages to be sent up the stack without
/// the need for displaying a full exception object, or stack trace.
/// </summary>
public ShowExceptionLevel ShowException { get; private set; }
}

WCF Known Type from System.Object in Config

I'm trying to specify a known type in my config, but I'm having problems with the fact that it derives from Object. I can make it work specifying the known type via attribute. But in this case I need to make it work from the config.
Here's an example. The following works fine:
[ServiceContract]
[ServiceKnownType(typeof(MyData))]
public interface IContract
{
[OperationContract]
void Send(object data);
}
[DataContract]
public class MyData
{
[DataMember]
public string Message { get; set; }
}
But if I remove the ServiceKnownType attribute and put the following in the config:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<knownType type="WpfApplication1.MyData, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
I get a ConfigurationErrorsException with the message "The value for the property 'type' is not valid. The error is: The type System.Object cannot be used as a declared type in config."
Is there anyway to make this work via config?
The answer turns out to be it's not possible to do what I want to do in the config file alone. The config above corresponds to the [KnownType] attribute used on DataContracts. There appears to be no way to implement [ServiceKnownType] in the config.
An alternate approach is to use [ServiceKnownType(methodName, type)] attribute with a custom configuration section. The new config looks like this:
<configuration>
<configSections>
<section
name="serviceKnownTypes"
type="WpfApplication1.ServiceKnownTypesSection, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</configSections>
<serviceKnownTypes>
<declaredServices>
<serviceContract type="WpfApplication1.IContract, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<knownTypes>
<knownType type="WpfApplication1.MyData, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</knownTypes>
</serviceContract>
</declaredServices>
</serviceKnownTypes>
</configuration>
The contracts:
[ServiceContract]
[ServiceKnownType("GetServiceKnownTypes", typeof(KnownTypeHelper))]
public interface IContract
{
[OperationContract]
void Send(object data);
}
[DataContract]
public class MyData
{
[DataMember]
public string Message { get; set; }
}
The helper class that contains the callback that returns the list of known types
public static class KnownTypeHelper
{
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
{
List<Type> result = new List<Type>();
ServiceKnownTypesSection serviceKnownTypes = (ServiceKnownTypesSection)ConfigurationManager.GetSection("serviceKnownTypes");
DeclaredServiceElement service = serviceKnownTypes.Services[((Type)(provider)).AssemblyQualifiedName];
foreach (ServiceKnownTypeElement knownType in service.KnownTypes)
{
result.Add(knownType.Type);
}
return result;
}
}
Information on creating custom config sections can be found here:
http://msdn.microsoft.com/en-us/library/2tw134k3.aspx
http://msdn.microsoft.com/en-us/library/system.configuration.configurationcollectionattribute.aspx
I'm not sure if it is by design, but the KnownTypeHelper below won't throw an error if you've not declared a service contract with known types. (i.e. its optional to add known types to service contracts).
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
/// <summary>
/// Helper for finding the known types for Wcf Services from a configuration file.
/// </summary>
public static class KnownTypeHelper
{
/// <summary>
/// Gets the known types for the service from a configuration file.
/// </summary>
/// <param name="provider">
/// The provider.
/// </param>
/// <returns>
/// The known types for the service from a configuration file.
/// </returns>
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
{
var result = new List<Type>();
var serviceKnownTypes = (ServiceKnownTypesSection)ConfigurationManager.GetSection("serviceKnownTypes");
if (serviceKnownTypes != null)
{
var service = serviceKnownTypes.Services[((Type)provider).AssemblyQualifiedName];
if (service != null)
{
foreach (ServiceKnownTypeElement knownType in service.KnownTypes)
{
result.Add(knownType.Type);
}
}
}
return result;
}
}
To save someone else the trouble of creating the configuration classes,
Note: There is no validation of the assembly qualified type names. If someone wants to add the appropiate attributes to do this, please do.
using System.Configuration;
/// <summary>
/// Section for configuration known types for services.
/// </summary>
public class ServiceKnownTypesSection : ConfigurationSection
{
/// <summary>
/// Gets services.
/// </summary>
[ConfigurationProperty("declaredServices", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(DeclaredServiceElement), AddItemName = "serviceContract", CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)]
public DeclaredServiceElementCollection Services
{
get
{
return (DeclaredServiceElementCollection)base["declaredServices"];
}
}
}
/// <summary>
/// Collection of declared service elements.
/// </summary>
public class DeclaredServiceElementCollection : ConfigurationElementCollection
{
/// <summary>
/// Gets the service for which known types have been declared for.
/// </summary>
/// <param name="key">
/// The key of the service.
/// </param>
public new DeclaredServiceElement this[string key]
{
get
{
return (DeclaredServiceElement)BaseGet(key);
}
set
{
var element = BaseGet(key);
var index = this.BaseIndexOf(element);
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
/// <summary>
/// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </summary>
/// <returns>
/// A new <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
protected override ConfigurationElement CreateNewElement()
{
return new DeclaredServiceElement();
}
/// <summary>
/// Gets the element key for a specified configuration element when overridden in a derived class.
/// </summary>
/// <returns>
/// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
/// <param name="element">
/// The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.
/// </param>
protected override object GetElementKey(ConfigurationElement element)
{
return ((DeclaredServiceElement)element).Type;
}
}
/// <summary>
/// The service for which known types are being declared for.
/// </summary>
public class DeclaredServiceElement : ConfigurationElement
{
/// <summary>
/// Gets or sets Type.
/// </summary>
[ConfigurationProperty("type", IsRequired = true, IsKey = true)]
public string Type
{
get
{
return (string) this["type"];
}
set
{
this["type"] = value;
}
}
/// <summary>
/// Gets KnownTypes.
/// </summary>
[ConfigurationProperty("knownTypes", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(DeclaredServiceElement), AddItemName = "knownType", CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)]
public ServiceKnownTypeElementCollection KnownTypes
{
get
{
return (ServiceKnownTypeElementCollection)base["knownTypes"];
}
}
}
/// <summary>
/// A collection of known type elements.
/// </summary>
public class ServiceKnownTypeElementCollection : ConfigurationElementCollection
{
/// <summary>
/// Gets an known type with the specified key.
/// </summary>
/// <param name="key">
/// The key of the known type.
/// </param>
public new ServiceKnownTypeElement this[string key]
{
get
{
return (ServiceKnownTypeElement)BaseGet(key);
}
set
{
var element = BaseGet(key);
var index = this.BaseIndexOf(element);
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
/// <summary>
/// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </summary>
/// <returns>
/// A new <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
protected override ConfigurationElement CreateNewElement()
{
return new ServiceKnownTypeElement();
}
/// <summary>
/// Gets the element key for a specified configuration element when overridden in a derived class.
/// </summary>
/// <returns>
/// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
/// <param name="element">
/// The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.
/// </param>
protected override object GetElementKey(ConfigurationElement element)
{
return ((ServiceKnownTypeElement)element).Type;
}
}
/// <summary>
/// Configuration element for a known type to associate with a service.
/// </summary>
public class ServiceKnownTypeElement : ConfigurationElement
{
/// <summary>
/// Gets or sets TypeString.
/// </summary>
[ConfigurationProperty("type", IsRequired = true, IsKey = true)]
public string TypeString
{
get
{
return (string)this["type"];
}
set
{
this["type"] = value;
}
}
/// <summary>
/// Gets or sets Type.
/// </summary>
public Type Type
{
get
{
return Type.GetType(this.TypeString);
}
set
{
this["type"] = value.AssemblyQualifiedName;
}
}
}

Categories

Resources