C# Generic way to store unknown properties in List - c#

I'd like to store LicenseInformations for multiple domains in my application.
The structure looks the following way:
public class LicenseData
{
// properties...
public List<LicenseDomain> Domains { get; set; }
// other properties...
}
public class LicenseDomain
{
// properties...
public object LicenseConfig { get; set; }
}
We have multiple domains with total different properties, but the license may contain multiple configurations..
For example:
{
"MaxValidUsers": 5
}
{
"Property": "xy"
"SubProperty": { "Foo" : "Bar"
}
}
The generation is no problem in any way..
But if I restore the informations from my signed json file I deserialize to object..
Which pattern / possiblity I have to work with Interfaces / Abstracts / that I can (RE)store generic informations here..
Right now I hack with:
JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(domain.LicenseConfig))
But I can't agree with myself.

So, based on the pieces of context I can grab, I would actually recommend having your LicenseConfig stored as a JSON string, which would give you the ability to do something like this:
public class LicenseDomain
{
// properties...
// Depending on how this is loaded,
// this property (or at least its setter) could be made private/protected/internal
public string LicenseConfigJson { get; set; }
public T LicenseConfig<T>() where T : BaseLicenseConfig
{
if (string.IsNullOrWhiteSpace(LicenseConfigJson))
{
return null;
}
return JsonConvert.DeserializeObject<T>(LicenseConfigJson);
}
public void SaveLicenseConfig<T>(T config) where T : BaseLicenseConfig
{
if (config == null)
{
LicenseConfigJson = null;
}
else
{
LicenseConfigJson = JsonConvert.SerializeObject(config);
}
}
}
Or if each LicenseDomain can only have one type of LicenseConfig, you could make it a generic parameter to the class:
public class LicenseData
{
// properties...
public List<LicenseDomain<BaseLicenseConfig>> Domains { get; set; }
// other properties...
}
public class LicenseDomain<T> where T : BaseLicenseConfig
{
// properties...
// Depending on where this value comes from, you could do this a variety of ways,
//but this is just one
public string LicenseConfigJson { get; set; }
public T LicenseConfig
{
get
{
if (string.IsNullOrWhiteSpace(LicenseConfigJson))
{
return null;
}
return JsonConvert.DeserializeObject<T>(LicenseConfigJson);
}
set
{
if (value == null)
{
LicenseConfigJson = null;
}
else
{
LicenseConfigJson = JsonConvert.SerializeObject(value);
}
}
}
}
public abstract class BaseLicenseConfig
{
}
public class LicConfig1 : BaseLicenseConfig
{
public int MaxValidUsers { get; set;}
}
public class LicConfig2 : BaseLicenseConfig
{
public string Property {get;set;}
public SubProp SubProperty {get;set;}
}
public class SubProp
{
public string Foo {get;set;}
}
In both cases, the BaseLicenseConfig class is strictly to enforce that everything in the domain list can come from a base class of some kind. If that's not important, you don't need the base class and can remove the where T : BaseLicenseConfig from LicenseDomain class.

Related

Assign string[] array field to another array field of same type c#

