I have base interface for my Metadata interfaces and Attributes.
public interface IBase
{
string Name { get; }
}
public interface IAAAMetaData : IBase
{
string[] Names { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Method)]
public class AAAMetaData : ExportAttribute, IAAAMetaData
{
public AAAMetaData(string contract)
{
Name = contract;
}
public AAAMetaData(string[] contracts)
{
Names = contracts;
}
public string Name { get; set; }
public string[] Names { get; set; }
}
public interface IBBBMetaData : IBase
{
string[] Names { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Method)]
public class BBBMetaData : ExportAttribute, IBBBMetaData
{
public BBBMetaData(string contract)
{
Name = contract;
}
public BBBMetaData(string[] contracts)
{
Names = contracts;
}
public string Name { get; set; }
public string[] Names { get; set; }
}
Here is my plugins:
[AAAMetaData("Test1")]
public void Plugin1(object sender, EventArgs e)
{
sender = "Plugin1";
}
[BBBMetaData("Test2")]
public void Plugin2(object sender, EventArgs e)
{
sender = "Plugin2";
}
Now when I try get exports I am getting wrong result. Here is the code that I am using to get exports:
var exports = _container.GetExports<Action<object, EventArgs>, IAAAMetaData>();
In the result of GetExprts<T>() I am getting two items in the list. If I open the list and see the items inside it imported Plugin2 also. What is wrong in here? IAAAMetaData and IBMetaData are totally different things. You can't even cast IAAAMetaData to IBBBMetaData. Can anyone explain what is going on in here?
Thanks for the help!
The reason behind this is that both metadata interfaces have exactly the same properties. Same type and name for each property. If for example you change your interfaces to:
public interface IAAAMetaData : IBase
{
string[] AAA_Names { get; }
}
public interface IBBBMetaData : IBase
{
string[] BBB_Names { get; }
}
you will get the single export that you expect.
This is kind of explained in the Metadata filtering and DefaultValueAttribute section of Exports and Metadata (CodePlex MEF site):
When you specifiy a metadata view, an implicit filtering will occur to
match only those exports which contain the metadata properties defined
in the view.
Of course this wouldn't happen if the signature of the exported methods were different. Try adding an extra variable and you will get the single export. Also another approach would be to use contract names with ExportAttribute and ImportAttribute.
I wasn't aware that there was a method with multiple generic parameters, but since you're not getting a compiler error I assume there is indeed one. However, the second argument is probably not a contract type. Contracts in MEF are specified by name. Thus, try this:
var exports = _container.GetExports<Action<object, EventArgs>>( "Test1" );
IMO IAAAMetaData and IBBBMetaData are not that different. They are actually identical.
The proof - GetExports retrieves two items of the same INNNMetaData type, depending on what the input was IAAAMetaData or IBBBMetaData. Because of the same base interface, you could actually write:
var exports = container.GetExports<Action<object, EventArgs>, IBase>();
and you will get two items of type IBase => Test1 and Test2.
So using your code, the only solution that i found was using the contract name:
var exports = container.GetExports<Action<object, EventArgs>, IAAAMetaData>().FirstOrDefault(iaaaa => iaaaa.Metadata.Name == "Test1");
In this case, the interface parameter for GetExports is irrelevant.
Related
I have two objects, lets call them A and B.
Each contain the following property:
[IgnoreDataMember]
public string SalesforceId { get; set; }
Then I have another two objects, lets call them UpdatedA and UpdatedB, which respectively extend A and B, and include nothing but:
[DataMember(Name = "sf__Id")]
public new string SalesforceId { get; set; }
[DataMember(Name = "sf__Created")]
public bool SalesforceCreated { get; set; }
The reason for this is so that I can use ServiceStack to convert A and B to CSV files and then use it again to convert CSV files from Salesforce back to C# Objects (If I don't ignore SalesforceId, the upload to Salesforce Bulk API 2.0 will fail).
So, the first question part of this question is do I really need to create two separate classes for UpdatedA and UpdatedB, as these classes are nearly identical and are actually both poltergeists, because I only use them in the following two methods:
private Dictionary<string, A> Update(Dictionary<string, A> aByExternalIds, RelayerContext context) {
IConfiguration config = context.Config;
string url = $"{config["SalesforceInstanceBaseUrl"]}/services/data/{config["SalesforceVersion"]}/jobs/ingest/{context.job.Id}/successfulResults";
this.restClient.Get(url, context.token)
.FromCsv<List<UploadedA>>()
.ForEach((updatedA) => {
if (aByExternalIds.TryGetValue(updatedA.ExternalId, out A oldA)) {
oldA.SalesforceId = updatedA.SalesforceId;
}
});
return aByExternalIds;
}
private Dictionary<string, B> Update(Dictionary<string, B> bBySalesforceAId, RelayerContext context) {
IConfiguration config = context.Config;
string url = $"{config["SalesforceInstanceBaseUrl"]}/services/data/{config["SalesforceVersion"]}/jobs/ingest/{context.job.Id}/successfulResults";
this.restClient.Get(url, context.token)
.FromCsv<List<UploadedB>>()
.ForEach((updatedB) => {
if (bBySalesforceAId.TryGetValue(updatedB.A__c, out B oldB)) {
oldB.SalesforceId = updatedB.SalesforceId;
}
});
return bBySalesforceAId;
}
Which leads to the second part of this question.
Both of these questions are very similar. We can see that the inputs are mapped by different properties on A and B... so I think I could do something like create an interface:
public interface Identifiable {
public string getIdentifier();
}
which would could be used to return either updatedA.ExternalId or updatedB.A__c.
But I'm not sure what the method signature would look like if I'm using generics.
Also, if I don't know how I could handle FromCsv<List<UploadedA>>() and FromCsv<List<UploadedB>>() in a generic way (maybe passing in a function?)
Anyway, to sum up, what I'd like to do is reduce those these two methods to just one, and if I can remove one or both of those Uploaded classes, so much the better.
Any ideas?
How about something like this:
public interface IBase
{
string SalesforceId { get; set; }
}
public class A : IBase
{
public string SalesforceId { get; set; }
}
public class UploadedA : A
{
public new string SalesforceId {
get => base.SalesforceId;
set => base.SalesforceId = value; }
public bool SalesforceCreated { get; set; }
}
public static void Update<T, TU>(Dictionary<string, T> oldBySalesForceId, Func<TU, string> updatedId)
where TU : T
where T : IBase
{
// Call service and read csv to produce a list of uploaded objects...
// Substituting with an empty list in the example
var list = new List<TU>();
foreach (var updated in list)
{
if (oldBySalesForceId.TryGetValue(updatedId(updated), out var old))
{
old.SalesforceId = updated.SalesforceId;
}
}
}
I have removed some details that did not seem relevant for the example. This uses generics with constraints and a interface to ensure both the updated and old value has a SalesForceId.
I changed the derived class so that it uses the same SalesforceId as the base class, you could change it to virtual/override if you prefer, but it is probably not a good idea that the base and derived class both have independent properties with the same name since it will be confusing.
It uses a delegate to describe the id/key for UpdatedA/UpdatedB. You could use an interface instead if you prefer.
First of all apologize for long post nevertheless i wanted to highlight problem exactly and to be most readable and understandably. I am developing architecture of my program which will be responsible for files/databases data gather and face some architecture issues so far. All information step by step down below.
Let's consider following code below:
public interface IWatchService<TEntity> where TEntity : IEntity
{
IList<TEntity> MatchingEntries { get; set; }
}
public interface IWatchServiceDatabase<TEntity> : IWatchService<TEntity> where TEntity : IDatabaseEntity
{ }
public interface IWatchServiceFiles<TEntity> : IWatchService<TEntity> where TEntity : IFileEntity
{ }
class Database : IWatchServiceDatabase<DatabaseQuery>
{
public IList<DatabaseQuery> MatchingEntries { get; set; }
}
class Files : IWatchServiceFiles<CsvFile>
{
public IList<CsvFile> MatchingEntries { get; set; }
}
class Consumer
{
public IWatchService<IEntity> WatchService { get; set; }
public Consumer(IWatchService<IEntity> watchService)
{
WatchService = watchService;
var newList = WatchService.MatchingEntries;
}
public void AddNewEntries(IEntity entity) => WatchService.MatchingEntries.Add(entity);
}
class Program
{
static void Main(string[] args)
{
IWatchServiceDatabase<DatabaseQuery> db = new Database();
IWatchServiceFiles<CsvFile> filesCsv = new Files();
var dbConsumer = new Consumer(db); //cannot convert from 'IWatchServiceDatabase<DatabaseQuery>' to 'IWatchService<IEntity>'
var filesCsvConsumer = new Consumer(filesCsv); //cannot convert from 'IWatchServiceFiles<CsvFile>' to 'IWatchService<IEntity>'
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
}
}
public interface IEntity { }
public interface IFileEntity : IEntity
{
int Id { get; set; }
string Name { get; set; }
}
public interface IDatabaseEntity : IEntity { }
public class CsvFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class XmlFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DatabaseQuery : IDatabaseEntity { }
We have two errors there:
var dbConsumer = new Consumer(db);
var filesCsvConsumer = new Consumer(filesCsv);
Errors:
cannot convert from 'IWatchServiceDatabase' to 'IWatchService'
cannot convert from 'IWatchServiceFiles' to 'IWatchService'
This seems to be understandable because otherwise "we would be able" to add CsvFile or XmlFile to dbConsumer where generic IDatabaseEntity is expected and CsvFile and XmlFile are in fact IFileEntity and from the other hand DatabaseQuery to filesConsumer which expects IFileEntity and DatabaseQuery is IDatabaseEntity
//Database related
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
//Files related
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
From my understanding this is the clue why compiler raise those errors and which is fine. Therefore I've decided to overcome it in this way:
public interface IWatchService<out TEntity> where TEntity : IEntity
{
IEnumerable<TEntity> MatchingEntries { get; }
}
As can be seen i marked generic parameter out and changed IList to IEnumerable because IEnumerable can be only foreached. Without possibility to modify the list.
Now having this there is no possibility to modify MatchingEntries e.g Add() on therefore we are now not able to add e.g CsvFile (IFileEntity) where IDatabaseEntity is expected and vice versa DatabaseQuery (IDatabaseEntity) where IFileEntity is expected. Fine and understandably.
At the end i have two main questions:
What is the benefit to have this: IEnumerable MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore i would always get empty list when calling that property. Or i am in wrong? Can somebody explain showing based on my code what can be done with it?
Let's imagine i want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Many thanks for your support and hope someone benefit from it as i saw a lot of confusions related to that topic.
First question:
What is the benefit to have this: IEnumerable<T> MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore I would always get empty list when calling that property. Or I am in wrong? Can somebody explain showing based on my code what can be done with it?
I am confused by the question. The interface says that a class that implements that interface must have a getter of this name and type. It says nothing at all about the contents of that sequence:
interface IFoo<out T>
{
IEnumerable<T> Bar { get; }
}
Now we can implement that interface however we want:
class TigerFoo : IFoo<Tiger>
{
public IEnumerable<Tiger> Bar
{
get
{
return new List<Tiger>() { new Tiger("Tony"), new Tiger("Terry") };
}
}
}
So why you think the returned sequence must be empty, I do not understand.
Similarly, nothing is stopping you from making a class that implements a setter:
class GiraffeFoo : IFoo<Giraffe>
{
public IEnumerable<Giraffe> Bar { get; set; }
}
…
GiraffeFoo gf = new GiraffeFoo();
List<Giraffe> giraffes = new List<Giraffe>() { new Giraffe("Gerry") };
gf.Bar = giraffes;
Nothing stops you from changing the contents of the list:
class TurtleFoo : IFoo<Turtle>
{
private List<Turtle> turtles = new List<Turtle>();
public IEnumerable<Turtle> Bar => turtles;
public void AddATurtle() => turtles.Add(new Turtle("Tommy"));
}
It is a mystery to me why you think you cannot do any of these things. You want to add a member to the collection? Write a method that adds a member to the collection. You just can't put it in the interface if you wan the interface to be covariant. But the interface tells you what services you must provide, not what services you must not provide! I do not understand why you think that an interface tells you what a class cannot do.
Since T is marked as out, you can now use any of these covariantly:
IFoo<Animal> ia1 = new TigerFoo();
IFoo<Animal> ia2 = new GiraffeFoo();
IFoo<Animal> ia3 = new TurtleFoo();
Of course you don't get to use the methods of the class once it is in an interface, but you never get to use the methods of a class once something is in an interface.
Second question:
Let's imagine I want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Just write code that does that. I don't understand what the question is asking. Please clarify the question.
I have a problem with designing my interfaces
I have these interfaces :
interface IField {
}
interface IScreenField : IField {
}
interface ITable {
CustomCollection<IField> CustomCollection { get; set; }
}
interface IScreen
{
AnotherCustomCollection<IScreenField> AnotherCustomCollection { get; set; }
}
IScreen interface should inherit from ITable but it shows an error that I have to implement a collection of IField but I already implement a collection of IScreenField
What is the soltuion for this ?
I uploaded a sample project to explain the issue more
You can check the error message in Screen class that says :
Screen does not implement interface member ITable.Fields. Screen.Fields cannot implement ITable.Fields becuase it does not have the matching return type of CusomCollection<IField>
Here is the sample :
Sample project
This description of this example helps you to solve the problem: If IExample2 inherits another Interface, when implementing IExample2 u need to implement
all the method(properties etc...) that has been declared in interface + the method of inhered interfaces from IExample2. Remember that when you implement an interface you have to implement all of members of that interface (you have to implement even the members of all interfaces that are in chain) and all the returns types has to be the same in interface and in class.
interface IExample
{
void Method1();
}
interface IExample2 : IExample
{
void Method2();
}
class Screen : IExample2
{
public void Method2()
{
}
public void Method1()
{
}
}
Chain Example
interface IExample
{
void Method1();
}
interface IExample2 : IExample
{
void Method2();
}
interface IExample3 : IExample2
{
void Method3();
}
This is the answer I posted on your previous post. Reposting it here as-is since your previous question was put on hold before I could
submit the answer.
It’s very difficult to identify the problem without looking at the full code but based on what you have said, I believe, you have implemented IScreenField explicitly and the compiler is not able to find any implementation of IField.
Checkout following for more info:
https://www.codeproject.com/Articles/1000374/Explicit-Interface-VS-Implicit-Interface-in-Csharp
Update: After looking at the code
First of all you need to understand difference between Implicit and Explicit implementations of an Interface:
Implicit: you access the interface properties and properties as if they were part of the class.
Explicit: you can only access properties and properties when treating the class as the implemented interface.
The problem with the Screen class is that it implements IScreen interface, which in-turn implements ITable interface. Both these interfaces have a property named Fields.
The problem surfaced due to this and you are required to
explicitly implement the Fields property in Screen class.
NOTE: It is irrelevant that you have different return types. Since you have implemented Fields property in both interfaces, it is assumed that you are expecting different values when the property is accessed from each of the interfaces.
public class Screen : IScreen
{
public string Name { get; set; }
AnotherCustomCollection<IBaseField> IScreen.Fields
{
get
{
return default(AnotherCustomCollection<IBaseField>);
}
}
CustomCollection<IField> ITable.Fields
{
get
{
return default(CustomCollection<IField>);
}
}
public string Title { get; set; }
public string Description { get; set; }
}
Now how to access them? To access Fields property of each of these Interfaces you need to access Screen object as those interfaces.
Ex:
var screen = new Screen();
var fields = screen.Fields; // Error
var fields = (screen as IScreen).Fields; // returns property value of IScreen Fields
var fields = (screen as ITable).Fields; // returns property value of ITable Fields
Here is the complete code: https://dotnetfiddle.net/5KS0Xd
Hope this was helpful. All the best and happy coding.
You could do something like this:
public class Screen : IScreen
{
public string Name { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public AnotherCustomCollection<IBaseField> Fields { get; set; }
CustomCollection<IField> ITable.Fields
{
get
{
throw new System.NotSupportedException();
}
}
}
And apparently the compiler likes that, and if you do something like this:
public static void Main(string[] args)
{
var collection = new List<IScreen>()
{
new Screen
{
Fields = new AnotherCustomCollection<ScreenInterface.IBaseField>
{
new TextField()
{
Name = "Hello"
}
}
}
};
var y = collection.First();
//Prints "Hello"
Console.WriteLine(string.Join(" ", y.Fields.Select(x => x.Name)));
Console.ReadLine();
}
But, if you are working with the upper interface (ITable)
public static void Main(string[] args)
{
var collection = new List<ITable>() //here
{
new Screen
{
Fields = new AnotherCustomCollection<ScreenInterface.IBaseField>
{
new TextField()
{
Name = "Hello"
}
}
}
};
var y = collection.First();
//Throws NotSupportedException
Console.WriteLine(string.Join(" ", y.Fields.Select(x => x.Name)));
Console.ReadLine();
}
My guess is that there isn't the concept of generic inheritance, and that may be proved if you switch the conditional generic parameter of AnotherCustomCollection from IBaseField to IField, and instead of throwing the exception, return the public Fields property on Screen.ITable.Fields. Compiler will automatically recognize the concrete property and everything will work.
So, for this to work, either define an implicit operator or a custom getter:
public class Screen : IScreen
{
public string Name { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public AnotherCustomCollection<IBaseField> Fields { get; set; }
CustomCollection<IField> ITable.Fields
{
get
{
var customCollection = new CustomCollection<IField>();
customCollection.AddRange(Fields);
return customCollection;
}
}
}
I have been battling with this bit of code for a while now and I am trying to get a solution as it is literally the last part before it goes to testing.
I have the following interfaces and classes (simplified to the relevant parts):
public interface ITagParent<T> where T : ITag
{
List<TagAddOn<T>> TagCollection { get; set; }
}
public interface ITag
{
int Id { get; set; }
string Description { get; set; }
TagGroup TagGroup { get; set; }
}
public class TagAddOn<T> : ViewModelBase where T : ITag
{
private T _currentTag;
public T CurrentTag
{
get { return _currentTag; }
set { _currentTag = value; }
}
}
public partial class Customer : ITagParent<CustomerTag>
{
List<TagAddOn<CustomerTag>> _tagCollection;
public List<TagAddOn<CustomerTag>> TagCollection
{
get { return _tagCollection; }
set { _tagCollection = value; }
}
}
public partial class CustomerTag : ITag
{
public int Id { get; set; }
}
public class TagAddOnManager
{
public static string GetTagCurrentValue(List<TagAddOn<ITag>> dataObjectAddOns)
{
// LOTS OF SNIPPING!
return string.Empty;
}
}
I am trying to use the GetTagCurrentValue method in the TagAddOnManager class like this:
string value = TagAddOnManager.GetTagCurrentValue(
((ITagParent<ITag>)gridCell.Row.Data).TagCollection));
Everything compiles fine, but errors when trying to cast gridCell.Row.Data to ITagParent<ITag>. I understand this is due to covarience and a workaround (if not a terribly safe one) is to mark T in the ITagParent interface with the out keyword, but that won't work as you can see it is used in the TagCollection property, which can't be read only.
I tried casting the above to ITagParent<CustomerTag>, but this fails at compile time with a 'cannot convert' error when trying to feed it into my GetTagCurrentValue method.
Another option I considered is using some base classes instead of the ITagParent interface, but that won't work as the Customer object already inherits from another base class, which can't be modified for this implementation.
I know I could just overload the GetTagCurrentValue method with List<TagAddOn<CustomerTag>> as the parameter type and all other variations, but that really seems like a 'I give up' solution. I could probably use reflection to get the desired results, but that would be unwieldy and not very efficient, especially considering this method could be called a lot in a particular process.
So does anyone have any suggestions?
Could you use something like that
public class TagAddOnManager
{
public static string GetTagCurrentValue<TTag>(ITagParent<TTag> tagParent)
where TTag : ITag
{
// Just an example.
return tagParent.TagCollection.First().CurrentTag.Description;
}
}
and use it like that?`
var value = TagAddOnManager.GetTagCurrentValue((Customer)CustomergridCell.Row.Data);
I have been getting a lot of traction from a builder pattern as a public class member of another class:
public class Part
{
public class Builder
{
public string Name { get; set; }
public int Type { get; set; }
public Part Build()
{
return new Part(Name, Type);
}
}
protected Part(string name, int type)
{
...
}
}
Note protected constructor - I like how I HAVE to use the builder to get a Part. Calls to
Part p = new Part.Builder() { Name = "one", Type = 1 }.Build();
work great. What I would like to do is use this builder to serve up a special kind of part based on the Type (for example):
public class SpecialPart : Part
{
protected SpecialPart(string name, int type) : base(name, type) { }
}
And a slight change to the builder:
public Part Build()
{
if (Type == _some_number_)
return new SpecialPart(Name, Type);
return new Part(Name, Type);
}
But this doesn't work - Part.Builder can't see SpecialPart's protected constructor. How can I get Builder to work with descendents of Part and get the same must-have-a-builder semantics?
There are many ways to skin a cat, but the path of least resistance here is going to be making the constructors of your various part types public or internal.
You can't do it, except for putting them in their own assembly and use the internal access specifier.