ImportMany only Metadata from MEF - c#

I have a custom Metadata for MEF which is declared as below
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class ,AllowMultiple = false)]
public class ModuleAttribute : ExportAttribute ,IModuleMetadata
{
public ModuleAttribute(string contract)
: base(typeof(IScreen))
{
Name = contract;
Region = Regions.Sidebar;
IsVisible = true;
}
public string Name { get; set; }
public string Module { get; set; }
public Regions Region { get; set; }
public string DisplayName { get; set; }
public bool IsVisible { get; set; }
public string Description { get; set; }
public short Order { get; set; }
public short Permissions { get; set; }
}
As of now, I import using ImportingConstructor
private readonly IEnumerable<Lazy<IScreen, IModuleMetadata>> _modules;
[ImportingConstructor]
public SidebarViewModel([ImportMany] IEnumerable<Lazy<IScreen, IModuleMetadata>> modules)
{
_modules= modules;
}
This gives me both the Object and Metadata using Lazy<>.
The problem here is, all class that I export using MEF are NonShared, and I want to create a new object every time. But LazyObject.Value give me the same instance everytime.
Is there a way in MEF, where I can only Import all the Metadata and then using ExportProvider, I can get the new ViewModal object.

Related

How to ignore object property on .GET but allow it on .POST?

I have the following object in my c# web api project:
public class InfoUtente
{
public string nome { get; set; }
public string cognome { get; set; }
public string cellulare { get; set; }
public string email { get; set; }
public bool termini { get; set; }
public string fidelity { get; set; }
public InfoUtente() { }
public InfoUtente(string nome, string cognome, string cellulare, string email, string fidelity)
{
this.nome = nome;
this.cognome = cognome;
this.cellulare = cellulare;
this.email = email;
this.fidelity = fidelity;
}
}
When a .POST call is made to the server InfoUtente is posted and in this case i should ignore the fidelity property and allow termini
while when the user request the data with a .GET request i should return the whole object with fidelity but without termini.
I'm using the default serializator (JSON.NET), and i've tryed to use JsonIgnore on termini and in a .GET call it returns the right json without termini but if i try to make a .POST of InfoUtente with JsonIgnore on termini property, it will be ignored in .POST too and it's value will be set to false in any case..
So i should be able to .POST a JSON with
nome,cognome,cellulare,email,termini
and be able to .GET it with
nome,cognome,cellulare,email,fidelity
Should i make two different objects one with termini and without fidelity to use in .POST and another without termini and with fidelity to .GET or i could achieve it by using only one object?
Creating two different classes is indeed the best solution.
You might want to have a look into Data Transfer Objects since they are also meant to achieve exactly what you want to do.
You would then have two classes, in addition to your InfoUtente entity. One for getting information about the particular instance you're interested in :
public class InfoUtenteDto
{
public string nome { get; set; }
public string cognome { get; set; }
public string cellulare { get; set; }
public string email { get; set; }
public string fidelity { get; set; }
}
And another one, for creating a new instance :
public class InfoUtenteForCreationDto
{
public string nome { get; set; }
public string cognome { get; set; }
public string cellulare { get; set; }
public string email { get; set; }
public bool termini { get; set; }
}
One of the possible ways is two interfaces with a custom Contract Resolver class. Assuming in addition to your class we have
public interface InfoUtentePost
{
public string nome { get; set; }
public string cognome { get; set; }
public string cellulare { get; set; }
public string email { get; set; }
public bool termini { get; set; }
}
public interface InfoUtentGet
{
public string nome { get; set; }
public string cognome { get; set; }
public string cellulare { get; set; }
public string email { get; set; }
public string fidelity { get; set; }
}
Let's create a contract resolver
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type _InterfaceType;
public InterfaceContractResolver(Type InterfaceType)
{
_InterfaceType = InterfaceType;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
//IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization);
return properties;
}
}
This is it, actually. The only thing we have to do now is to use JsonConvert.SerializeObject with some parameters:
InfoUtentePost product = new InfoUtente();
var settings = new JsonSerializerSettings()
{
ContractResolver = new InterfaceContractResolver(typeof(InfoUtentePost))
};
string output = JsonConvert.SerializeObject(product, typeof(InfoUtentePost), settings);
// output = "{\"nome\":null,\"cognome\":null,\"cellulare\":null,\"email\":null,\"termini\":false}"

Creating base Attribute class with a generic property 'value'

