I have a .NET Core C# app that is referencing several third-party libraries. Each of these libraries has an object that I need to use in my app. There's one catch, my database has two properties associated with each of the properties in each of the classes I need in the third-party libraries. Those two properties are Name and Description.
In an ideal world, each of these third-party libraries would use my base class that has Name and Description :). Since that's not going to happen, I was wondering if there was a way in .NET Core to dynamically create a new type, and add properties from another type using Reflection.
I've seen examples of creating a new type via Roslyn using a string of Text. However, I don't want to use a string of text. I'd rather define a type using pure code. Then, I'd like to use Reflection to 1) get this list of the properties on the third-party class and 2) add them to my new type.
I know how to get a list of properties from a Type using Reflection. However, I'm not sure how to use Rosalyn to 1) Create a new type and 2) add new properties to that type. Is that even possible in the .NET Core world? I know it was in .NET pre-.NET Core days. But, I can't seem to figure out how to do it in .NET Core.
Let's say you have this third party class:
class SomeThirdPartyClass
{
public object Foo { get; set; }
public object Bar { get; set; }
}
If you need to add properties, just subclass it and add them:
class MyClass : SomeThirdPartyClass
{
public string Name { get; set; }
public string Description { get; set; }
}
//Example use
var o = new MyClass();
o.Name = "Name"; //Custom property
o.Foo = new object(); //Base property
If the third party class is sealed, you can wrap it instead.
class MyClass<T>
{
public string Name { get; set; }
public string Description { get; set; }
public T Item { get; set; }
}
//Example use
var o = new MyClass<SomeThirdPartyClass>
{
Item = new SomeThirdPartyClass()
};
o.Name = "name"; //Custom property
o.Item.Foo = new object(); //Base property
Related
I wrote a PCL with custom objects in there, and then I create a GUI that handles the objects from the PCL... and I try to use PropertyGrid to edit the properties... I've already read that in order for the grid to know what to do with the object, I need to specify the EditorAttribute as well as providing a TypeConverter... but I don't think I could add those 2 in the PCL...
Is there a way to handle this at the GUI level, like telling the PropertyGrid to use a specific type of Editor/TypeConverter at runtime? I went through the list of available function/properties of the grid and doesn't look like is possible.
You can create a metadata class containing same properties as your original class and decorate properties of metadata class with some attributes. Then tell the type descriptor to use the metadata class for providing type description for your original class:
var provider = new AssociatedMetadataTypeTypeDescriptionProvider(
typeof(MyClass),
typeof(MyClassMetadata));
TypeDescriptor.AddProvider(provider, typeof(MyPortableClass));
The PropertyGrid control uses the type descriptor of your class to show the properties of your class, their display name, their description, their editor and so on. You can assign the type descriptor in different ways.
In your case, the best solution is registering a new TypeDescriptorProvider for your class at run-time. This way you can change the appearance of your class in PropertyGrid simply at run-time.
Using AssociatedMetadataTypeTypeDescriptionProvider you can create a type descriptor provider for your class that uses a metadata class to provide type description. Then you can register the provider using TypeDescriptor.AddProvider.
This way you can introduce a metadata class for your class that contains attributes for properties.
Step by Step Example
Add a portable class library to solution and add a class to it:
public class MyClass
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
Add reference of the portable class library to your Windows Forms project. Just make sure that the target framework is consistent.
Add System.Design and System.ComponentModel.DataAnnotations references to your Windows Forms Project.
In Windows Forms Project, add a meta data class for your portable class. The class should contain exactly the same properties of your original class:
public class MyClassMetadata
{
[Category("My Properties")]
[DisplayName("First Property")]
[Description("This is the first Property.")]
public string Property1 { get; set; }
[Category("My Properties")]
[DisplayName("Second Property")]
[Description("This is the second Property.")]
[Editor(typeof(MultilineStringEditor), typeof(UITypeEditor))]
public string Property2 { get; set; }
}
You need to add these usings:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.Design;
using System.Drawing.Design;
In Load event of your form, register the metadata provider for your type this way:
var provider = new AssociatedMetadataTypeTypeDescriptionProvider(
typeof(MyClass),
typeof(MyClassMetadata));
TypeDescriptor.AddProvider(provider, typeof(MyClass));
Show an instance of your portable class in property grid:
var myObject = new MyClass();
this.propertyGrid1.SelectedObject = myObject ;
Here is the result after running the application:
I'd like to create a wrapper class dynamically, such that for every desired class (probably underneath a certain namespace like DBO) I'll get an appropriate class like this:
public class [ClassName]Wrapper{
public [ClassName] [ClassName] { get; set; }
}
Second, I need to if based on wrapper vs original type. I'm assuming I can just do something like:
(classBob as Type).ToString().EndsWith("Wrapper")
If I require anything more, please help me out :).
I'm fairly new to reflection and I've never built a class at runtime. Code to do this would be great, but even pointing out excellent resources to study up on the tools used to do this would be a great move forward for me.
Thanks!
Wouldn't using generics solve your problem?
public class Wrapper<T>
where T : class
{
public Wrapper(T wrappee)
{
Class = wrappee;
}
public T Class { get; } // C# 6.0 Syntax, otherwise add "private set;"
}
Then you can create a wrapper at runtime with
Type typeToBeWrapped = objToBeWrapped.GetType();
Type genericWrapper = typeof(Wrapper<>);
Type constructedWrapper = genericWrapper.MakeGenericType(typeToBeWrapped);
object obj = Activator.CreateInstance(constructedWrapper, objToBeWrapped);
I'm looking for anyone that can identify if this is possible or not, I am currently writing a small but extensible application that will have a plugin architecture.
To ensure the application is applicably to the current application, I'm using a custom Attribute to attach to the assembly
[AttributeUsage(AttributeTargets.Assembly)]
class PluginAttributes: Attribute
{
public PluginAttributes(string name, string description, string version, string author)
{
Name = name;
Description = description;
Version = version;
Author = author;
}
public string Name { get; private set; }
public string Description { get; private set; }
public string Version { get; private set; }
public string Author { get; private set; }
public XXXXXX Implements { get; private set; }
}
What I aiming for, is that the 'Implements' property will identify the appropriate plugin's Interface type. I have numerous interfaces defined in the library, such as ICompress, IEncrypt, ILogging, etc.
The attribute will be used to classify which type of plugin it is, without having to create a separate attribute for each.
I know I could have it passed as a string and use reflection, or use an enum, but I want the code to be as low-maintenance as possible, so that plugins can be developed without touching the core of the application.
I have made various of this type of plugin mechanism, and often it is easier to create an instance of the plugin class and check against various interfaces than to define it on a attribute or so. This will leave the door open for a plugin to implement more than one interface, which is nice.
You can do that like this using reflection (assuming you know the type name):
object plugin = Activator.CreateInstance("Assembly.Name", "Type.Class.Name");
if (plugin is ILogging)
{ ... }
if (plugin is IEncrypt)
{ ... }
For your current code, I suggest you to make the type of your Implements property Type and fill it using typeof(ILogging), typeof(IEncrypt), etc.
Is it somehow possible to extend a type, wich is defined in another assembly, to add an attribute on one of its properties?
Exemple I have in assembly FooBar:
public class Foo
{
public string Bar { get; set; }
}
But in my UI assembly, I want to pass this type to a third party tool, and for this third party tool to work correctly I need the Bar property to have a specific attribute. This attribute is defined in the third party assembly, and I don't want a reference to this assembly in my FooBar assembly, since FooBar contains my domain an this is a UI tool.
You can't, if the thirdy-party tool uses standard reflection to get the attributes for your type.
You can, if the third-party tool uses the TypeDescriptor API to get the attributes for your type.
Sample code for the type descriptor case:
public class Foo
{
public string Bar { get; set; }
}
class FooMetadata
{
[Display(Name = "Bar")]
public string Bar { get; set; }
}
static void Main(string[] args)
{
PropertyDescriptorCollection properties;
AssociatedMetadataTypeTypeDescriptionProvider typeDescriptionProvider;
properties = TypeDescriptor.GetProperties(typeof(Foo));
Console.WriteLine(properties[0].Attributes.Count); // Prints X
typeDescriptionProvider = new AssociatedMetadataTypeTypeDescriptionProvider(
typeof(Foo),
typeof(FooMetadata));
TypeDescriptor.AddProviderTransparent(typeDescriptionProvider, typeof(Foo));
properties = TypeDescriptor.GetProperties(typeof(Foo));
Console.WriteLine(properties[0].Attributes.Count); // Prints X+1
}
If you run this code you'll see that last console write prints plus one attribute because the Display attribute is now also being considered.
No. It's not possible to add attributes to types from separate assemblies.
What you can do, though, is create your own type that wraps the third-party type. Since you have full control over your wrapper class, you can add the attributes there.
What about:
public class Foo
{
public virtual string Bar
}
public class MyFoo : Foo
{
[yourcustomattribute]
public overrides string Bar
}
What I think you need is some kind of adapter layer that will not let that infrastructure dependency leak into your domain logic. Maybe you can create an adapter class that will be like a data transfer object to the other technology. This class lives in an integration assembly that has a dependency on the 3rd party library:
public class FooDTO {
[TheirAttribute]
public string Bar { get; set; }
}
You can then use something like AutoMapper to ease the pain of changing representations.
The ideal solution, though, is if the 3rd party library supported additional ways to provide metadata about their operations. Maybe you can ask them for this feature.
I have multiple Linq2Sql Classes such as "Article" "NewsItem" "Product".
They all have a title, they all have a unique ID and they all have a Summary.
So, I created an interface called IContent
public interface IContent {
int Id { get; set; }
String Title { get; set; }
String Summary { get; set; }
String HyperLink { get; set; }
}
In my code, I'm trying to make it possible to pass a List<T> that implements IContent and then use those common properties that I have implemented in each of the partial classes in my project.
So, just to clarify
Article is a Linq Entity.
I create a partial class and implement IContent Here's a snippet of Article.cs:
#region IContent Members
public int Id {
get {
return this.ArticleID;
}
set {
this.ArticleID = value;
}
}
Pretty Simple. In my code I'm trying to this, but I don't know where I'm going wrong:
List<IContent> items;
MyDataContext cms = new MyDataContext();
items = cms.GetArticles();
// ERROR: Can not implicitly convert List<Article> to List<IContent>
If my Article class implement IContent why can't I pass in Articles? I don't know what the objects that are going to be passed in.
I know I can do this with Inheriting a base class, but using LinqToSQL doesn't use regular objects.
I'm sure it's something simple that I'm missing.
This is because the List class and interface aren't covariant. If you're using .NET 4.0 and C# 4.0, you can actually use covariance on the IEnumerable<> interface, which should be okay for LINQ applications.
There's a good FAQ covering those topics.
Have you tried
items = cms.GetArticles().Cast<IContent>().ToList();
This is classic case that calls for co-variance in C# 4.0. You only have to change List<Article> to IEnumerable<Article> to make this assignemnt work:
IEnumerable<IContent> articles = myContext.GetArticles();
If you are stuck with .NET 3.5 you can just use Linq Cast<T>():
IEnumerable<IContent> articles = myContext.GetArticles().Cast<IContent>();