I have a class that I have applied a DataContract attribute with a name property.
[DataContract(Name ="ProductInformation")]
public class ProductViewModel : BaseViewModel
{
[DataMember]
public string ProductName {get; set;}
}
All of my other ViewModel classes also inheriting from the BaseViewModel class.How do I retrieve the Name property from the BaseViewModel.
Update
If i understand you correctly, you want the most derived attribute when called form the base class. Note typeof(T) gets the instantiated type, I.e the most derived type
Also note GetTypeInfo() is basically added because this is a Xamarin Question
public static List<DataContractAttribute> GetDataContracts<T>()where T : class
{
return typeof(T).GetTypeInfo()
.GetCustomAttributes(false)
.OfType<DataContractAttribute>()
.ToList();
}
Original
public static List<DataContractAttribute> GetDataContracts<T>()where T : class
{
return typeof(T).BaseType?
.GetCustomAttributes(false)
.OfType<DataContractAttribute>()
.ToList();
}
public static void Main()
{
var attribute = GetDataContracts<ProductViewModel>().FirstOrDefault();
Console.WriteLine(attribute?.Name ?? "NoCigar");
}
PCL BaseViewModel Code
public class BaseViewModel
{
public static Dictionary<Assembly, Type> AllAssembelyUsedInBaseViewModel = new Dictionary<Assembly, Type>();
public static void RegisterAssemblyAndBase(Assembly assembly, Type baseType)
{
AllAssembelyUsedInBaseViewModel.Add(assembly, baseType);
}
static BaseViewModel()
{
RegisterAssemblyAndBase(typeof(BaseViewModel).GetTypeInfo().Assembly, typeof(BaseViewModel));
}
public static void GetDataContractNameFromAllAssembly()
{
List<string> dname = new List<string>();
foreach (var item in BaseViewModel.AllAssembelyUsedInBaseViewModel)
{
var assembly = item.Key;
var types = assembly.DefinedTypes.Where(x => x.BaseType == item.Value);
foreach (var type in types)
{
var attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
var dt = attributes.FirstOrDefault() as DataContractAttribute;
if (dt != null)
{
dname.Add(dt.Name);
}
}
}
}
}
Register other UI Assembelies
public MainWindow()
{
InitializeComponent();
BaseViewModel.RegisterAssemblyAndBase(typeof(MainWindow).Assembly, typeof(BaseViewModel));
}
Get All Datacontract Name by calling
BaseViewModel.GetDataContractNameFromAllAssembly();
Related
I have a parent class called Snack with subclasses Drink and Sweets. I want to store my Snacks in a "VendingMachine" Class where there is a list for each of the Products. However, I don't want to write the same method for each type of Snack. How would you write this as a generic method ?
// DRINKS LIST
List<Drink> drinks = new List<Drink>();
public List<Drink> Drinks { get => drinks; set => drinks = value; }
private void FillWithProducts <Product> (params Product[] products) where Product : Snack
{
Type typeParameter = typeof(Product);
Type drink = typeof(Drink);
foreach (Product p in products)
{
if (typeParameter.Equals(drink))
{
Drinks.Add(p);
}
}
}
If you really need to store each kinds of products in theair own list, you can use a dynamically populated dictionary where the key is the type, something like this.
private readonly Dictionary<Type, List<Product>> storeByType = new();
public List<Drink> Drinks => (List<Drink>)this.storeByType[typeof(Drink)]
private void FillWithProducts<Product>(params Product[] products) where Product : Snack
{
foreach (Product p in products)
{
var key = p.GetType();
if (!this.storeByType.ContainsKey(key)) {
// ... add new List<T> instantiated by reflection
// use MakeGenericType + Activator.CreateInstance for example
}
// cast to the non-generic interface
var list = (IList)this.storeByType[key];
list.Add(p);
}
}
Note, that the code is just present as an example to demonstrate the idea, missing many checks and safety, and might not even work as is.
I would keep a dictionary inside the VendingMachine that holds the snacks of different types with the type as the key. By doing so you avoid having to search a list with mixed types every time you want to fetch the items.
static void Main(string[] args)
{
var m = new VendingMachine();
m.AddRange(new Drink(), new Drink());
m.AddRange(new Sweet());
var drinks = m.Fetch<Drink>();
var sweets = m.Fetch<Sweet>();
}
public class VendingMachine
{
private readonly Dictionary<Type, List<Snack>> _snacks = new();
public void AddRange<T>(params T[] snacks) where T : Snack
{
var type = typeof(T);
if (_snacks.TryGetValue(type, out var existingSnacks))
existingSnacks.AddRange(snacks);
else
_snacks.Add(type, new List<Snack>(snacks));
}
public List<T> Fetch<T>() where T : Snack
{
if (_snacks.TryGetValue(typeof(T), out var existingSnacks))
return new List<T>(existingSnacks.Cast<T>());
return new List<T>();
}
}
I think maybe there's a different way of doing this. With your base SnackBase base class and derived Drink and Sweet classes, you can fill a VendingMachine class with snacks then get the drink and sweet lists from the vending machine. The code below illustrates this:
Base Class
internal class SnackBase
{
public string Name { get; }
protected SnackBase(string name)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentOutOfRangeException(nameof(name));
Name = name;
}
}
Derived classes
internal class Drink : SnackBase
{
public Drink(string name) : base(name) {}
}
internal class Sweet : SnackBase
{
public Sweet(string name) : base(name) {}
}
VendingMachine.cs
internal class VendingMachine
{
private readonly List<SnackBase> _snacks;
public VendingMachine(List<SnackBase> snacks)
{
_snacks = snacks;
}
public List<SnackBase> GetDrinks()
{
return _snacks.Where(s => s.GetType().Name == nameof(Drink)).ToList();
}
public List<SnackBase> GetSweets()
{
return _snacks.Where(s => s.GetType().Name == nameof(Sweet)).ToList();
}
}
Program.cs
internal static class Program
{
public static void Main()
{
var snacks = new List<SnackBase>
{
new Drink("Coke"),
new Sweet("Snickers"),
new Drink("Pepsi"),
new Sweet("Mars Bar"),
new Drink("7 Up"),
new Sweet("Reece's Pieces")
};
var vendingMachine = new VendingMachine(snacks);
Console.WriteLine("Drinks");
Console.WriteLine("------");
var drinks = vendingMachine.GetDrinks();
foreach (var drink in drinks)
{
Console.WriteLine(drink.Name);
}
Console.WriteLine("Sweets");
Console.WriteLine("------");
var sweets = vendingMachine.GetSweets();
foreach (var sweet in sweets)
{
Console.WriteLine(sweet.Name);
}
}
}
The vending machine class only needs one list of the common type (Snack)
Snacks
public abstract class Snack
{
protected Snack(string name)
{
Name = name;
}
public string Name { get; }
public abstract override string ToString();
}
public class Sweet : Snack
{
public Sweet(string name) : base(name)
{
}
public override string ToString() => $"Sweet({Name})";
}
public class Drink : Snack
{
public Drink(string name) : base(name)
{
}
public override string ToString() => $"Drink({Name})";
}
Vending Machine
public class VendingMachine
{
readonly List<Snack> _snacks;
public VendingMachine(params Snack[] snacks) => _snacks = new List<Snack>(snacks);
public VendingMachine(IEnumerable<Snack> snacks) => _snacks = new List<Snack>(snacks);
public IReadOnlyList<Snack> Snacks { get => _snacks; }
public IReadOnlyList<Drink> Drinks { get => _snacks.OfType<Drink>().ToList(); }
public IReadOnlyList<Sweet> Sweets { get => _snacks.OfType<Sweet>().ToList(); }
public void AddDrink(string name) => _snacks.Add(new Drink(name));
public void AddSweet(string name) => _snacks.Add(new Sweet(name));
}
Test Program
static class Program
{
static void Main(string[] args)
{
var vend = new VendingMachine();
vend.AddDrink("Joke Cola");
vend.AddSweet("Mersa Bar");
vend.AddDrink("Diet Goo");
vend.AddDrink("Bronto Care");
vend.AddSweet("Broken Tooth");
Console.WriteLine("Vending Machine Sweets");
foreach (var item in vend.Sweets)
{
Console.WriteLine(item);
}
Console.WriteLine();
Console.WriteLine("Vending Machine Drinks");
foreach (var item in vend.Drinks)
{
Console.WriteLine(item);
}
}
}
Sample Output
Vending Machine Sweets
Sweet(Mersa Bar)
Sweet(Broken Tooth)
Vending Machine Drinks
Drink(Joke Cola)
Drink(Diet Goo)
Drink(Bronto Care)
I have a class with a lot of properties of type IDbSet<SomeClass>:
public InMemoryContext : IContext
{
public IDbSet<ClassA> ClassASet { get; set; }
public IDbSet<ClassB> ClassBSet { get; set; }
[...]
public void SaveChanges()
{
//TODO: this is relevant part
}
}
All these properties are instantiated in the constructor:
public InMemoryContext
{
ClassASet = new InMemoryDbSet<ClassA>();
[...]
}
InMemoryDbSet has one relevant method for this question:
public class InMemoryDbSet<T> : IDbSet<T> where T : class
{
public void SaveChanges()
{
[...]
}
}
I'd like to get all properties that are IDbSet and call SaveChanges() on them in a loop, so I don't need to repeat all set names second time.
I tried with reflection but as my properties are generic I can't get it to work. I tried making ClassA and ClassB deriving from same common interface but still no luck.
Is it actually possible to do it without explicitly specifying all sets? What should I change to achieve this result?
I imagine the pseudocode to be like:
public void SaveChanges()
{
foreach(var set in GetDbSetsFromClass(this))
{
set.SaveChanges();
}
}
With reflection I tried:
public void SaveChanges()
{
SaveChangesCalled = true;
var properties = GetType().GetProperties().Where(p => p.PropertyType.IsInterface && typeof(IDbSet<IEntity>).IsAssignableFrom(p.PropertyType)).Cast<InMemoryDbSet<IEntity>>();
foreach (var property in properties)
{
CallSaveChanges(property);
}
}
And the list is empty
public bool SaveChangesCalled { get; set; }
private static void CallSaveChanges<T>(InMemoryDbSet<T> set) where T : class
{
set.SaveChanges();
}
EDIT: The public properties must remain visible at the top level of InMemoryContext class as this is restricted by the interface defining EF context.
Your code doesn't work because IDbSet<Class> is not assignable to IDbSet<IEntity>, because IDbSet<T> is not covariant.
What you want is to find properties whose type's generic definition is IDbSet<>.
var properties = typeof (InMemoryContext)
.GetProperties()
.Where(p => p.PropertyType.IsGenericType &&
p.PropertyType.GetGenericTypeDefinition() == typeof (IDbSet<>));
To invoke your CallSaveChanges method, you'll need this:
foreach(var proparty in properties)
{
var value = property.GetValue(this);
var entityType = property.PropertyType.GetGenericArguments().First();
var callSaveChanges = this.GetType()
.GetMethod("CallSaveChanges", BindingFlags.NonPublic | BindingFlags.Static);
var constructedCallSaveChanges = callSaveChanges.MakeGenericMethod(entityType );
constructedCallSaveChanges.Invoke(null, BindingFlags.NonPublic | BindingFlags.Static, null, new object[]{ value }, CultureInfo.InvariantCulture);
}
That being said, I don't think this scenario warrants the usage of reflection.
For a simple solution you could try something like this
public InMemoryContext
{
public Dictionary<string, object> allSets = new Dictionary<string, object>();
public InMemoryContext()
{
allSets.Add("classA", new InMemoryDbSet<ClassA>());
allSets.Add("classB", new InMemoryDbSet<ClassB>());
[...]
}
public IDbSet<ClassA> ClassASet { get
{
return allSets["classA"];
}
set
{
allSets["classA"] = Value
}
}
[...]
public void SaveChanges()
{
//TODO: this is relevant part
foreach(var kvp in allSets)
{
kvp.Value.SaveChanges();
}
}
}
Some things maybe wrong since it's just off top of my head.. but you should get the idea
I have an problem with this three classes.
In first class i'm extending for listview class for common method
In second class we put one method it's invoke by the 1st class
This is fine for above two classes but in my third class need to pass that T, M values.
But i don't understand how to do this?
anybody help this issue?
Thank you
1st Class
public class MyListView : ListView
{
public UserControl uc { get; set; }
internal MyLEvent<Type,Type> MyLEvnt { get; set; }
public MyListView()
{
PreviewKeyDown += new KeyEventHandler(MyListView_PreviewKeyDown);
}
private void MyListView_PreviewKeyDown(object sender,KeyEventArgs e)
{
ListView view = sender as ListView;
var item = view.SelectedItem;
if (item != null)
{
string str = item.GetType().Name;
if (e.Key == Key.Delete)
{
MyLEvnt.Method(item, "Delete");
}
else if (e.Key == Key.Enter)
{
MyLEvnt.Method(item, "Modify");
uc.GetType().GetProperty("Update").SetValue(uc, 1, null);
MethodInfo mi = uc.GetType().GetMethod("IClear");
mi.Invoke(uc, null);
}
}
}
}
2nd Class
public class MyLEvent<T,M> where T : class where M : class
{
private M manager;
private T type;
public MyLEvent()
{
}
public object Method(object _view, string flog)
{
object retVal = null;
type = Activator.CreateInstance<T>();
manager = Activator.CreateInstance<M>();
if (flog == "Modify")
{
MethodInfo method = typeof(M).GetMethod("getData");
type = (T)method.Invoke(manager, new[] { _view });
}
else if (flog == "Set")
{
MethodInfo method = typeof(M).GetMethod("setDefault");
retVal = method.Invoke(manager, new[] { _view });
}
else
{
if (MyMessage.askDelete() == true)
{
PropertyClass.Properties(_view, type, 'U');
MethodInfo method = typeof(M).GetMethod("Delete");
retVal = method.Invoke(manager, new[] { type });
}
}
return retVal;
}
}
3rd Class
public partial class SubASettings : UserControl
{
public SubASettings()
{
InitializeComponent();
MAILV.uc = this;
MAILV.MyLEvnt = new MyLEvent<typeof(InvMail), MailManager>();
Clear();
}
}
Thank you,
You can add a constraint to your generic type by declaring an interface:
public interface IManager
{
void getData();
setDefault
Delete
}
Define this constraint in declaration of second class which means that M type should implement IManager interface:
public class MyLEvent<T, M>
where T : class
where M : class, IManager
Then, you can invoke members of your class which defined in the interface:
public class MyLEvent<T, M>
where T : class
where M : class, IManager
{
private M manager;
public MyLEvent()
{
manager.Delete();
}
}
The declaration:
public class MyLEvent<T,M> where T : class where M : class
{
...
}
...defines a generic type that is used to create concrete types when provided with the appropriate type parameters. You cannot use generic types directly, you can only use them to create concrete types which can then be used.
For example, the List<T> generic type defines structure and code that can be used to create a variety of concrete types depending on the type parameter you use. List<string> is a concrete type created from the List<T> generic type.
In the case of your MyLEvent generic, there are two type parameters: T and M. You need to specify both of those to create a concrete type that can be used.
In your MyListView class you define the MyLEvnt field like this:
internal MyLEvent<Type,Type> MyLEvnt { get; set; }
This defines the MyLEvnt field as an instance of the concrete type MyLEvent<Type, Type>. Note that Type is a class that is used to access information about types. In this usage it is not a way to avoid supplying a type parameter, it is a type parameter.
In your third class you then do this:
MAILV.MyLEvnt = new MyLEvent<typeof(InvMail), MailManager>();
Even when we take the typeof() out of it, this will fail because you are attempting to assign an instance of MyLEvent<InvMail, MailManager> to a field of type MyLEvent<Type, Type>. These are different types, just as List<string> is different from List<int>.
You need to read the MSDN articles on Generics. These explain the details of how generics work and give you a lot of examples of how to use them and why.
Thank you so much for supporting. And i got one solution for this....
This is my previous 1st class
public class MyListView : ListView
{
public UserControl uc { get; set; }
internal MyLEvent<Type,Type> MyLEvnt { get; set; }
public MyListView()
{
PreviewKeyDown += new KeyEventHandler(MyListView_PreviewKeyDown);
}
private void MyListView_PreviewKeyDown(object sender,KeyEventArgs e)
{
ListView view = sender as ListView;
var item = view.SelectedItem;
if (item != null)
{
string str = item.GetType().Name;
if (e.Key == Key.Delete)
{
MyLEvnt.Method(item, "Delete");
}
else if (e.Key == Key.Enter)
{
MyLEvnt.Method(item, "Modify");
uc.GetType().GetProperty("Update").SetValue(uc, 1, null);
MethodInfo mi = uc.GetType().GetMethod("IClear");
mi.Invoke(uc, null);
}
}
}
}
After modified in my level....
public class MyListView : ListView
{
public UserControl uc { get; set; }
public object ML { get; set; }
public MyListView()
{
PreviewKeyDown += new KeyEventHandler(MyListView_PreviewKeyDown);
}
public void Methode<T,M>() where T : class where M : class
{
ListViewEvents<T, M> mn = new ListViewEvents<T, M>();
ML = mn;
}
private void MyListView_PreviewKeyDown(object sender,KeyEventArgs e)
{
ListView view = sender as ListView;
var item = view.SelectedItem;
if (item != null)
{
string str = item.GetType().Name;
if (e.Key == Key.Delete)
{
MethodInfo mi = ML.GetType().GetMethod("Method");
mi.Invoke(ML,new[]{item,"Delete"});
MethodInfo m = uc.GetType().GetMethod("IClear");
m.Invoke(uc, null);
}
else if (e.Key == Key.Enter)
{
MethodInfo m = ML.GetType().GetMethod("Method");
object ob = m.Invoke(ML, new[] { item, "Modify" });
PropertyClass.Properties(uc, ob, 'U');
}
}
}
}
This solution is better working for me. But if is there any wrong with this
please let me guide
Thank you again...
there have been quite some posts about this, all trying to serialize a Func delegate.
But could someone think of an alternative, when the use of the delegate is always clear?
We have a generic create command, which takes a delegate as paramater in the constructor. This delegate will create the Item for the create command:
public class CreateCommand<T> : Command
{
public T Item;
protected Func<T> Constructor;
public ClientCreateCommand(Func<T> constructor)
{
Constructor = constructor;
}
public override void Execute()
{
Item = Constructor();
}
}
The command is used like this:
var c = new CreateCommand<MyType>( () => Factory.CreateMyType(param1, param2, ...) );
History.Insert(c);
Then the History serializes the command and sends it to the server. ofc the delegate can't be serialized as is and we get an exception.
Now could someone think of a very simple Constructor class that can be serialized and does the same job than the lambda expresseion? Means it takes a list of paramters and returns an instance of type T, that we then can write somethink like this:
var constructor = new Constructor<MyType>(param1, param2, ...);
var c = new CreateCommand<MyType>(constructor);
History.Insert(c);
How would the Constructor class look like? Thanks for any ideas!
EDIT(2): I've provided a couple of complete example implementations. They are categorized below as "Implementation 1" and "Implementation 2".
Your delegate is essentially a factory. You could define a factory interface and create a class that implements that interface for your Item class. Below is an example:
public interface IFactory<T>
{
T Create();
}
[Serializable]
public class ExampleItemFactory : IFactory<T>
{
public int Param1 { get; set; }
public string Param2 { get; set; }
#region IFactory<T> Members
public Item Create()
{
return new Item(this.Param1, this.Param2);
}
#endregion
}
public class CreateCommand<T> : Command
{
public T Item;
protected IFactory<T> _ItemFactory;
public CreateCommand(IFactory<T> factory)
{
_ItemFactory = factory;
}
public override void Execute()
{
Item = _ItemFactory.Create();
}
}
You would utilize this code in the following manner:
IFactory<Item> itemFactory = new ExampleItemFactory { Param1 = 5, Param2 = "Example!" };
CreateCommand<Item> command = new CreateCommand<Item>(itemFactory);
command.Execute();
EDIT(1): The specific implementations of IFactory<T> that your application needs will be up to you. You could create specific factory classes for each class that you need, or you could create some kind of factory that dynamically creates an instance using, for example, the Activator.CreateInstance function or perhaps using some kind of Inversion of Control framework such as Spring or StructureMap.
Below is a complete example implementation that uses two factory implementations. One implementation can create any type given an array of arguments using that type's constructor with matching parameters. Another implementation creates any type that has been registered with my "Factory" class.
The Debug.Assert statements ensure that everything is behaving as intended. I ran this application without error.
Implementation 1
[Serializable]
public abstract class Command
{
public abstract void Execute();
}
public class Factory
{
static Dictionary<Type, Func<object[], object>> _DelegateCache = new Dictionary<Type, Func<object[], object>>();
public static void Register<T>(Func<object[], object> #delegate)
{
_DelegateCache[typeof(T)] = #delegate;
}
public static T CreateMyType<T>(params object[] args)
{
return (T)_DelegateCache[typeof(T)](args);
}
}
public interface IFactory<T>
{
T Create();
}
[Serializable]
public class CreateCommand<T> : Command
{
public T Item { get; protected set; }
protected IFactory<T> _ItemFactory;
public CreateCommand(IFactory<T> itemFactory)
{
this._ItemFactory = itemFactory;
}
public override void Execute()
{
this.Item = this._ItemFactory.Create();
}
}
// This class is a base class that represents a factory capable of creating an instance using a dynamic set of arguments.
[Serializable]
public abstract class DynamicFactory<T> : IFactory<T>
{
public object[] Args { get; protected set; }
public DynamicFactory(params object[] args)
{
this.Args = args;
}
public DynamicFactory(int numberOfArgs)
{
if (numberOfArgs < 0)
throw new ArgumentOutOfRangeException("numberOfArgs", "The numberOfArgs parameter must be greater than or equal to zero.");
this.Args = new object[numberOfArgs];
}
#region IFactory<T> Members
public abstract T Create();
#endregion
}
// This implementation uses the Activator.CreateInstance function to create an instance
[Serializable]
public class DynamicConstructorFactory<T> : DynamicFactory<T>
{
public DynamicConstructorFactory(params object[] args) : base(args) { }
public DynamicConstructorFactory(int numberOfArgs) : base(numberOfArgs) { }
public override T Create()
{
return (T)Activator.CreateInstance(typeof(T), this.Args);
}
}
// This implementation uses the Factory.CreateMyType function to create an instance
[Serializable]
public class MyTypeFactory<T> : DynamicFactory<T>
{
public MyTypeFactory(params object[] args) : base(args) { }
public MyTypeFactory(int numberOfArgs) : base(numberOfArgs) { }
public override T Create()
{
return Factory.CreateMyType<T>(this.Args);
}
}
[Serializable]
class DefaultConstructorExample
{
public DefaultConstructorExample()
{
}
}
[Serializable]
class NoDefaultConstructorExample
{
public NoDefaultConstructorExample(int a, string b, float c)
{
}
}
[Serializable]
class PrivateConstructorExample
{
private int _A;
private string _B;
private float _C;
private PrivateConstructorExample()
{
}
public static void Register()
{
// register a delegate with the Factory class that will construct an instance of this class using an array of arguments
Factory.Register<PrivateConstructorExample>((args) =>
{
if (args == null || args.Length != 3)
throw new ArgumentException("Expected 3 arguments.", "args");
if (!(args[0] is int))
throw new ArgumentException("First argument must be of type System.Int32.", "args[0]");
if (!(args[1] is string))
throw new ArgumentException("Second argument must be of type System.String.", "args[1]");
if (!(args[2] is float))
throw new ArgumentException("Third argument must be of type System.Single.", "args[2]");
var instance = new PrivateConstructorExample();
instance._A = (int)args[0];
instance._B = (string)args[1];
instance._C = (float)args[2];
return instance;
});
}
}
class Program
{
static void Main(string[] args)
{
var factory1 = new DynamicConstructorFactory<DefaultConstructorExample>(null);
var command1 = new CreateCommand<DefaultConstructorExample>(factory1);
var factory2 = new DynamicConstructorFactory<NoDefaultConstructorExample>(3);
factory2.Args[0] = 5;
factory2.Args[1] = "ABC";
factory2.Args[2] = 7.1f;
var command2 = new CreateCommand<NoDefaultConstructorExample>(factory2);
PrivateConstructorExample.Register(); // register this class so that it can be created by the Factory.CreateMyType function
var factory3 = new MyTypeFactory<PrivateConstructorExample>(3);
factory3.Args[0] = 5;
factory3.Args[1] = "ABC";
factory3.Args[2] = 7.1f;
var command3 = new CreateCommand<PrivateConstructorExample>(factory3);
VerifySerializability<DefaultConstructorExample>(command1);
VerifySerializability<NoDefaultConstructorExample>(command2);
VerifySerializability<PrivateConstructorExample>(command3);
}
static void VerifySerializability<T>(CreateCommand<T> originalCommand)
{
var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (var stream = new System.IO.MemoryStream())
{
System.Diagnostics.Debug.Assert(originalCommand.Item == null); // assert that originalCommand does not yet have a value for Item
serializer.Serialize(stream, originalCommand); // serialize the originalCommand object
stream.Seek(0, System.IO.SeekOrigin.Begin); // reset the stream position to the beginning for deserialization
// deserialize
var deserializedCommand = serializer.Deserialize(stream) as CreateCommand<T>;
System.Diagnostics.Debug.Assert(deserializedCommand.Item == null); // assert that deserializedCommand still does not have a value for Item
deserializedCommand.Execute();
System.Diagnostics.Debug.Assert(deserializedCommand.Item != null); // assert that deserializedCommand now has a value for Item
}
}
}
EDIT(2): After re-reading the question, I think I got a better idea of what the asker was really trying to get at. Essentially, we still want to take advantage of the flexibility offered by lambda expressions / anonymous delegates, but avoid the serialization issues.
Below is another example implementation that utilizes a Factory<T> class to store delegates used to return instances of type T.
Implementation 2
[Serializable]
public abstract class Command
{
public abstract void Execute();
}
[Serializable]
public abstract class CreateCommand<T> : Command
{
public T Item { get; protected set; }
}
public class Factory<T>
{
private static readonly object _SyncLock = new object();
private static Func<T> _CreateFunc;
private static Dictionary<string, Func<T>> _CreateFuncDictionary;
/// <summary>
/// Registers a default Create Func delegate for type <typeparamref name="T"/>.
/// </summary>
public static void Register(Func<T> createFunc)
{
lock (_SyncLock)
{
_CreateFunc = createFunc;
}
}
public static T Create()
{
lock (_SyncLock)
{
if(_CreateFunc == null)
throw new InvalidOperationException(string.Format("A [{0}] delegate must be registered as the default delegate for type [{1}]..", typeof(Func<T>).FullName, typeof(T).FullName));
return _CreateFunc();
}
}
/// <summary>
/// Registers a Create Func delegate for type <typeparamref name="T"/> using the given key.
/// </summary>
/// <param name="key"></param>
/// <param name="createFunc"></param>
public static void Register(string key, Func<T> createFunc)
{
lock (_SyncLock)
{
if (_CreateFuncDictionary == null)
_CreateFuncDictionary = new Dictionary<string, Func<T>>();
_CreateFuncDictionary[key] = createFunc;
}
}
public static T Create(string key)
{
lock (_SyncLock)
{
Func<T> createFunc;
if (_CreateFuncDictionary != null && _CreateFuncDictionary.TryGetValue(key, out createFunc))
return createFunc();
else
throw new InvalidOperationException(string.Format("A [{0}] delegate must be registered with the given key \"{1}\".", typeof(Func<T>).FullName, key));
}
}
}
[Serializable]
public class CreateCommandWithDefaultDelegate<T> : CreateCommand<T>
{
public override void Execute()
{
this.Item = Factory<T>.Create();
}
}
[Serializable]
public class CreateCommandWithKeyedDelegate<T> : CreateCommand<T>
{
public string CreateKey { get; set; }
public CreateCommandWithKeyedDelegate(string createKey)
{
this.CreateKey = createKey;
}
public override void Execute()
{
this.Item = Factory<T>.Create(this.CreateKey);
}
}
[Serializable]
class DefaultConstructorExample
{
public DefaultConstructorExample()
{
}
}
[Serializable]
class NoDefaultConstructorExample
{
public NoDefaultConstructorExample(int a, string b, float c)
{
}
}
[Serializable]
class PublicPropertiesExample
{
public int A { get; set; }
public string B { get; set; }
public float C { get; set; }
}
class Program
{
static void Main(string[] args)
{
// register delegates for each type
Factory<DefaultConstructorExample>.Register(() => new DefaultConstructorExample());
Factory<NoDefaultConstructorExample>.Register(() => new NoDefaultConstructorExample(5, "ABC", 7.1f));
Factory<PublicPropertiesExample>.Register(() => new PublicPropertiesExample() { A = 5, B = "ABC", C = 7.1f });
// create commands
var command1 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
var command2 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
var command3 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
// verify that each command can be serialized/deserialized and that the creation logic works
VerifySerializability<DefaultConstructorExample>(command1);
VerifySerializability<DefaultConstructorExample>(command2);
VerifySerializability<DefaultConstructorExample>(command3);
// register additional delegates for each type, distinguished by key
Factory<DefaultConstructorExample>.Register("CreateCommand", () => new DefaultConstructorExample());
Factory<NoDefaultConstructorExample>.Register("CreateCommand", () => new NoDefaultConstructorExample(5, "ABC", 7.1f));
Factory<PublicPropertiesExample>.Register("CreateCommand", () => new PublicPropertiesExample() { A = 5, B = "ABC", C = 7.1f });
// create commands, passing in the create key to the constructor
var command4 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
var command5 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
var command6 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
// verify that each command can be serialized/deserialized and that the creation logic works
VerifySerializability<DefaultConstructorExample>(command4);
VerifySerializability<DefaultConstructorExample>(command5);
VerifySerializability<DefaultConstructorExample>(command6);
}
static void VerifySerializability<T>(CreateCommand<T> originalCommand)
{
var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (var stream = new System.IO.MemoryStream())
{
System.Diagnostics.Debug.Assert(originalCommand.Item == null); // assert that originalCommand does not yet have a value for Item
serializer.Serialize(stream, originalCommand); // serialize the originalCommand object
stream.Seek(0, System.IO.SeekOrigin.Begin); // reset the stream position to the beginning for deserialization
// deserialize
var deserializedCommand = serializer.Deserialize(stream) as CreateCommand<T>;
System.Diagnostics.Debug.Assert(deserializedCommand.Item == null); // assert that deserializedCommand still does not have a value for Item
deserializedCommand.Execute();
System.Diagnostics.Debug.Assert(deserializedCommand.Item != null); // assert that deserializedCommand now has a value for Item
}
}
}
If I apply attributes to a partial class via the MetadataType attribute, those attributes are not found via Attribute.IsDefined(). Anyone know why, or what I'm doing wrong?
Below is a test project I created for this, but I'm really trying to apply custom attributes to a LINQ to SQL entity class - like this answer in this question.
Thanks!
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace MetaDataTest
{
class Program
{
static void Main(string[] args)
{
PropertyInfo[] properties = typeof(MyTestClass).GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
Console.WriteLine(Attribute.IsDefined(propertyInfo, typeof(MyAttribute)));
Console.WriteLine(propertyInfo.IsDefined(typeof(MyAttribute), true));
Console.WriteLine(propertyInfo.GetCustomAttributes(true).Length);
// Displays:
// False
// False
// 0
}
Console.ReadLine();
}
}
[MetadataType(typeof(MyMeta))]
public partial class MyTestClass
{
public string MyField { get; set; }
}
public class MyMeta
{
[MyAttribute()]
public string MyField { get; set; }
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : System.Attribute
{
}
}
The MetadataType attribute is used to specify help specify the additional information on the data object. To access the additional attributes you would need to do something like the following:
using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace MetaDataTest
{
class Program
{
static void Main(string[] args)
{
MetadataTypeAttribute[] metadataTypes = typeof(MyTestClass).GetCustomAttributes(typeof(MetadataTypeAttribute), true).OfType<MetadataTypeAttribute>().ToArray();
MetadataTypeAttribute metadata = metadataTypes.FirstOrDefault();
if (metadata != null)
{
PropertyInfo[] properties = metadata.MetadataClassType.GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
Console.WriteLine(Attribute.IsDefined(propertyInfo, typeof(MyAttribute)));
Console.WriteLine(propertyInfo.IsDefined(typeof(MyAttribute), true));
Console.WriteLine(propertyInfo.GetCustomAttributes(true).Length);
RequiredAttribute attrib = (RequiredAttribute)propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true)[0];
Console.WriteLine(attrib.ErrorMessage);
}
// Results:
// True
// True
// 2
// MyField is Required
}
Console.ReadLine();
}
}
[MetadataType(typeof(MyMeta))]
public partial class MyTestClass
{
public string MyField { get; set; }
}
public class MyMeta
{
[MyAttribute()]
[Required(ErrorMessage="MyField is Required")]
public string MyField { get; set; }
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : System.Attribute
{
}
}
This also includes a sample attribute to show how to extract info that was added.
I had a similar situation. I ended up writing the following extension method for it.
The idea is to hide the abstraction of looking in 2 places (main class and metadata class).
static public Tattr GetSingleAttribute<Tattr>(this PropertyInfo pi, bool Inherit = true) where Tattr : Attribute
{
var attrs = pi.GetCustomAttributes(typeof(Tattr), Inherit);
if (attrs.Length > 0)
return (Tattr)attrs[0];
var mt = pi.DeclaringType.GetSingleAttribute<MetadataTypeAttribute>();
if (mt != null)
{
var pi2 = mt.MetadataClassType.GetProperty(pi.Name);
if (pi2 != null)
return pi2.GetSingleAttribute<Tattr>(Inherit);
}
return null;
}
My solution for generic use. Get the attribute the property that you are looking for. Return null if not found.
If found, it returns the attribute itself. So you can have access to the properties inside the attribute if you wihs.
Hopes this help.
public static Attribute GetAttribute<T>(this PropertyInfo PI, T t) where T: Type
{
var Attrs = PI.DeclaringType.GetCustomAttributes(typeof(MetadataTypeAttribute), true);
if (Attrs.Length < 1) return null;
var metaAttr = Attrs[0] as MetadataTypeAttribute;
var metaProp = metaAttr.MetadataClassType.GetProperty(PI.Name);
if (metaProp == null) return null;
Attrs = metaProp.GetCustomAttributes(t, true);
if (Attrs.Length < 1) return null;
return Attrs[0] as Attribute;
}
Given the following classes:
public partial class Person
{
public int PersonId { get; set; }
}
[MetadataType(typeof(PersonMetadata))]
public partial class Person
{
public partial class PersonMetadata
{
[Key]
public int PersonId { get; set; }
}
}
I needed to get see if Key has been defined on a property for Person class. I then needed to get the value of the property. Using #AdamGrid answer, I modified the code like this to get it:
private static object GetPrimaryKeyValue(TEntity entity)
{
MetadataTypeAttribute[] metadataTypes = typeof(TEntity).GetCustomAttributes(typeof(MetadataTypeAttribute), true).OfType<MetadataTypeAttribute>().ToArray();
MetadataTypeAttribute metadata = metadataTypes.FirstOrDefault();
if (metadata == null)
{
ThrowNotFound();
}
PropertyInfo[] properties = metadata.MetadataClassType.GetProperties();
PropertyInfo primaryKeyProperty =
properties.SingleOrDefault(x => Attribute.GetCustomAttribute(x, typeof(KeyAttribute)) as KeyAttribute != null);
if (primaryKeyProperty == null)
{
ThrowNotFound();
}
object primaryKeyValue = typeof(TEntity).GetProperties().Single(x => x.Name == primaryKeyProperty.Name).GetValue(entity);
return primaryKeyValue;
}
private static void ThrowNotFound()
{
throw new InvalidOperationException
($"The type {typeof(TEntity)} does not have a property with attribute KeyAttribute to indicate the primary key. You must add that attribute to one property of the class.");
}