What I've done is created a base class of 'Attribute' in C#. From there I created other classes which inhert Attribute and add any additional properties as necessary. However when I try to create my observable collection which contains all these various attributes I get an underline here
private ObservableCollection<Attribute> _attributes;
under 'Attribute' saying: Using the generic type 'Attribute< TValue >' requires one type arguments. The reason for the base class of Attribute is so I can create multiple attributes as seen below.
Attribute Class
using System.Collections.Generic;
namespace ExampleTool.Model
{
public class Attribute<TValue>
{
public string Key { get; set; }
public TValue Value { get; set; }
}
public class FloatAttr : Attribute<float>
{
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class IntAttr : Attribute<int>
{
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class StringAttr : Attribute<string>
{
public string Label { get; set; }
}
public class BoolAttr : Attribute<bool>
{
public string Label { get; set; }
}
public class ListStringAttr : List<string>
{
public string Label { get; set; }
}
}
ViewModel - where error occurs...
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ExampleTool.Model;
using ExampleTool.Helper;
namespace ExampleTool.ViewModel
{
public class AttributeViewModel : ObservableObject
{
private ObservableCollection<Attribute> _attributes;
public ObservableCollection<Attribute> Attributes
{
get { return _attributes; }
set
{
_attributes = value;
NotifyPropertyChanged("Attributes");
}
}
public AttributeViewModel()
{
//hard coded data for testing
Attributes = new ObservableCollection<Attribute>();
FloatAttr floatAttr = new FloatAttr();
Attributes.Add(floatAttr);
IntAttr intAttr = new IntAttr();
Attributes.Add(intAttr);
StringAttr stringAttr = new StringAttr();
Attributes.Add(stringAttr);
BoolAttr boolAttr = new BoolAttr();
Attributes.Add(boolAttr);
ListStringAttr listStringAttr = new ListStringAttr();
Attributes.Add(listStringAttr);
}
}
}
Solution idea #1
- simply remove the property of value from the base class and define it in each sub class.
public class Attribute
{
public string Key { get; set; }
}
public class FloatAttr : Attribute
{
public float Value { get; set; }
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class IntAttr : Attribute
{
public int Value { get; set; }
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class StringAttr : Attribute
{
public string Value { get; set; }
public string Label { get; set; }
}
public class BoolAttr : Attribute
{
public bool Value { get; set; }
public string Label { get; set; }
}
public class ListStringAttr : Attribute
{
public List<string> Value { get; set; }
public string Label { get; set; }
}
Your base Attribute class is a generic type, then you must add type argument to it's usage. But you can't add just T:
private ObservableCollection<Attribute<T>> _attributes;
because T is not your type parameter. You should add new non-generic base class:
public class AttributeBase
{
public string Key { get; set; }
}
public class Attribute<TValue> : AttributeBase
{
public TValue Value { get; set; }
}
And implement AttributeRetriever like in this question:
public Attribute<T> GetAttribute<T>() where T: DatabaseItem, new()
{
return _attributes.OfType(typeof(Attribute<T>)).FirstOrDefault as Attribute<T>;
}
Good news are that your WPF View can works fine without type parameter because Binding uses reflection. Then if you no need to have an access to your properties in code you no need to implement retriever too.

Importing other attributes to metadata using C# MEF

Using some tutorials on the web, I created a metadata class for my needs using the MEF in C#
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class ActionMetadataAttribute : Attribute
{
public bool HasSettingsDialog { get; set; }
public bool UseThreadedProcessing { get; set; }
}
public interface IActionMetadata
{
[DefaultValue(false)]
bool HasSettingsDialog { get; }
[DefaultValue(false)]
bool UseThreadedProcessing { get; }
}
I've got different kind of plugin types, so there is e. g. IHandlerMetadata and HandlerMetadataAttribute. Now I load it via the Lazy helper to allow strictly typed metadata.
[ImportMany(typeof(IActionPlugin))]
private IEnumerable<Lazy<IActionPlugin, IActionMetadata>> _plugins = null;
public ActionManager()
{
var catalog = new DirectoryCatalog(".");
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
foreach (var contract in _plugins)
{
Debug.WriteLine(contract.Metadata.HasSettingsDialog);
}
}
Works perfectly. Now, I also like to have some information about the plugins. So I've created an PluginInformationAttribute.
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class PluginInformationAttribute : Attribute
{
public string Name { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Url { get; set; }
public string Contact { get; set; }
public string Description { get; set; }
public Version Version { get; set; }
}
The question is now: how can I access this attribute for example in the for loop in the above code snippet? Is there any way or is my design wrong? I don't want to include the PluginInformation stuff to IActionMetadata because I'd like to use it on different types of plugins, e. g. on the IHandlerMetadata.
If you specify the type of your exporting class as a property of ActionMetadataAttribute, then you can access every attribute of the class by reflection with GetCustomAttributes method
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class ActionMetadataAttribute : Attribute
{
public bool HasSettingsDialog { get; set; }
public bool UseThreadedProcessing { get; set; }
public Type ClassType { get; set; }
}
var attributes = contract.Metadata.ClassType.GetCustomAttributes(typeof(PluginInformationAttribute), true)

Serialize List<ICustomClass> to xml?

I have the following code files:
public interface IMod
{
string Name { get; set; }
string Description { get; set; }
bool Enabled { get; set; }
List<IClassFile> ClassFiles { get; set; }
}
public interface IClassFile
{
string Path { get; set; }
string FileName { get; set; }
bool Enabled { get; set; }
}
public class ClassFile : IClassFile
{
public string Path { get; set; }
public string FileName { get { return System.IO.Path.GetFileName(Path); } }
public bool Enabled { get; set; }
....
}
public class ZippedMod : IMod
{
public string Name { get; set; }
public string Description { get; set; }
public bool Enabled { get; set; }
public List<IClassFile> ClassFiles { get; set; }
....
}
public class ConfigurationBlock
{
public List<IMod> Mods { get; set; }
....
}
Throughout the course of my program, I add a few ZippedMods to the ConfigurationBlock, but now I want to serialize them. I tried doing:
using (var stream = new StreamWriter("config.xml"))
{
var ser = new XmlSerializer(typeof(ConfigurationBlock));
ser.Serialize(stream, configBlock);
}
But I get this error:
There was an error reflecting type 'MinecraftModManager.ConfigurationBlock'.
|-Inner Exception:
Cannot serialize member 'MinecraftModManager.ConfigurationBlock.Mods' of type 'System.Collections.Generic.List`1[[MinecraftModManager.IMod, MinecraftModManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for more details.
|-Inner Exception:
Cannot serialize member MinecraftModManager.ConfigurationBlock.Mods of type MinecraftModManager.IMod because it is an interface.
Help?
You can't serialize an interface due to the abstract nature of them. Lots of concrete types can implement the same interface, so it creates an ambiguity. You must use a concrete type.
You can't serialize an interface, only a concrete class with the default XmlSerializer. You may be able to implement IXmlSerializable to override this behavior.

Ria Services and navigation property issues

I'm encountering an issue using Silverlight4, Ria Services and Entity Framework.
From my sl client I try to get some data through ria services, in my domainService class this method gets called:
public IQueryable<LastMinuteWachtLijstPromotie> GetLastMinuteWachtLijstPromoties(){
IQueryable<LastMinuteWachtLijstPromotie> list = (IQueryable<LastMinuteWachtLijstPromotie>)this.ObjectContext.LastMinuteWachtLijstPromoties.Include("Promotie");
return (from LastMinuteWachtLijstPromotie lwmp in list where lwmp.Actief select lwmp);
}
when I check the contents of the list, in debug mode, it's filled with objects of type LastMinuteWachtLijstPromotie.
these objects have a navigation property to an Object named Promotie.
And i can access the properties of these Promotie objects.
On the silveright client however a method gets invoked when loading is complete:
public void OnLoadEntitiesCompleted(ServiceLoadResult<T> result) {
}
In this method I get all the requested LastMinuteWachtLijstPromotie objects as expected, the property
Promotie however is null.
I have set the [Include] tag on the property Promotie in the auto generated metadata class
and I use the .Include("Promotie")
These same methods are used for different objects from my Domain Model, this works perfectly.
Also, I cannot seem to find differences in the .edmx file with the database mappings and navigation properties.
Has anyone encountered the same issue or know a solution for it?
the metadata classes:
[MetadataTypeAttribute(typeof(LastMinuteWachtLijstPromotie.LastMinuteWachtLijstPromotieMetadata))]
public partial class LastMinuteWachtLijstPromotie
{
// This class allows you to attach custom attributes to properties
// of the LastMinuteWachtLijstPromotie class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class LastMinuteWachtLijstPromotieMetadata
{
// Metadata classes are not meant to be instantiated.
private LastMinuteWachtLijstPromotieMetadata()
{
}
public int AlertId { get; set; }
public string ArtikelNummer { get; set; }
public Nullable<int> ArtikelVariant { get; set; }
public int LastMinuteWachtLijstPromotieId { get; set; }
[Include]
public Promotie Promotie { get; set; }
public int PromotieArtikelId { get; set; }
public int PromotieId { get; set; }
public bool Actief { get; set; }
public DateTime Aanmaakdatum { get; set; }
}
}
[MetadataTypeAttribute(typeof(Promotie.PromotieMetadata))]
public partial class Promotie
{
// This class allows you to attach custom attributes to properties
// of the Promotie class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class PromotieMetadata
{
// Metadata classes are not meant to be instantiated.
private PromotieMetadata()
{
}
public string ActieType { get; set; }
public string AssortimentsManagerNaam { get; set; }
public string AssortimentsManagerTeamIds { get; set; }
[Display(Name = "Commerciele tekst")]
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Nokavision.ReclameFolder.UI.Web.Resources.ValidationResources))]
public string CommercieleTekst { get; set; }
[Display(Name = " ")]
public string CommercieleTekstDetails { get; set; }
[Include]
public Frame Frame { get; set; }
public Nullable<int> FrameId { get; set; }
public Nullable<DateTime> LastMinuteWijzigingsDatum { get; set; }
public string Opmerkingen { get; set; }
[Display(Name = "Op wachtlijst")]
public Nullable<bool> OpWachtLijst { get; set; }
//public Nullable<int> PromotieCopyId { get; set; }
public int PromotieId { get; set; }
[Include]
public EntityCollection<PromotieLeverancier> PromotieLeveranciers { get; set; }
[Include]
public EntityCollection<PromotieMutatie> PromotieMutaties{ get; set; }
//public Nullable<int> PromotieOrigineleId { get; set; }
[Include]
public EntityCollection<PromotieSymbool> PromotieSymbolen { get; set; }
public string Status { get; set; }
[Display(Name = "Promotie inhoud")]
public string PromotieInhoud { get; set; }
[Display(Name = "Promotie eenheid")]
public string PromotieEenheid { get; set; }
[Display(Name = "Promotie prijs")]
public decimal PromotiePrijs { get; set; }
}
}
Add the Composition attribute to the property Promotie property of the LastMinuteWachtLijstPromotieMetadata class. Then it should work.
public partial class LastMinuteWachtLijstPromotie {
internal sealed class LastMinuteWachtLijstPromotieMetadata{
[Include]
[Composition]
public Promotie Promotie { get; set; }
}
}
I know this is an older thread and it may well have been answered elsewhere but I just stumbled upon it and since nobody has provided a link or a better answer.
I'm currently using Silverlight 5 and this is what worked for me (I think the process is the same in SL4 IIRC).
When propegating navigation properties to the client you need to tell RIA services that there is a relationship somewhere using the [Key] and [Association] attributes, this, not unlike the entity framework just describes how to map the relationship to the proper object.
First the metadata classes:
[MetadataTypeAttribute(typeof(Category.CategoryMetadata))]
public partial class Category
{
internal sealed class CategoryMetadata
{
private CategoryMetadata() {
}
[Key]
public int Id { get; set; }
public string NAME { get; set; }
[Association("CategoryToProducts", "Id", "CAT")]
[Include]
public EntityCollection<Product> Products { get; set; }
}
}
[MetadataTypeAttribute(typeof(Order.OrderMetadata))]
public partial class Order
{
internal sealed class OrderMetadata
{
// Metadata classes are not meant to be instantiated.
private OrderMetadata() {
}
[Key]
public int Id { get; set; }
public int PRODID { get; set; }
public DateTime DATE { get; set; }
public bool DONE { get; set; }
public int QTY { get; set; }
[Association("OrderToProduct", "PRODID", "Id", IsForeignKey = true)]
[Include]
public Product Product { get; set; }
}
}
[MetadataTypeAttribute(typeof(Product.ProductMetadata))]
public partial class Product
{
internal sealed class ProductMetadata
{
private ProductMetadata() {
}
[Key]
public int Id { get; set; }
public int CAT { get; set; }
public string NAME { get; set; }
public string DESC { get; set; }
public decimal PRICE { get; set; }
public int QTY { get; set; }
public long UPC { get; set; }
[Association("ProdToCat", "CAT", "Id", IsForeignKey = true)]
[Include]
public Category Category { get; set; }
[Association("ProductToOrders", "Id", "PRODID")]
[Include]
public EntityCollection<Order> Orders { get; set; }
}
}
Now we need to tell RIA services we want it to load the association:
(Note: Intellisense says it's a dot separated list of property names to include, however I tried something like .Include("Category.SubCategory") and this failed with an exception... though .Include("Category").Include("SubCategory") worked like a charm!)
public IQueryable<Product> GetProducts() {
return this.ObjectContext.Products.Include("Category");
}
I can now access my "Category" property from the Silverlight client and it is not NULL :)
Same as SilverX: just had the issue, solved it and thought it could be useful to someone.
I too had all the configuration stuff correct ([Include] for RIA S, Include() for EF) but a navigation property was still null on the Silverlight side.
Turns out the domain service method was using the [Invoke] attribute (and returning a IEnumerable<T>). Removing this attribute solved the issue.
(just for the record, [Invoke] was being used because the method had a List<Entity> parameter)

Categories

Resources