I've read some stuff about the ExpandoObject, and that I can expand it with properties,fields,methods.
//that's how to add a property to an ExpandoObject.
dynamic x = new ExpandoObject();
x.NewProp = string.Empty;
But sometimes, it could be handy to add a property with some "extra-code".
class sample
{
// a sample field.
public string sampleString{get;set}
// a sample property with some "extra code"
private string s;
public string sampleExtraString
{
get{return s;}
set{s=value;Console.WriteLine(s);}
}
}
Now my question is, how can I add a property to the ExpandoObject that will execute my Console.WriteLine(s); for example on set.
ExpandoObject implements INotifyPropertyChanged, as explained here
(at the bottom of the page)
((INotifyPropertyChanged)x).PropertyChanged +=
new PropertyChangedEventHandler(Expando_PropertyChanged);
x.NewProp = string.Empty;
private static void Expando_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
Console.WriteLine("{0} has changed.", e.PropertyName);
}
I think a better approach would be using DynamicObject which you can intercept the calls for methods and properties.
This is a simple example, a more robust one would not use reflection to perform set/get operations on the property but rather using reflection.Emit or any compiled operation strategy.
public class Sample
{
public string SampleExtraString { get; set; }
}
public class Factory
{
public class ExtraPropertyObject<T> : DynamicObject
{
private readonly T instance = default(T);
private readonly Type instanceType = null;
public ExtraPropertyObject(T instance) {
this.instance = instance;
instanceType = instance.GetType();
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
PropertyInfo prop = null;
if (binder.Name.Equals("SampleExtraString")) {
Console.WriteLine(value);
}
prop = instanceType.GetProperty(binder.Name);
if (prop != null) {
try {
prop.SetValue(instance, value);
return true;
}
catch (Exception ex) {
}
}
return false;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
var prop = instanceType.GetProperty(binder.Name);
if (prop != null) {
try {
result = prop.GetValue(instance);
return true;
}
catch (Exception ex) {
}
}
result = null;
return false;
}
}
public static dynamic CreateInstance<TInstance>() where TInstance : class, new() {
return new ExtraPropertyObject<TInstance>(new TInstance());
}
public static dynamic CreateInstance<TInstance>(TInstance instance) {
return new ExtraPropertyObject<TInstance>(instance);
}
}
class Program
{
static void Main(string[] args) {
var instance = Factory.CreateInstance<Sample>();
instance.SampleExtraString = "value";
Console.WriteLine("Get Operation: {0}", instance.SampleExtraString);
}
}
Related
I have created an architecture in my C# code which does exactly what I want, but seems it would be very difficult to maintain in the long-run and am hoping there's a design pattern / better architecture I could be pointed towards.
I have created an object Test which, again, does exactly what I need perfectly which has the following structure:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
private Lazy<object> _test1;
public object Test1 { get { return _test1.Value; } }
private Lazy<object> _test2;
public object Test2 { get { return _test2.Value; } }
public Test()
{
_test1 = new Lazy<object>(() => MethodDictionary["Test1"](this), true);
_test2 = new Lazy<object>(() => MethodDictionary["Test2"](this), true);
}
}
What this allows me to do is, at run-time to assign a dictionary of functions to my Test object and the 2 properties Test1 & Test2 will use the functions loaded into it to return values.
The implementation looking somewhat as follows:
class Program
{
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1.ToString());
Console.WriteLine(x.Test2.ToString());
Console.ReadKey();
}
private static object TestMethod1(Test t)
{ return t.Var1 + t.Var2; }
private static object TestMethod2(Test t)
{ return t.Var1 - t.Var2; }
}
And it works great and has proven very efficient for large sets of Test objects.
My challenge is that if I ever want to add in a new method to my Test class, I need to add in the:
private Lazy<object> _myNewMethod;
public object MyNewMethod { get { return _myNewMethod.Value; } }
Update the constuctor with the key to look for in the dictionary
And, although that is pretty simple, I'd love to have a 1-line add-in (maybe some form of custom object) or have the properties read directly form the dictionary without any need for defining them at all.
Any ideas? ANY help would be great!!!
Thanks!!!
One of the ways in which you could achieve your desired behavior, is to use something that resembles a miniature IoC framework for field injection, tuned to your specific use case.
To make things easier, allow less typing in your concrete classes and make things type-safe, we introduce the LazyField type:
public class LazyField<T>
{
private static readonly Lazy<T> Default = new Lazy<T>();
private readonly Lazy<T> _lazy;
public LazyField() : this(Default) { }
public LazyField(Lazy<T> lazy)
{
_lazy = lazy;
}
public override string ToString()
{
return _lazy.Value.ToString();
}
public static implicit operator T(LazyField<T> instance)
{
return instance._lazy.Value;
}
}
Furthermore, we define an abstract base class, that ensures that these fields will be created at construction time:
public abstract class AbstractLazyFieldHolder
{
protected AbstractLazyFieldHolder()
{
LazyFields.BuildUp(this); // ensures fields are populated.
}
}
Skipping for a moment how this is achieved (explained further below), this allows the following way of defining your Test class:
public class Test : AbstractLazyFieldHolder
{
public double Var1;
public double Var2;
public readonly LazyField<double> Test1;
public readonly LazyField<double> Test2;
}
Note that these fields are immutable, initialized in the constructor. Now, for your usage example, the below snippet shows the "new way" of doing this:
LazyFields.Configure<Test>()
// We can use a type-safe lambda
.SetProvider(x => x.Test1, inst => inst.Var1 + inst.Var2)
// Or the field name.
.SetProvider("Test2", TestMethod2);
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1);
double test2Val = x.Test2; // type-safe conversion
Console.WriteLine(test2Val);
// Output:
// 50
// -10
The class below provides the services that support the configuration and injection of these field value.
public static class LazyFields
{
private static readonly ConcurrentDictionary<Type, IBuildUp> _registry = new ConcurrentDictionary<Type,IBuildUp>();
public interface IConfigureType<T> where T : class
{
IConfigureType<T> SetProvider<FT>(string fieldName, Func<T, FT> provider);
IConfigureType<T> SetProvider<F, FT>(Expression<Func<T, F>> fieldExpression, Func<T, FT> provider) where F : LazyField<FT>;
}
public static void BuildUp(object instance)
{
System.Diagnostics.Debug.Assert(instance != null);
var builder = _registry.GetOrAdd(instance.GetType(), BuildInitializer);
builder.BuildUp(instance);
}
public static IConfigureType<T> Configure<T>() where T : class
{
return (IConfigureType<T>)_registry.GetOrAdd(typeof(T), BuildInitializer);
}
private interface IBuildUp
{
void BuildUp(object instance);
}
private class TypeCfg<T> : IBuildUp, IConfigureType<T> where T : class
{
private readonly List<FieldInfo> _fields;
private readonly Dictionary<string, Action<T>> _initializers;
public TypeCfg()
{
_fields = typeof(T)
.GetFields(BindingFlags.Instance | BindingFlags.Public)
.Where(IsLazyField)
.ToList();
_initializers = _fields.ToDictionary(x => x.Name, BuildDefaultSetter);
}
public IConfigureType<T> SetProvider<FT>(string fieldName, Func<T,FT> provider)
{
var pi = _fields.First(x => x.Name == fieldName);
_initializers[fieldName] = BuildSetter<FT>(pi, provider);
return this;
}
public IConfigureType<T> SetProvider<F,FT>(Expression<Func<T,F>> fieldExpression, Func<T,FT> provider)
where F : LazyField<FT>
{
return SetProvider((fieldExpression.Body as MemberExpression).Member.Name, provider);
}
public void BuildUp(object instance)
{
var typedInstance = (T)instance;
foreach (var initializer in _initializers.Values)
initializer(typedInstance);
}
private bool IsLazyField(FieldInfo fi)
{
return fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(LazyField<>);
}
private Action<T> BuildDefaultSetter(FieldInfo fi)
{
var itemType = fi.FieldType.GetGenericArguments()[0];
var defValue = Activator.CreateInstance(typeof(LazyField<>).MakeGenericType(itemType));
return (inst) => fi.SetValue(inst, defValue);
}
private Action<T> BuildSetter<FT>(FieldInfo fi, Func<T, FT> provider)
{
return (inst) => fi.SetValue(inst, new LazyField<FT>(new Lazy<FT>(() => provider(inst))));
}
}
private static IBuildUp BuildInitializer(Type targetType)
{
return (IBuildUp)Activator.CreateInstance(typeof(TypeCfg<>).MakeGenericType(targetType));
}
}
Look at library https://github.com/ekonbenefits/impromptu-interface.
With it and using DynamicObject i wrote sample code that shows how to simplify adding new methods:
public class Methods
{
public Methods()
{
MethodDictionary = new Dictionary<string, Func<ITest, object>>();
LazyObjects = new Dictionary<string, Lazy<object>>();
}
public Dictionary<string, Func<ITest, object>> MethodDictionary { get; private set; }
public Dictionary<string, Lazy<object>> LazyObjects { get; private set; }
}
public class Proxy : DynamicObject
{
Methods _methods;
public Proxy()
{
_methods = new Methods();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _methods.LazyObjects[binder.Name].Value;
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_methods.MethodDictionary[binder.Name] = (Func<ITest, object>)value;
_methods.LazyObjects[binder.Name] = new Lazy<object>(() => _methods.MethodDictionary[binder.Name](this.ActLike<ITest>()), true);
return true;
}
}
//now you can add new methods by add single method to interface
public interface ITest
{
object Test1 { get; set; }
object Test2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var x = new Proxy().ActLike<ITest>();
x.Test1 = new Func<ITest, object>((y) => "Test1");
x.Test2 = new Func<ITest, object>((y) => "Test2");
Console.WriteLine(x.Test1);
Console.WriteLine(x.Test2);
}
}
I don't know what you are trying to do, but I think you can use a simpler approach like this:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
}
Calling the function is simple:
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(Test.MethodDictionary["Test1"](x).ToString());
Console.WriteLine(Test.MethodDictionary["Test2"](x).ToString());
Console.ReadKey();
}
I'm trying to expose a model to be available for OData services. The approach I'm currently taking is along the lines of:
1) Defining a class in the model to expose IQueryable collections such as:
public class MyEntities
{
public IQueryable<Customer> Customers
{
get
{
return DataManager.GetCustomers().AsQueryable<Customer>();
}
}
public IQueryable<User> Users
{
get
{
return DataManager.GetUsers().AsQueryable<User>();
}
}
}
2) Set up a WCF DataService with the queryable collection class such as:
public class MyDataService : DataService<MyEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Customers", EntitySetRights.All);
config.SetEntitySetAccessRule("Users", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
I'm running into 3 issues and/or limitations with this approach:
1) I'm unable to add any derived class collections to the IQueryable lists.
2) I must apply the IgnoreProperties attribute to hide any members that are derived from a base type.
3) I'm unable to prevent unwanted entities from being accessed by the OData service and causing errors. For example, I only want BLL layer objects to be exposed, but it seems like the model is being reflected far beyond the members of the classes I added to the queryable list, and picking up all the DAL classes, causing errors being undefined and also having the same name as the BLL classes. There are no links to DAL classes from BLL class members. At the very least, I would like to have these classes ignored altogether.
Any pointers on how to address any of these issues would be greatly appreciated. Should I be doing a different approach on this? For example, should I implement IQueryable directly in my model collections?
Thanks.
The reflection provider which you're using is designed to walk all public types/properties. So the #3 and probably even #2 (which I don't fully understand what's the problem) are by design because of that.
#1 is also by design but for a different reason - the reflection provider can only expose one entity set for each type hierarchy. It doesn't support so called "MEST" (Multiple Entity Sets per Type), because it would not know which one to pick. It needs a 1 to 1 mapping between entity types and entity sets.
The reflection provider is meant for simple services which are "Easy" to setup. It's definitely not designed for customizations.
If you want greater control, then you need custom provider, which can be either implemented directly (if it's based on existing CLR classes it's not that hard), or through some library, like the one suggested in the comments above.
The reflection provider is not designed to handle rich data models with a fair amount of inheritance and other dependences. I ended up building a custom provider that could handle queries, updates, inheritance, and relationships based on Alex James' excellent blog post on Creating a Data Service Provider.
An example implementation with 3 CLR classes: ResidentialCustomer, Customer, and User is provided below. ResidentialCustomer extends Customer, Customer has a list of Users, and User has a reference back to Customer.
An interface for DataContext classes such as:
public interface IODataContext
{
IQueryable GetQueryable(ResourceSet set);
object CreateResource(ResourceType resourceType);
void AddResource(ResourceType resourceType, object resource);
void DeleteResource(object resource);
void SaveChanges();
}
A class to implement IDataServiceMetadataProvider such as:
public class ODataServiceMetadataProvider : IDataServiceMetadataProvider
{
private Dictionary<string, ResourceType> resourceTypes = new Dictionary<string, ResourceType>();
private Dictionary<string, ResourceSet> resourceSets = new Dictionary<string, ResourceSet>();
private List<ResourceAssociationSet> _associationSets = new List<ResourceAssociationSet>();
public string ContainerName
{
get { return "MyDataContext"; }
}
public string ContainerNamespace
{
get { return "MyNamespace"; }
}
public IEnumerable<ResourceSet> ResourceSets
{
get { return this.resourceSets.Values; }
}
public IEnumerable<ServiceOperation> ServiceOperations
{
get { yield break; }
}
public IEnumerable<ResourceType> Types
{
get { return this.resourceTypes.Values; }
}
public bool TryResolveResourceSet(string name, out ResourceSet resourceSet)
{
return resourceSets.TryGetValue(name, out resourceSet);
}
public bool TryResolveResourceType(string name, out ResourceType resourceType)
{
return resourceTypes.TryGetValue(name, out resourceType);
}
public bool TryResolveServiceOperation(string name, out ServiceOperation serviceOperation)
{
serviceOperation = null;
return false;
}
public void AddResourceType(ResourceType type)
{
type.SetReadOnly();
resourceTypes.Add(type.FullName, type);
}
public void AddResourceSet(ResourceSet set)
{
set.SetReadOnly();
resourceSets.Add(set.Name, set);
}
public bool HasDerivedTypes(ResourceType resourceType)
{
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
return true;
}
return false;
}
public IEnumerable<ResourceType> GetDerivedTypes(ResourceType resourceType)
{
List<ResourceType> derivedResourceTypes = new List<ResourceType>();
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
foreach (ResourceType resource in Types)
{
if (resource.InstanceType == typeof(Customer))
{
derivedResourceTypes.Add(resource);
}
}
}
return derivedResourceTypes;
}
public void AddAssociationSet(ResourceAssociationSet associationSet)
{
_associationSets.Add(associationSet);
}
public ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
{
return resourceProperty.CustomState as ResourceAssociationSet;
}
public ODataServiceMetadataProvider() { }
}
A class to implement IDataServiceQueryProvider such as:
public class ODataServiceQueryProvider<T> : IDataServiceQueryProvider where T : IODataContext
{
T _currentDataSource;
IDataServiceMetadataProvider _metadata;
public object CurrentDataSource
{
get
{
return _currentDataSource;
}
set
{
_currentDataSource = (T)value;
}
}
public bool IsNullPropagationRequired
{
get { return true; }
}
public object GetOpenPropertyValue(object target, string propertyName)
{
throw new NotImplementedException();
}
public IEnumerable<KeyValuePair<string, object>> GetOpenPropertyValues(object target)
{
throw new NotImplementedException();
}
public object GetPropertyValue(object target, ResourceProperty resourceProperty)
{
throw new NotImplementedException();
}
public IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet)
{
return _currentDataSource.GetQueryable(resourceSet);
}
public ResourceType GetResourceType(object target)
{
Type type = target.GetType();
return _metadata.Types.Single(t => t.InstanceType == type);
}
public object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters)
{
throw new NotImplementedException();
}
public ODataServiceQueryProvider(IDataServiceMetadataProvider metadata)
{
_metadata = metadata;
}
}
A class to implement IDataServiceUpdateProvider such as:
public class ODataServiceUpdateProvider<T> : IDataServiceUpdateProvider where T : IODataContext
{
private IDataServiceMetadataProvider _metadata;
private ODataServiceQueryProvider<T> _query;
private List<Action> _actions;
public T GetContext()
{
return ((T)_query.CurrentDataSource);
}
public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues)
{
throw new NotImplementedException();
}
public void SetReference(object targetResource, string propertyName, object propertyValue)
{
_actions.Add(() => ReallySetReference(targetResource, propertyName, propertyValue));
}
public void ReallySetReference(object targetResource, string propertyName, object propertyValue)
{
targetResource.SetPropertyValue(propertyName, propertyValue);
}
public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
_actions.Add(() => ReallyAddReferenceToCollection(targetResource, propertyName, resourceToBeAdded));
}
public void ReallyAddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
var collection = targetResource.GetPropertyValue(propertyName);
if (collection is IList)
{
(collection as IList).Add(resourceToBeAdded);
}
}
public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
_actions.Add(() => ReallyRemoveReferenceFromCollection(targetResource, propertyName, resourceToBeRemoved));
}
public void ReallyRemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
var collection = targetResource.GetPropertyValue(propertyName);
if (collection is IList)
{
(collection as IList).Remove(resourceToBeRemoved);
}
}
public void ClearChanges()
{
_actions.Clear();
}
public void SaveChanges()
{
foreach (var a in _actions)
a();
GetContext().SaveChanges();
}
public object CreateResource(string containerName, string fullTypeName)
{
ResourceType type = null;
if (_metadata.TryResolveResourceType(fullTypeName, out type))
{
var context = GetContext();
var resource = context.CreateResource(type);
_actions.Add(() => context.AddResource(type, resource));
return resource;
}
throw new Exception(string.Format("Type {0} not found", fullTypeName));
}
public void DeleteResource(object targetResource)
{
_actions.Add(() => GetContext().DeleteResource(targetResource));
}
public object GetResource(IQueryable query, string fullTypeName)
{
var enumerator = query.GetEnumerator();
if (!enumerator.MoveNext())
throw new Exception("Resource not found");
var resource = enumerator.Current;
if (enumerator.MoveNext())
throw new Exception("Resource not uniquely identified");
if (fullTypeName != null)
{
ResourceType type = null;
if (!_metadata.TryResolveResourceType(fullTypeName, out type))
throw new Exception("ResourceType not found");
if (!type.InstanceType.IsAssignableFrom(resource.GetType()))
throw new Exception("Unexpected resource type");
}
return resource;
}
public object ResetResource(object resource)
{
_actions.Add(() => ReallyResetResource(resource));
return resource;
}
public void ReallyResetResource(object resource)
{
var clrType = resource.GetType();
ResourceType resourceType = _metadata.Types.Single(t => t.InstanceType == clrType);
var resetTemplate = GetContext().CreateResource(resourceType);
foreach (var prop in resourceType.Properties
.Where(p => (p.Kind & ResourcePropertyKind.Key) != ResourcePropertyKind.Key))
{
var clrProp = clrType.GetProperties().Single(p => p.Name == prop.Name);
var defaultPropValue = clrProp.GetGetMethod().Invoke(resetTemplate, new object[] { });
clrProp.GetSetMethod().Invoke(resource, new object[] { defaultPropValue });
}
}
public object ResolveResource(object resource)
{
return resource;
}
public object GetValue(object targetResource, string propertyName)
{
var value = targetResource.GetType().GetProperties().Single(p => p.Name == propertyName).GetGetMethod().Invoke(targetResource, new object[] { });
return value;
}
public void SetValue(object targetResource, string propertyName, object propertyValue)
{
targetResource.GetType().GetProperties().Single(p => p.Name == propertyName).GetSetMethod().Invoke(targetResource, new[] { propertyValue });
}
public ODataServiceUpdateProvider(IDataServiceMetadataProvider metadata, ODataServiceQueryProvider<T> query)
{
_metadata = metadata;
_query = query;
_actions = new List<Action>();
}
}
A class to implement IServiceProvider such as:
public class ODataService<T> : DataService<T>, IServiceProvider where T : IODataContext
{
private ODataServiceMetadataProvider _metadata;
private ODataServiceQueryProvider<T> _query;
private ODataServiceUpdateProvider<T> _updater;
public object GetService(Type serviceType)
{
if (serviceType == typeof(IDataServiceMetadataProvider))
{
return _metadata;
}
else if (serviceType == typeof(IDataServiceQueryProvider))
{
return _query;
}
else if (serviceType == typeof(IDataServiceUpdateProvider))
{
return _updater;
}
else
{
return null;
}
}
public ODataServiceMetadataProvider GetMetadataProvider(Type dataSourceType)
{
ODataServiceMetadataProvider metadata = new ODataServiceMetadataProvider();
ResourceType customer = new ResourceType(
typeof(Customer),
ResourceTypeKind.EntityType,
null,
"MyNamespace",
"Customer",
false
);
ResourceProperty customerCustomerID = new ResourceProperty(
"CustomerID",
ResourcePropertyKind.Key |
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(Guid))
);
customer.AddProperty(customerCustomerID);
ResourceProperty customerCustomerName = new ResourceProperty(
"CustomerName",
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(string))
);
customer.AddProperty(customerCustomerName);
ResourceType residentialCustomer = new ResourceType(
typeof(ResidentialCustomer),
ResourceTypeKind.EntityType,
customer,
"MyNamespace",
"ResidentialCustomer",
false
);
ResourceType user = new ResourceType(
typeof(User),
ResourceTypeKind.EntityType,
null,
"MyNamespace",
"User",
false
);
ResourceProperty userUserID = new ResourceProperty(
"UserID",
ResourcePropertyKind.Key |
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(Guid))
);
user.AddProperty(userUserID);
ResourceProperty userCustomerID = new ResourceProperty(
"CustomerID",
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(Guid))
);
user.AddProperty(userCustomerID);
ResourceProperty userEmailAddress = new ResourceProperty(
"EmailAddress",
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(string))
);
user.AddProperty(userEmailAddress);
var customerSet = new ResourceSet("Customers", customer);
var residentialCustomerSet = new ResourceSet("ResidentialCustomers", residentialCustomer);
var userSet = new ResourceSet("Users", user);
var userCustomer = new ResourceProperty(
"Customer",
ResourcePropertyKind.ResourceReference,
customer
);
user.AddProperty(userCustomer);
var customerUserList = new ResourceProperty(
"UserList",
ResourcePropertyKind.ResourceSetReference,
user
);
customer.AddProperty(customerUserList);
metadata.AddResourceType(customer);
metadata.AddResourceSet(customerSet);
metadata.AddResourceType(residentialCustomer);
metadata.AddResourceSet(residentialCustomerSet);
metadata.AddResourceType(user);
metadata.AddResourceSet(userSet);
ResourceAssociationSet customerUserListSet = new ResourceAssociationSet(
"CustomerUserList",
new ResourceAssociationSetEnd(
customerSet,
customer,
customerUserList
),
new ResourceAssociationSetEnd(
userSet,
user,
userCustomer
)
);
customerUserList.CustomState = customerUserListSet;
userCustomer.CustomState = customerUserListSet;
metadata.AddAssociationSet(customerUserListSet);
return metadata;
}
public ODataServiceQueryProvider<T> GetQueryProvider(ODataServiceMetadataProvider metadata)
{
return new ODataServiceQueryProvider<T>(metadata);
}
public ODataServiceUpdateProvider<T> GetUpdateProvider(ODataServiceMetadataProvider metadata, ODataServiceQueryProvider<T> query)
{
return new ODataServiceUpdateProvider<T>(metadata, query);
}
public ODataService()
{
_metadata = GetMetadataProvider(typeof(T));
_query = GetQueryProvider(_metadata);
_updater = GetUpdateProvider(_metadata, _query);
}
}
The DataContext class holds the CLR collections and wires up the service operations such as:
public partial class MyDataContext: IODataContext
{
private List<Customer> _customers = null;
public List<Customer> Customers
{
get
{
if (_customers == null)
{
_customers = DataManager.GetCustomers);
}
return _customers;
}
}
private List<ResidentialCustomer> _residentialCustomers = null;
public List<ResidentialCustomer> ResidentialCustomers
{
get
{
if (_residentialCustomers == null)
{
_residentialCustomers = DataManager.GetResidentialCustomers();
}
return _residentialCustomers;
}
}
private List<User> _users = null;
public List<User> Users
{
get
{
if (_users == null)
{
_users = DataManager.GetUsers();
}
return _users;
}
}
public IQueryable GetQueryable(ResourceSet set)
{
if (set.Name == "Customers") return Customers.AsQueryable();
if (set.Name == "ResidentialCustomers") return ResidentialCustomers.AsQueryable();
if (set.Name == "Users") return Users.AsQueryable();
throw new NotSupportedException(string.Format("{0} not found", set.Name));
}
public object CreateResource(ResourceType resourceType)
{
if (resourceType.InstanceType == typeof(Customer))
{
return new Customer();
}
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
return new ResidentialCustomer();
}
if (resourceType.InstanceType == typeof(User))
{
return new User();
}
throw new NotSupportedException(string.Format("{0} not found for creating.", resourceType.FullName));
}
public void AddResource(ResourceType resourceType, object resource)
{
if (resourceType.InstanceType == typeof(Customer))
{
Customer i = resource as Customer;
if (i != null)
{
Customers.Add(i);
return;
}
}
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
ResidentialCustomeri = resource as ResidentialCustomer;
if (i != null)
{
ResidentialCustomers.Add(i);
return;
}
}
if (resourceType.InstanceType == typeof(User))
{
Useri = resource as User;
if (i != null)
{
Users.Add(i);
return;
}
}
throw new NotSupportedException(string.Format("{0} not found for adding.", resourceType.FullName));
}
public void DeleteResource(object resource)
{
if (resource.GetType() == typeof(Customer))
{
Customers.Remove(resource as Customer);
return;
}
if (resource.GetType() == typeof(ResidentialCustomer))
{
ResidentialCustomers.Remove(resource as ResidentialCustomer);
return;
}
if (resource.GetType() == typeof(User))
{
Users.Remove(resource as User);
return;
}
throw new NotSupportedException(string.Format("{0} not found for deletion.", resource.GetType().FullName));
}
public void SaveChanges()
{
foreach (var item in Customers.Where(i => i.IsModified == true))
item.Save();
foreach (var item in ResidentialCustomers.Where(i => i.IsModified == true))
item.Save();
foreach (var item in Users.Where(i => i.IsModified == true))
item.Save();
}
}
Then, create your data service using the custom data service class and your data context, such as:
public class MyDataService : ODataService<MyDataContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Customers", EntitySetRights.All);
config.SetEntitySetAccessRule("ResidentialCustomers", EntitySetRights.All);
config.SetEntitySetAccessRule("Users", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.DataServiceBehavior.AcceptProjectionRequests = true;
}
}
Lots of wiring up, but pretty straightforward once you've got the hang of it.
How do you give a C# Auto-Property a default value, using a custom attribute?
This is the code I want to see:
class Person
{
[MyDefault("William")]
public string Name { get; set; }
}
I am aware that there is no built in method to initialize the default using an attribute - can I write my own custom class that uses my custom attributes to initialize the default?
If you want to do it with PostSharp (as your tags suggest) then use a Lazy Loading aspect. You can see the one I built here http://programmersunlimited.wordpress.com/2011/03/23/postsharp-weaving-community-vs-professional-reasons-to-get-a-professional-license/
With an aspect you can apply default value to a single property or apply it to multiple properties with a single declaration at the class level.
Lazy loading aspect will use LocationInterceptionAspect base class.
[Serializable]
[LazyLoadingAspect(AttributeExclude=true)]
[MulticastAttributeUsage(MulticastTargets.Property)]
public class LazyLoadingAspectAttribute : LocationInterceptionAspect
{
public object DefaultValue {get; set;}
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue();
if (args.Value != null)
{
return;
}
args.Value = DefaultValue;
args.ProceedSetValue();
}
}
then apply the aspect like so
[LazyLoadingAspect(DefaultValue="SomeValue")]
public string MyProp { get; set; }
You could use a helper class like that:
public class DefaultValueHelper
{
public static void InitializeDefaultValues<T>(T obj)
{
var properties =
(from prop in obj.GetType().GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select new
{
Property = prop,
DefaultValue = attr.Value
}).ToArray();
foreach (var p in properties)
{
p.Property.SetValue(obj, p.DefaultValue, null);
}
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
And call InitializeDefaultValues in the constructor of your class.
class Foo
{
public Foo()
{
DefaultValueHelper.InitializeDefaultValues(this);
}
[DefaultValue("(no name)")]
public string Name { get; set; }
}
EDIT: updated version, which generates and caches a delegate to do the initialization. This is to avoid using reflection every time the method is called for a given type.
public static class DefaultValueHelper
{
private static readonly Dictionary<Type, Action<object>> _initializerCache;
static DefaultValueHelper()
{
_initializerCache = new Dictionary<Type, Action<object>>();
}
public static void InitializeDefaultValues(object obj)
{
if (obj == null)
return;
var type = obj.GetType();
Action<object> initializer;
if (!_initializerCache.TryGetValue(type, out initializer))
{
initializer = MakeInitializer(type);
_initializerCache[type] = initializer;
}
initializer(obj);
}
private static Action<object> MakeInitializer(Type type)
{
var arg = Expression.Parameter(typeof(object), "arg");
var variable = Expression.Variable(type, "x");
var cast = Expression.Assign(variable, Expression.Convert(arg, type));
var assignments =
from prop in type.GetProperties()
let attr = GetDefaultValueAttribute(prop)
where attr != null
select Expression.Assign(Expression.Property(variable, prop), Expression.Constant(attr.Value));
var body = Expression.Block(
new ParameterExpression[] { variable },
new Expression[] { cast }.Concat(assignments));
var expr = Expression.Lambda<Action<object>>(body, arg);
return expr.Compile();
}
private static DefaultValueAttribute GetDefaultValueAttribute(PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.FirstOrDefault();
}
}
If to speculate with Expressions you could make initializing delegates and cache them. It will make code much faster comparing with just pure reflection.
internal static class Initializer
{
private class InitCacheEntry
{
private Action<object, object>[] _setters;
private object[] _values;
public InitCacheEntry(IEnumerable<Action<object, object>> setters, IEnumerable<object> values)
{
_setters = setters.ToArray();
_values = values.ToArray();
if (_setters.Length != _values.Length)
throw new ArgumentException();
}
public void Init(object obj)
{
for (int i = 0; i < _setters.Length; i++)
{
_setters[i](obj, _values[i]);
}
}
}
private static Dictionary<Type, InitCacheEntry> _cache = new Dictionary<Type, InitCacheEntry>();
private static InitCacheEntry MakeCacheEntry(Type targetType)
{
var setters = new List<Action<object, object>>();
var values = new List<object>();
foreach (var propertyInfo in targetType.GetProperties())
{
var attr = (DefaultAttribute) propertyInfo.GetCustomAttributes(typeof (DefaultAttribute), true).FirstOrDefault();
if (attr == null) continue;
var setter = propertyInfo.GetSetMethod();
if (setter == null) continue;
// we have to create expression like (target, value) => ((TObj)target).setter((T)value)
// where T is the type of property and obj is instance being initialized
var targetParam = Expression.Parameter(typeof (object), "target");
var valueParam = Expression.Parameter(typeof (object), "value");
var expr = Expression.Lambda<Action<object, object>>(
Expression.Call(Expression.Convert(targetParam, targetType),
setter,
Expression.Convert(valueParam, propertyInfo.PropertyType)),
targetParam, valueParam);
var set = expr.Compile();
setters.Add(set);
values.Add(attr.DefaultValue);
}
return new InitCacheEntry(setters, values);
}
public static void Init(object obj)
{
Type targetType = obj.GetType();
InitCacheEntry init;
if (!_cache.TryGetValue(targetType, out init))
{
init = MakeCacheEntry(targetType);
_cache[targetType] = init;
}
init.Init(obj);
}
}
You could create a method like this:
public static void FillProperties<T>(T obj)
{
foreach (var property in typeof(T).GetProperties())
{
var attribute = property
.GetCustomAttributes(typeof(DefaultValueAttribute), true)
.Cast<DefaultValueAttribute>()
.SingleOrDefault();
if (attribute != null)
property.SetValue(obj, attribute.Value, null);
}
}
You can then either use a factory method that calls this method or call it directly from the constructor. Note that this usage of reflection is probably not a good idea if you create a lot of objects this way and performance is important.
I wrote a class that allows a derivate to specify which of its properties can be lazy loaded. The code is:
public abstract class SelfHydratingEntity<T> : DynamicObject where T : class {
private readonly Dictionary<string, LoadableBackingField> fields;
public SelfHydratingEntity(T original) {
this.Original = original;
this.fields = this.GetBackingFields().ToDictionary(f => f.Name);
}
public T Original { get; private set; }
protected virtual IEnumerable<LoadableBackingField> GetBackingFields() {
yield break;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
result = field.GetValue();
return true;
} else {
var getter = PropertyAccessor.GetGetter(this.Original.GetType(), binder.Name);
result = getter(this.Original);
return true;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
field.SetValue(value);
return true;
} else {
var setter = PropertyAccessor.GetSetter(this.Original.GetType(), binder.Name);
setter(this.Original, value);
return true;
}
}
}
And a derivate class:
public class SelfHydratingPerson : SelfHydratingEntity<IPerson> {
private readonly IDataRepository dataRepository;
public SelfHydratingDerivate(IDataRepository dataRepository, IPerson person)
: base(person) {
this.dataRepository = dataRepository
}
protected override IEnumerable<LoadableBackingField> GetBackingFields() {
yield return new LoadableBackingField("Address", () => this.dataRepository.Addresses.Get(this.Original.AddressID));
}
}
This works perfectly fine for getting and settings property values, but I get a either a RuntimeBinderException when I implicitly cast or an InvalidCastException with an explicitly cast SelfHydratingEntity back to T.
I know that you can override the DynamicObject.TryConvert method, but I'm wondering what exactly to put in this method. I've read a lot about duck typing today, and have tried out several libraries, but none of them work for this particular scenario. All of the libraries I've tried today generate a wrapper class using Reflection.Emit that makes calls to "get_" and "set_" methods and naturally use reflection to find these methods on the wrapped instance. SelfHydratingEntity of course doesn't have the "get_" and "set_" methods defined.
So, I'm wondering if this kind of thing is even possible. Is there any way to cast an instance of SelfHydratingEntity to T? I'm looking for something like this:
var original = GetOriginalPerson();
dynamic person = new SelfHydratingPerson(new DataRepository(), original);
string name = person.Name; // Gets property value on original
var address = person.Address; // Gets property value using LoadableBackingField registration
var iPerson = (IPerson)person;
- or -
var iPerson = DuckType.As<IPerson>(person);
Have you seen this Duck Typing project. It looks pretty good. I have just found a great example from Mauricio. It uses the Windsor Castle dynamic proxy to intercept method calls
Using the code from Mauricio the following code works like a dream
class Program
{
static void Main(string[] args)
{
dynamic person = new { Name = "Peter" };
var p = DuckType.As<IPerson>(person);
Console.WriteLine(p.Name);
}
}
public interface IPerson
{
string Name { get; set; }
}
public static class DuckType
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
impromptu-interface
https://github.com/ekonbenefits/impromptu-interface
Can static cast interfaces onto objects derived from DynamicObject.
public class CustomProperty<T>
{
private T _value;
public CustomProperty(T val)
{
_value = val;
}
public T Value
{
get { return this._value; }
set { this._value = value; }
}
}
public class CustomPropertyAccess
{
public CustomProperty<string> Name = new CustomProperty<string>("cfgf");
public CustomProperty<int> Age = new CustomProperty<int>(0);
public CustomPropertyAccess() { }
}
//I jest beginer in reflection.
//How can access GetValue of CPA.Age.Value using fuly reflection
private void button1_Click(object sender, EventArgs e)
{
CustomPropertyAccess CPA = new CustomPropertyAccess();
CPA.Name.Value = "lino";
CPA.Age.Value = 25;
//I did like this . this is the error “ Non-static method requires a target.”
MessageBox.Show(CPA.GetType().GetField("Name").FieldType.GetProperty("Value").GetValue(null ,null).ToString());
}
How about a method like this:
public Object GetPropValue(String name, Object obj) {
foreach (String part in name.Split('.')) {
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
And use it like so:
Object val = GetPropValue("Age.Value", CPA);
Read the error message.
Non-static methods and properties are associated with an instance of a class - and so you need to provide an instance when trying to access them through reflection.
In the GetProperty.GetValue method, you need to specify the object for which you want to get the property value. In your case, it would be: GetValue(CPA ,null)