I have two classes defined in my solution
public class Registration {
[...]
public list<Account> Accounts {get; set;}
}
public class Account {
[...]
public string Code { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
In the web service that I am consuming, the following class definitions are available
public partial class VendReg {
[...]
private Payment_Details[] requestDetailsField;
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Payment_Details[] RequestDetails {
get {
return this.requestDetailsField;
}
set {
this.requestDetailsField = value;
}
}
}
public partial class Payment_Details {
private string bk_CodeField;
private string bk_NameField;
private string bk_AddressField;
public string Bk_Code {
get {
return this.bk_CodeField;
}
set {
this.bk_CodeField = value;
}
}
public string Bk_Name {
get {
return this.bk_NameField;
}
set {
this.bk_NameField = value;
}
}
public string Bk_Address {
get {
return this.bk_AddressField;
}
set {
this.bk_AddressField = value;
}
}
}
I want to assign Account to Request Details which is an array of Payment_Details. I tried this code below
vendReg.RequestDetails = registration.Accounts.Cast<Payment_Details>().ToArray();
I got invalid cast exception: Unable to cast object of type 'Account' to type 'Payment_Details'
Please guide on what I am not doing right
You need to convert this yourself (or you can look into things like Automapper)
vendReg.RequestDetails = registration.Accounts.Select(acc =>
new Payment_Details {
Bk_Code = acc.Code,
Bk_Name = acc.Name,
Bk_Address = acc.Address
}).ToArray();

C# Print TableEntity Properties, but ignore those properties with attribute [IgnoreProperty]

I am trying to print out an object that implements TableEntity class, without those that should be Ignored regarding the persistence. The approach I generally use to print out objects is to use the StatePrinter.
public class MyEntity : TableEntity
{
public string MyProperty { get; set; }
[IgnoreProperty]
public string MyIgnoredProperty { get; set; }
public override string ToString()
{
Stateprinter printer = new Stateprinter();
return printer.PrintObject(this);
}
}
While this works pretty good for any kind of classes, with this MyEntity class it also prints the MyIgnoredProperty. Is there a clever way to also ignore the properties that have [IgnoredProperty] as attribute when printing out the object?
You can configure what fields/properties the Stateprinter cares about by configuring what "field harvester" to use.
Here's a simple field harvester that only returns public properties without the 'IgnoreProperty' attribute.
class PersistencePropertiesHarvester : IFieldHarvester
{
public bool CanHandleType(Type type)
{
return typeof(TableEntity).IsAssignableFrom(type);
}
public List<SanitizedFieldInfo> GetFields(Type type)
{
var fields = new HarvestHelper().GetFieldsAndProperties(type);
return fields.Where(IsPerstistenceProperty).ToList();
}
private static bool IsPerstistenceProperty(SanitizedFieldInfo field)
{
return
// Only return properties ...
field.FieldInfo.MemberType == MemberTypes.Property
&&
// ... that has a public get method ...
(field.FieldInfo as PropertyInfo)?.GetGetMethod(false) != null
&&
// ... that does not have the IgnoreProperty attribute
field.FieldInfo.GetCustomAttribute<IgnoreProperty>() == null
;
}
}
Then you use it like this:
public class MyEntity : TableEntity
{
public string MyProperty { get; set; }
[IgnoreProperty]
public string MyIgnoredProperty { get; set; }
public override string ToString()
{
Stateprinter printer = new Stateprinter();
printer.Configuration.Add(new PersistencePropertiesHarvester());
return printer.PrintObject(this);
}
}
And the result of new MyEntity().ToString() is now
new MyEntity()
{
MyProperty = null
}

Using abstraction and dependency injection, what if implementation-specific details need to be configurable in the UI?

I have an application that loads a list of client/matter numbers from an input file and displays them in a UI. These numbers are simple zero-padded numerical strings, like "02240/00106". Here is the ClientMatter class:
public class ClientMatter
{
public string ClientNumber { get; set; }
public string MatterNumber { get; set; }
}
I'm using MVVM, and it uses dependency injection with the composition root contained in the UI. There is an IMatterListLoader service interface where implementations represent mechanisms for loading the lists from different file types. For simplicity, let's say that only one implementation is used with the application, i.e. the application doesn't support more than one file type at present.
public interface IMatterListLoader
{
IReadOnlyCollection<string> MatterListFileExtensions { get; }
IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile);
}
Let's say in my initial version, I've chosen an MS Excel implementation to load the list of matters, like this:
I'd like to allow the user to configure at runtime the row and column numbers where the list starts, so the view might look like this:
And here's the MS Excel implementation of IMatterListLoader:
public sealed class ExcelMatterListLoader : IMatterListLoader
{
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load using StartRowNum and StartColNum
}
}
The row and column numbers are an implementation detail specific to MS Excel implementations, and the view model doesn't know about it. Nevertheless, MVVM dictates that I control view properties in the view model, so if I were to do that, it would be like this:
public sealed class MainViewModel
{
public string InputFilePath { get; set; }
// These two properties really don't belong
// here because they're implementation details
// specific to an MS Excel implementation of IMatterListLoader.
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public ICommandExecutor LoadClientMatterListCommand { get; }
public MainViewModel(IMatterListLoader matterListLoader)
{
// blah blah
}
}
Just for comparison, here's an ASCII text file based implementation that I might consider for the next version of the application:
public sealed class TextFileMatterListLoader : IMatterListLoader
{
public bool HasHeaderLine { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load tab-delimited client/matters from each line
// optionally skipping the header line.
}
}
Now I don't have the row and column numbers that the MS Excel implementation needed, but I have a Boolean flag indicating whether the client/matter numbers start on the first row (i.e. no header row) or start on the second row (i.e. with a header row).
I believe the view model should be unaware of the change between implementations of IMatterListLoader. How do I let the view model do its job controlling presentation concerns, but still keep certain implementation details unknown to it?
Here's the dependency diagram:
You'd need a seperate viewmodel for each type of file you intend to load.
Each viewmodel does the setup for its particular loader.
These viewmodels can then be passed in as dependencies to the main viewmodel, which calls load on each viewmodel when needed;
public interface ILoaderViewModel
{
IReadOnlyCollection<ClientMatter> Load();
}
public class ExcelMatterListLoaderViewModel : ILoaderViewModel
{
private readonly ExcelMatterListLoader loader;
public string InputFilePath { get; set; }
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public ExcelMatterListLoaderViewModel(ExcelMatterListLoader loader)
{
this.loader = loader;
}
IReadOnlyCollection<ClientMatter> Load()
{
// Stuff
loader.Load(fromFile);
}
}
public sealed class MainViewModel
{
private ExcelMatterListLoaderViewModel matterListLoaderViewModel;
public ObservableCollection<ClientMatter> ClientMatters
= new ObservableCollection<ClientMatter>();
public MainViewModel(ExcelMatterListLoaderViewModel matterListLoaderViewModel)
{
this.matterListLoaderViewModel = matterListLoaderViewModel;
}
public void LoadCommand()
{
var clientMatters = matterListLoaderViewModel.Load();
foreach (var matter in clientMatters)
{
ClientMatters.Add(matter)
}
}
}
As you add more types to the application, you'd create new view models and add those as dependencies.
You could have a function that constructs the UI elements based on the specific type of the interface.
public static void ConstructUI(IMatterListLoader loader) {
Type loaderType = loader.GetType();
// Do logic based on type
}
You could have classes for each of the IMatterListLoader implementations, which contains logic concerning the presentation. (You don't want to mix the UI presentation logic in with the IMatterListLoader implementations).
Based on the type of the loader, you use the correct class to generate the UI elements.
I would add a Draw() method to the IMatterListLoader interface. Your MainViewModel would then just call Draw() and the the actual IMatterListLoader will add whatever parameters are needed to the UI.
This is a bit conceptual as I'm not too familiar with WPF, so you might need to change the code to use UserControl's or something but the logic is the same.
For example, lets say you have AsciiMatterListLoader which requires no input from the client, then nothing will be shown in the MainViewModel. But if the ExcelMatterListLoader is loaded, the MainViewModel should add the necessary user inputs.
public sealed class AsciiMatterListLoader : IMatterListLoader
{
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load data with no parameters
}
public Panel Draw()
{
// Nothing needs to be drawn
return null;
}
}
public sealed class ExcelMatterListLoader : IMatterListLoader
{
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// load using StartRowNum and StartColNum
}
public Panel Draw()
{
Panel panelForUserParams = new Panel();
panelForUserParams.Height = 400;
panelForUserParams.Width = 200;
TextBox startRowTextBox = new TextBox();
startRowTextBox.Name = "startRowTextBox";
TextBox startColumnTextBox = new TextBox();
startColumnTextBox.Name = "startColumnTextBox";
panelForUserParams.Children().Add(startRowTextBox);
panelForUserParams.Children().Add(startColumnTextBox);
return panelForUserParams;
}
}
public sealed class MainViewModel
{
public string InputFilePath { get; set; }
public ICommandExecutor LoadClientMatterListCommand { get; }
public MainViewModel(IMatterListLoader matterListLoader)
{
var panel = matterListLoader.Draw();
if (panel != null)
{
// Your MainViewModel should have a dummy empty panel called "placeHolderPanelForChildPanel"
var parent = this.placeHolderPanelForChildPanel.Parent;
parent.Children.Remove(this.placeHolderPanelForChildPanel); // Remove the dummy panel
parent.Children.Add(panel); // Replace with new panel
}
}
}
You might need to use event handlers to pass the user input changes to the IMatterListLoader or maybe make IMatterListLoader a UserControl.
Edit
#rory.ap is right, the service layer shouldn't know about the UI components. Here is my adjusted answer where the IMatterListLoader just exposes the properties it needs by using a dictionary as a PropertyBag instead of telling the UI what to draw. This way the UI layer does all the UI work:
public interface IMatterListLoader
{
IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile);
IDictionary<string, object> Properties { get; }
void SetProperties(IDictionary<string, object> properties);
}
public sealed class AsciiMatterListLoader : IMatterListLoader
{
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
public IDictionary<string, object> Properties
{
get
{
return new Dictionary<string, object>(); // Don't need any parameters for ascii files
}
}
public void SetProperties(IDictionary<string, object> properties)
{
// Nothing to do
}
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
// Load without using any additional params
return null;
}
}
public sealed class ExcelMatterListLoader : IMatterListLoader
{
private const string StartRowNumParam = "StartRowNum";
private const string StartColNumParam = "StartColNum";
public uint StartRowNum { get; set; }
public uint StartColNum { get; set; }
public IReadOnlyCollection<string> MatterListFileExtensions { get; set; }
private bool havePropertiesBeenSet = false;
public IDictionary<string, object> Properties
{
get
{
var properties = new Dictionary<string, object>();
properties.Add(StartRowNumParam, (uint)0); // Give default UINT value so UI knows what type this property is
properties.Add(StartColNumParam, (uint)0); // Give default UINT value so UI knows what type this property is
return properties;
}
}
public void SetProperties(IDictionary<string, object> properties)
{
if (properties != null)
{
foreach(var property in properties)
{
switch(property.Key)
{
case StartRowNumParam:
this.StartRowNum = (uint)property.Value;
break;
case StartColNumParam:
this.StartColNum = (uint)property.Value;
break;
default:
break;
}
}
this.havePropertiesBeenSet = true;
}
else
throw new ArgumentNullException("properties");
}
public IReadOnlyCollection<ClientMatter> Load(FileInfo fromFile)
{
if (this.havePropertiesBeenSet)
{
// Load using StartRowNum and StartColNum
return null;
}
else
throw new Exception("Must call SetProperties() before calling Load()");
}
}
public sealed class MainViewModel
{
public string InputFilePath { get; set; }
public ICommandExecutor LoadClientMatterListCommand { get; }
private IMatterListLoader matterListLoader;
public MainViewModel(IMatterListLoader matterListLoader)
{
this.matterListLoader = matterListLoader;
if (matterListLoader != null && matterListLoader.Properties != null)
{
foreach(var prop in matterListLoader.Properties)
{
if (typeof(prop.Value) == typeof(DateTime))
{
// Draw DateTime picker for datetime value
this.placeHolderPanelForParams.Add(new DateTimePicker() { Name = prop.Key });
}
else
{
// Draw textbox for everything else
this.placeHolderPanelForParams.Add(new TextBox() { Name = prop.Key });
// You can also add validations to the input here (E.g. Dont allow negative numbers of prop is unsigned)
// ...
}
}
}
}
public void LoadFileButtonClick(object sender, EventArgs e)
{
//Get input params from UI
Dictionary<string, object> properties = new Dictionary<string, object>();
foreach(Control propertyControl in this.placeHolderPanelForParams().Children())
{
if (propertyControl is TextBox)
properties.Add(propertyControl.Name, ((TextBox)propertyControl).Text);
else if (propertyControl is DateTimePicker)
properties.Add(propertyControl.Name, ((DateTimePicker)propertyControl).Value);
}
this.matterListLoader.SetProperties(properties);
this.matterListLoader.Load(null); //Ready to load
}
}
Not sure why nobody suggested property attributes and reflection
Just create a new Attribute, eg:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ExposeToViewAttribute : Attribute
{
public string Name { get; set; }
public ExposeToViewAttribute([System.Runtime.CompilerServices.CallerMemberName]string name = "")
{
this.Name = name;
}
}
and make sure it gets added in your view
var t = matterListLoader.GetType();
var props = t.GetProperties().Where((p) => p.GetCustomAttributes(typeof(ExposeToViewAttribute), false).Any());
foreach(var prop in props)
{
var att = prop.GetCustomAttributes(typeof(ExposeToViewAttribute), true).First() as ExposeToViewAttribute;
//Add to view
}
approach will not get much cleaner.
Usage then would be as simple as:
[ExposeToView]
public int Something { get; set; }
[ExposeToView("some name")]
public int OtherFieldWithCustomNameThen { get; set; }
If you use something like WPF however, there are other solutions that may work better for you.

