diff --git a/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj b/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj index 8aa98d7..72a67d6 100644 --- a/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj +++ b/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj @@ -11,7 +11,7 @@ http://www.esiur.com https://github.com/esiur/esiur-dotnet/ True - 1.2.0 + 1.2.2 diff --git a/Esiur.Stores.MongoDB/MongoDBStore.cs b/Esiur.Stores.MongoDB/MongoDBStore.cs index 40dce94..18fa2ea 100644 --- a/Esiur.Stores.MongoDB/MongoDBStore.cs +++ b/Esiur.Stores.MongoDB/MongoDBStore.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Esiur.Resource.Template; using System.Linq; using Esiur.Security.Permissions; +using Esiur.Proxy; namespace Esiur.Stores.MongoDB { @@ -100,8 +101,9 @@ namespace Esiur.Stores.MongoDB return new AsyncReply(null); var document = list[0]; + var type = Type.GetType(document["classname"].AsString); - IResource resource = (IResource)Activator.CreateInstance(Type.GetType(document["classname"].AsString)); + IResource resource = (IResource)Activator.CreateInstance(ResourceProxy.GetProxy(type)); resources.Add(document["_id"].AsObjectId.ToString(), resource); Warehouse.Put(resource, document["name"].AsString, this); diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj index 356bf69..489fd08 100644 --- a/Esiur/Esiur.csproj +++ b/Esiur/Esiur.csproj @@ -7,10 +7,10 @@ https://github.com/esiur/esiur-dotnet/blob/master/LICENSE http://www.esiur.com true - 1.2.3 + 1.2.5 https://github.com/esiur/esiur-dotnet Ahmed Kh. Zamil - 1.2.3.0 + 1.2.5.0 Esiur Foundation @@ -44,6 +44,7 @@ + \ No newline at end of file diff --git a/Esiur/Net/IIP/DistributedConnectionProtocol.cs b/Esiur/Net/IIP/DistributedConnectionProtocol.cs index 31e74c4..43861c5 100644 --- a/Esiur/Net/IIP/DistributedConnectionProtocol.cs +++ b/Esiur/Net/IIP/DistributedConnectionProtocol.cs @@ -1369,11 +1369,16 @@ namespace Esiur.Net.IIP } else { + + /* #if NETSTANDARD1_5 var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); #else var pi = r.GetType().GetProperty(pt.Name); -#endif +#endif*/ + + var pi = pt.Info; + if (pi != null) { diff --git a/Esiur/Proxy/ResourceProxy.cs b/Esiur/Proxy/ResourceProxy.cs new file mode 100644 index 0000000..9549f8f --- /dev/null +++ b/Esiur/Proxy/ResourceProxy.cs @@ -0,0 +1,151 @@ +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; + +namespace Esiur.Proxy +{ + public static class ResourceProxy + { + static Dictionary cache = new Dictionary(); + +#if NETSTANDARD1_5 + static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified"); + static MethodInfo instanceGet = typeof(IResource).GetTypeInfo().GetProperty("Instance").GetGetMethod(); +#else + static MethodInfo modifyMethod = typeof(Instance).GetMethod("Modified"); + static MethodInfo instanceGet = typeof(IResource).GetProperty("Instance").GetGetMethod(); +#endif + + + public static Type GetProxy(Type type) + { + + if (cache.ContainsKey(type)) + return cache[type]; + +#if NETSTANDARD1_5 + var typeInfo = type.GetTypeInfo(); + + if (typeInfo.IsSealed) + throw new Exception("Sealed class can't be proxied."); + + var props = from p in typeInfo.GetProperties() + where p.CanWrite && p.GetSetMethod().IsVirtual && + p.GetCustomAttributes(typeof(ResourceProperty), false).Count() > 0 + select p; + +#else + if (type.IsSealed) + throw new Exception("Sealed class can't be proxied."); + + var props = from p in type.GetProperties() + where p.CanWrite && p.GetSetMethod().IsVirtual && + p.GetCustomAttributes(typeof(ResourceProperty), false).Count() > 0 + select p; + +#endif + var assemblyName = new AssemblyName("Esiur.Proxy.T." + type.Namespace); + var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); + var typeName = Assembly.CreateQualifiedName(assemblyName.FullName, type.Name); + + var typeBuilder = moduleBuilder.DefineType(typeName, + TypeAttributes.Public | TypeAttributes.Class, type); + + foreach (PropertyInfo propertyInfo in props) + CreateProperty(propertyInfo, typeBuilder); + + + +#if NETSTANDARD1_5 + var t = typeBuilder.CreateTypeInfo().AsType(); + cache.Add(type, t); + return t; +#else + + var t = typeBuilder.CreateType(); + cache.Add(type, t); + return t; +#endif + } + + public static Type GetProxy() + where T : IResource + { + return GetProxy(typeof(T)); + } + + + + private static void CreateProperty(PropertyInfo pi, TypeBuilder typeBuilder) + { + var propertyBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, null); + + // Create set method + MethodBuilder builder = typeBuilder.DefineMethod("set_" + pi.Name, + MethodAttributes.Public | MethodAttributes.Virtual, null, new Type[] { pi.PropertyType }); + builder.DefineParameter(1, ParameterAttributes.None, "value"); + ILGenerator g = builder.GetILGenerator(); + + Label callModified = g.DefineLabel(); + Label exitMethod = g.DefineLabel(); + + /* + IL_0000: ldarg.0 + IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance() + // (no C# code) + IL_0006: dup + IL_0007: brtrue.s IL_000c + + IL_0009: pop + // } + IL_000a: br.s IL_0017 + + // (no C# code) + IL_000c: ldstr "Level3" + IL_0011: call instance void [Esiur]Esiur.Resource.Instance::Modified(string) + IL_0016: nop + + IL_0017: ret + + */ + + // Add IL code for set method + g.Emit(OpCodes.Nop); + g.Emit(OpCodes.Ldarg_0); + g.Emit(OpCodes.Ldarg_1); + g.Emit(OpCodes.Call, pi.GetSetMethod()); + + /* + IL_0000: ldarg.0 + IL_0001: call instance class [Esiur]Esiur.Resource.Instance [Esiur]Esiur.Resource.Resource::get_Instance() + IL_0006: ldstr "Level3" + IL_000b: callvirt instance void [Esiur]Esiur.Resource.Instance::Modified(string) + IL_0010: ret + */ + + // Call property changed for object + g.Emit(OpCodes.Nop); + g.Emit(OpCodes.Ldarg_0); + g.Emit(OpCodes.Call, instanceGet); + + g.Emit(OpCodes.Dup); + g.Emit(OpCodes.Brtrue_S, callModified); + g.Emit(OpCodes.Pop); + g.Emit(OpCodes.Br_S, exitMethod); + + g.MarkLabel(callModified); + g.Emit(OpCodes.Ldstr, pi.Name); + g.Emit(OpCodes.Callvirt, modifyMethod); + g.Emit(OpCodes.Nop); + g.MarkLabel(exitMethod); + g.Emit(OpCodes.Ret); + propertyBuilder.SetSetMethod(builder); + } + + } +} diff --git a/Esiur/Resource/Instance.cs b/Esiur/Resource/Instance.cs index c8ef27d..473e7fe 100644 --- a/Esiur/Resource/Instance.cs +++ b/Esiur/Resource/Instance.cs @@ -270,19 +270,22 @@ namespace Esiur.Resource if (pt == null) return false; + /* #if NETSTANDARD1_5 - var pi = resource.GetType().GetTypeInfo().GetProperty(name); + var pi = resource.GetType().GetTypeInfo().GetProperty(name, new[] { resource.GetType() }); #else var pi = resource.GetType().GetProperty(pt.Name); #endif - if (pi.PropertyType == typeof(DistributedPropertyContext)) +*/ + + if (pt.Info.PropertyType == typeof(DistributedPropertyContext)) return false; try { - if (pi.CanWrite) - pi.SetValue(resource, DC.CastConvert(value, pi.PropertyType)); + if (pt.Info.CanWrite) + pt.Info.SetValue(resource, DC.CastConvert(value, pt.Info.PropertyType)); } catch(Exception ex) { @@ -354,12 +357,16 @@ namespace Esiur.Resource foreach (var pt in template.Properties) { + /* #if NETSTANDARD1_5 var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name); #else var pi = resource.GetType().GetProperty(pt.Name); #endif - var rt = pi.GetValue(resource, null); +*/ + + + var rt = pt.Info.GetValue(resource, null); props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index])); } @@ -474,7 +481,7 @@ namespace Esiur.Resource /// /// /// - public void Modified([CallerMemberName] string propertyName = "")//, object newValue = null)//, object oldValue = null) + public void Modified([CallerMemberName] string propertyName = "") { object value; if (GetPropertyValue(propertyName, out value)) @@ -499,16 +506,20 @@ namespace Esiur.Resource /// True, if the resource has the property. public bool GetPropertyValue(string name, out object value) { + /* #if NETSTANDARD1_5 PropertyInfo pi = resource.GetType().GetTypeInfo().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); #else PropertyInfo pi = resource.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); #endif +*/ - if (pi != null) + var pt = template.GetPropertyTemplate(name); + + if (pt != null && pt.Info != null) { - + /* #if NETSTANDARD1_5 object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false).ToArray(); @@ -523,6 +534,11 @@ namespace Esiur.Resource // value = (value as Func)(sender); return true; } + */ + + value = pt.Info.GetValue(resource, null); + return true; + } value = null; diff --git a/Esiur/Resource/Template/PropertyTemplate.cs b/Esiur/Resource/Template/PropertyTemplate.cs index 9f6fade..ed77f92 100644 --- a/Esiur/Resource/Template/PropertyTemplate.cs +++ b/Esiur/Resource/Template/PropertyTemplate.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -17,6 +18,12 @@ namespace Esiur.Resource.Template } + public PropertyInfo Info + { + get; + set; + } + //bool ReadOnly; //IIPTypes::DataType ReturnType; public PropertyPermission Permission { diff --git a/Esiur/Resource/Template/ResourceTemplate.cs b/Esiur/Resource/Template/ResourceTemplate.cs index fa54fa0..06fcc6d 100644 --- a/Esiur/Resource/Template/ResourceTemplate.cs +++ b/Esiur/Resource/Template/ResourceTemplate.cs @@ -126,6 +126,10 @@ namespace Esiur.Resource.Template public ResourceTemplate(Type type) { + + if (type.Namespace.Contains("Esiur.Proxy.T")) + type = type.GetTypeInfo().BaseType; + // set guid var typeName = Encoding.UTF8.GetBytes(type.FullName); @@ -156,7 +160,8 @@ namespace Esiur.Resource.Template if (ps.Length > 0) { var pt = new PropertyTemplate(this, i++, pi.Name, ps[0].ReadExpansion, ps[0].WriteExpansion, ps[0].Storage); - properties.Add(pt); + pt.Info = pi; + properties.Add(pt); } } diff --git a/Esiur/Resource/Warehouse.cs b/Esiur/Resource/Warehouse.cs index 54f4d27..a95bb42 100644 --- a/Esiur/Resource/Warehouse.cs +++ b/Esiur/Resource/Warehouse.cs @@ -24,6 +24,7 @@ SOFTWARE. using Esiur.Data; using Esiur.Engine; +using Esiur.Proxy; using Esiur.Resource.Template; using Esiur.Security.Permissions; using System; @@ -347,8 +348,10 @@ namespace Esiur.Resource } public static T New(string name, IStore store = null, IResource parent = null, IPermissionsManager manager = null) + where T:IResource { - var res = Activator.CreateInstance(typeof(T)) as IResource; + var type = ResourceProxy.GetProxy(); + var res = Activator.CreateInstance(type) as IResource; Put(res, name, store, parent, null, 0, manager); return (T)res; } diff --git a/Test/MyMembership.cs b/Test/MyMembership.cs index 5034f95..0a72bd5 100644 --- a/Test/MyMembership.cs +++ b/Test/MyMembership.cs @@ -9,7 +9,7 @@ using System.Text; namespace Test { - class MyMembership : IMembership + public class MyMembership : IMembership { public Instance Instance { get; set; } diff --git a/Test/MyObject.cs b/Test/MyObject.cs index 87d39b5..0869562 100644 --- a/Test/MyObject.cs +++ b/Test/MyObject.cs @@ -9,14 +9,14 @@ using System.Threading; namespace Test { - class MyObject : Resource + public class MyObject : Resource { - + [ResourceEvent] public event ResourceEventHanlder LevelUp; [ResourceEvent] public event ResourceEventHanlder LevelDown; - + public MyObject() { Info = new Structure(); @@ -27,7 +27,7 @@ namespace Test Level = 5; } - + [ResourceFunction] public int Add(int value) { @@ -113,6 +113,7 @@ namespace Test } } + /* int level; [ResourceProperty] public int Level @@ -124,6 +125,21 @@ namespace Test Instance?.Modified(); } } + */ + + [ResourceProperty] + public virtual int Level + { + get; + set; + } + + [ResourceProperty] + public int Level3 + { + get => 0; + set => Instance?.Modified(); + } } diff --git a/Test/Program.cs b/Test/Program.cs index b467762..b5d70c7 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -41,12 +41,13 @@ namespace Test { static MyObject myObject; static DistributedResource remoteObject; - - + static async Task Main(string[] args) { + + //AsyncContext.Run(() => ()); // Create stores to keep objects. @@ -85,6 +86,11 @@ namespace Test else myObject =(MyObject) (await Warehouse.Get("db/my"));//.Then((o) => { myObject = (MyObject)o; }); + + //var obj = ProxyObject.(); + //Warehouse.Put(obj, "dd", system); + //obj.Level2= 33; + // Create new distributed server object var iip = Warehouse.New("iip", system); // Set membership which handles authentication. @@ -136,7 +142,10 @@ namespace Test running = false; }); else - Console.WriteLine(myObject.Name + " " + myObject.Level); + { + myObject.Level = 88; + Console.WriteLine(myObject.Name + " " + myObject.Level ); + } }