Search code examples
c#jsonbreezebreeze-sharp

Dynamic Objects With BreezeSharp


I am working on a project which requires to work with dynamic objects with BreezeSharp. I am receiving the structure of my objects via a JSON file such as:

[{
    name: 'Admin',
    serviceAddress: 'http://abcd.com/breeze/admins',
    dataProperties: {
    Id: { type: breeze.DataType.Int32 },
    Title: { nullable: true },
    ContentTypeId: {},
    Created: { type: breeze.DataType.DateTime },
    Modified: { type: breeze.DataType.DateTime },
    File: { complexType: 'Document:#File' },
},
{
    name: 'Car',
    serviceAddress: 'http://abcd.com/breeze/cars',
    dataProperties: {
    Id: { type: breeze.DataType.Int32 },
    Model: { nullable: true },
    Created: { type: breeze.DataType.DateTime },
    Modified: { type: breeze.DataType.DateTime },
}]

and I want to auto generate objects which inherits from Breeze.Sharp.BaseEntity with the properties listed.

I will also have to create the breeze entity manager after the dynamic object has been created.

The reason why I need this is because I have a SharePoint app which uses BreezeJS. The entities are based of a JSON file over there.

So I want to create a Desktop app which would be using the same entities based of the JSON file. I am not sure it is possible to create dynamic objects which inherit the BaseEntity class.


Solution

  • yes, this is possible, but you need to put some work and code into it. Here is the idea (untested):

    Create a class that inherits from BaseEntity and implements IDynamicMetaObjectProvider. Then you need to build functions that convert your function definitions to dynamic properties:

    public class DynamicBreezeEntity : BaseEntity, IDynamicMetaObjectProvider
    {
        private readonly Dictionary<string, PropertyDefinition> _properties;
    
        public DynamicBreezeEntity ()
        {
            _properties = new Dictionary<string, PropertyDefinition>();
        }
    
        public void DefineProperty(string name, Type type, bool isNullable = false)
        {
            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");
    
            if (_properties.ContainsKey(name))
                throw new ArgumentException("Property already defined.", "name");
    
            if (type == null)
                throw new ArgumentNullException("type");
    
            if (isNullable && !type.IsValueType)
                throw new ArgumentException("Only value types can be nullable.", "type");            
    
            if (isNullable)
            {
                type = Nullable.GetUnderlyingType(type);
                if (type.IsValueType)
                    type = typeof(Nullable<>).MakeGenericType(type);
            }
    
            _properties.Add(name, new PropertyDefinition { Type = type });
        }
    
        public object GetValue(string name)
        {
            PropertyDefinition def;
            if (_properties.TryGetValue(name, out def))
                return def.Value;
    
            throw new ArgumentException("Property not defined.", "name");
        }
    
        public void SetValue(string name, object value)
        {
            // more work todo here: handle value == null correctly
    
            PropertyDefinition def;
            if (_properties.TryGetValue(name, out def) && def.Type.IsAssignableFrom(value.GetType()))            
                def.Value = value;
    
            throw new ArgumentException("Property not defined.", "name");
        }
    
        public IEnumerable<string> GetPropertyNames()
        {
            return _properties.Keys.ToList();
        }
    
        DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
        {
            return new Proxy(this);
        }   
    
        private class PropertyDefinition
        {
            public Type Type { get; set; }
    
            public object Value { get; set; }
        }
    
        private class Proxy : DynamicMetaObject 
        {
             public Proxy(DynamicBreezeEntity host, Expression expression)
                 : this(host, expression, BindingRestrictions.Empty) { }
    
             public Proxy(DynamicBreezeEntity host, Expression expression, BindingRestrictions restrictions)
                 : base(expressiom restrictions, host) { }
    
             private DynamicBreezeEntity Host
             {
                 get { return (DynamicBreezeEntity)Value; }
             }
    
             private BindingRestrictions GetBindingRestrictions()
             {
                 var restrictions = BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType);
                 return restrictions.Merge(BindingRestrictions.GetInstanceRestriction(this.Expression, this.Host));            
             }
    
             public override IEnumerable<string> GetDynamicMemberNames()
             {
                 return this.Host.GetPropertyNames();
             }
    
             public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
             {
                var arguments = new Expression[] { Expression.Constant(binder.Name) };
                var method = typeof(DynamicBreezeEntity).GetMethod("GetValue");
    
                var callExpression = Expression.Convert(Expression.Call(Expression.Convert(this.Expressiom, this.LimitType), method, arguments), binder.ReturnType);
                return new DynamicMetaObject(callExpression, GetBindingRestrictions());
             }
    
             public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
             {
                 var arguments = new Expression[] {
                     Expression.Constant(binder.Name),
                     Expression.Convert(value.Expression, typeof(object))
                 };
                 var method = typeof(DynamicBreezeEntity).GetMethod("SetValue");
                 return new DynamicMetaObject(
                     Expression.Call(Expression.Convert(this.Expression, this.LimitType), method, arguments),
                     this.GetBindingRestrictions()
                 );
             }
        }
    }
    

    I do not know Breeze, BaseEntity may has abstract properties/methods that you need to implement. I did not focus on that.

    You can use now as

    var breeze = new DynamicBreezeEntity();
    breeze.DefineProperty("Id", typeof(Int32));
    
    dynamic dynBreeze = breeze;
    dynBreeze.Id = "Foo";
    
    Console.WriteLine("Id: {0}", dynBreeze.Id);
    

    Code is probably not complete (have no VS access at the moment), but should point you in the right direction.