Casting to Interface (Dynamic casting at runtime)

I have 3 classes Media,Image, Info. All three classes contain List of files implementing IResourceModel interface. These Media, Image & InfoFile are serialized and added to a Dictionary. I am looping on Dictionary, at runtime how can I cast these objects (Media, Image & InfoFile) to IResourceModel and fetch Files property.
public interface IResourceModel<T> {
T Files { get; set; }
}
class Media : IResourceModel<MediaFiles>
{
public MediaFiles Files { get; set; }
}
class Image : IResourceModel<ImagesFiles>
{
public ImagesFiles Files{ get; set; }
}
class InfoFiles : IResourceModel<InfoFiles>
{
public InfoFiles Files{ get; set; }
}
Dictionary<ResourceType, object> resourcesList = new Dictionary<ResourceType, object> {
{ ResourceType.Media,Media},
{ ResourceType.Image,Image},
{ ResourceType.InfoFiles , InfoFile}
};
The problem with your type hierarchy is that there is no IResourceModel. There is only a IResourceModel<T> where T is of a concrete type. With generic types, IResourceModel<A> and IResourceModel<B> are two discrete types for any different type argument A and B, that actually share nothing except the similar name. So you simply cannot cast Media, Image and InfoFiles into a common type (other than object) because there is no common type.
This is usually solved by introducing another non-generic type that is additionally implemented. For example, types implementing IEnumerable<T> also implement a non-generic IEnumerable to allow a non-generic iteration.
So your type hierarchy could look like this:
public interface IResourceModel
{
IFiles Files { get; }
}
public interface IResourceModel<T> : IResourceModel
{
new T Files { get; set; }
}
public class Media : IResourceModel<MediaFiles>
{
public MediaFiles Files { get; set; }
IFiles IResourceModel.Files => Files;
}
public class Image : IResourceModel<ImagesFiles>
{
public ImagesFiles Files { get; set; }
IFiles IResourceModel.Files => Files;
}
public class Info : IResourceModel<InfoFiles>
{
public InfoFiles Files { get; set; }
IFiles IResourceModel.Files => Files;
}
public interface IFiles {}
public class MediaFiles : IFiles { }
public class ImagesFiles : IFiles { }
public class InfoFiles : IFiles { }
Now you could cast your types into IResourceModel and access the Files property to iterate over all IFiles.
You will need to define a comment interface / class for you ImageFiles, InfoFiles and MediaFiles. Once example below.
interface IFiles
{
string myField { get; set; }
int myHash { get; set; }
}
class MediaFiles : IFiles
{
public string myField { get; set; }
public int myHash { get; set; }
}
Then you can iterate your dictionary by something like the following
Dictionary<ResourceType, IResourceModel<IFiles>> resourcesList = new Dictionary<ResourceType, IResourceModel<IFiles>> {
{ ResourceType.Media, new Media {Files = new MediaFiles {myHash = 20203, myField = "MediaFiles"} } }
};
foreach (KeyValuePair<ResourceType, IResourceModel<IFiles>> entry in resourcesList)
{
var files = entry.Value.Files;
var field = files.myField;
var hash = files.myHash;
}
Edit
Theoretically you can use dynamic instead of object to access what you want, i.e. File. But I would always do something like the above, to always have strong types enforced.
Edit 2
To cast generic interface, another interface implementation is needed.
public class Media : IResourceModel<MediaFiles>, IResourceModel<IFiles>
{
public MediaFiles Files { get; set; }
IFiles IResourceModel<IFiles>.Files
{
get { return Files; }
set { Files = (MediaFiles)value; } // <--- Cautious! Check type in production code.
}
}
Edit 3
Rethink of the fundamental problem you met, i.e. Why would you want a dictionary resourcesList? i.e. shouldn't your resource give you the type it belongs to, rather than you keep a mapping for them? This drives me to give the following complete re-implementation.
ResourceType is a property of Files.
Firstly, what you want to achieve at the end
var listOfFiles = new List<IFiles> // <-- List rather than Dictionary
{
new ImagesFiles(
new List<ImagesFile>
{
new ImagesFile {path = "C:\\wf.n"},
new ImagesFile {path = "C:\\wfz.n"}
}
),
new MediaFiles(
new List<MediaFile>
{
new MediaFile {path = "C:\\wf.jpg", foo = 1},
new MediaFile {path = "C:\\wfz.png", foo = 2}
}
)
};
foreach (var files in listOfFiles)
{
Console.WriteLine(files.resourceType); // <-- Gives Media / Image
var fileshash = files.GetHashCode();
foreach (IFile file in files.GetFiles())
{
var myPath = file.path;
var hash = file.GetHashCode();
}
}
Interface definitions
public interface IFile
{
string path { get; set; } // <--- GetHashCode doesn't need to be in here.
}
public interface IFiles : IEnumerable<IFile>
{
IEnumerable<IFile> GetFiles();
ResourceType resourceType { get; } // <-- Getter only here on the interface
}
public interface IFiles<out T> : IFiles
where T : IFile // <-- This will enforce the same file type in this collection
{
new IEnumerable<T> GetFiles();
}
Class definitions
public class ImageFile : IFile
{
public string path { get; set; }
public override int GetHashCode()
{
return path.GetHashCode();
}
}
public class ImageFiles : IFiles<ImageFile>
{
public ImageFiles(IEnumerable<ImageFile> files)
{
this.files = files.ToList();
}
public bool mySpecialProperty { get; set; } // <--- ImageFiles special, Not in Media nor Image
public ResourceType resourceType => ResourceType.Image;
private List<ImageFile> files;
public IEnumerable<IFile> GetFiles()
{
return files;
}
IEnumerable<ImageFile> IFiles<ImageFile>.GetFiles()
{
return files;
}
public IEnumerator<IFile> GetEnumerator()
{
return files.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public override int GetHashCode()
{
return files.GetHashCode();
}
}

MEF Import Scenario

Hi
I have some problems in import scenarios example:
[Export(typeof(IICon))]
public class WriteInputData : IICon
{
[Import(typeof(IIOWriter))]
public IIOWriter IOWriter { get; set; }
public object Input { get; set; }
public void Process()
{
IOWriter.Write(Input);
}
}
Then i hawe two classes that implement interface IIOWriter like :
[Export(typeof(IIOWriter))]
public class FileWriter : IIOWriter
{
public string FilePath { get; set; }
public void Write(object data)
{
if (string.IsNullOrEmpty(FilePath))
FilePath = #"c:\test.txt";
var fl = new StreamWriter(FilePath, true);
fl.Write((string)data);
fl.Flush();
fl.Close();
}
public string Name
{
get { return "FileWriter"; }
}
}
[Export(typeof(IIOWriter))]
public class ConsoleWrite : IIOWriter
{
public void Write(object data)
{
Console.WriteLine((string)data);
}
public string Name
{
get { return "ConsoleWrite"; }
}
}
How can i let that to user so he can change that in runtime, so example whene he type select in ListBox FileWriter than the IIOWriter in WriteInputData will be injected FileWriter end so one..
Sorry for my bad english.
You probably need to supply some metadata to the export, such like:
[Export(typeof(IIOWriter)),
ExportMetadata("Name", "ConsoleWriter")]
public class ConsoleWriter : IIOWriter
{
}
The reason you need to do this, is that you need to know ahead of time what the user selection will match to. Because of this, you may want to refactor your design to remove the dependency on the IOWriter property:
[Export(typeof(IICon))]
public class WriteInputData : IICon
{
public object Input { get; set; }
public void Process(IIOWriter writer)
{
}
}
If you define your Process method to take in an instance, we can resolve it using the CompositionContainer. Firstly, define a metadata interface that matches your ExportMetadata value:
public interface INamedMetadata
{
string Name { get; }
}
And then, we can resolve the instance:
public IIOWriter GetWriter(string name)
{
return container
.GetExports<IIOWriter, INamedMetadata>()
.Where(e => e.Metadata.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
.Select(e => e.Value)
.FirstOrDefault();
}
Hope that points you in the right direction....

Categories