2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-06-26 21:13:13 +00:00

Updated to support IIP v3.3

This commit is contained in:
2019-05-24 05:38:53 +03:00
parent d215e48761
commit 08e95bd4dc
106 changed files with 6643 additions and 1179 deletions

View File

@ -1,4 +1,28 @@
using System;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

View File

@ -1,4 +1,30 @@
using Esiur.Engine;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using Esiur.Data;
using Esiur.Engine;
using Esiur.Resource.Template;
using System;
using System.Collections.Generic;
using System.Linq;
@ -13,5 +39,15 @@ namespace Esiur.Resource
AsyncReply<IResource> Retrieve(uint iid);
bool Put(IResource resource);
string Link(IResource resource);
bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime);
bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime);
bool Remove(IResource resource);
//AsyncReply<PropertyValue[]> GetPropertyRecord(IResource resource, string propertyName, ulong fromAge, ulong toAge);
//AsyncReply<PropertyValue[]> GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate);
//AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, ulong fromAge, ulong toAge);
// AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecordByDate(IResource resource, DateTime fromDate, DateTime toDate);
AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate);
}
}

View File

@ -10,6 +10,7 @@ using Esiur.Net.IIP;
using Esiur.Misc;
using Esiur.Security.Permissions;
using Esiur.Resource.Template;
using Esiur.Security.Authority;
namespace Esiur.Resource
{
@ -21,27 +22,33 @@ namespace Esiur.Resource
IResource resource;
IStore store;
AutoList<IResource, Instance> parents;// = new AutoList<IResource>();
bool inherit;
//bool inherit;
ResourceTemplate template;
AutoList<IPermissionManager, Instance> managers;// = new AutoList<IPermissionManager, Instance>();
AutoList<IPermissionsManager, Instance> managers;// = new AutoList<IPermissionManager, Instance>();
public delegate void ResourceModifiedEvent(IResource resource, string propertyName, object newValue);
//public delegate void ResourceEventOccurredEvent(IResource resource, string eventName, string[] users, DistributedConnection[] connections, object[] args);
public delegate void ResourceEventOccurredEvent(IResource resource, object issuer, Session[] receivers, string eventName, object[] args);
public delegate void ResourceModifiedEvent(IResource resource, string propertyName, object newValue, object oldValue);
public delegate void ResourceEventOccurredEvent(IResource resource, string eventName, string[] receivers, object[] args);
public delegate void ResourceDestroyedEvent(IResource resource);
public event ResourceModifiedEvent ResourceModified;
public event ResourceEventOccurredEvent ResourceEventOccured;
public event ResourceEventOccurredEvent ResourceEventOccurred;
public event ResourceDestroyedEvent ResourceDestroyed;
KeyList<string, object> attributes = new KeyList<string, object>();
KeyList<string, object> attributes;
List<uint> ages = new List<uint>();
private uint age;
List<ulong> ages = new List<ulong>();
List<DateTime> modificationDates = new List<DateTime>();
private ulong instanceAge;
private DateTime instanceModificationDate;
uint id;
/// <summary>
/// Instance attributes are custom properties associated with the instance, a place to store information by IStore.
/// </summary>
@ -53,12 +60,151 @@ namespace Esiur.Resource
}
}
public override string ToString()
{
return name + " (" + Link + ")";
}
public bool RemoveAttributes(string[] attributes = null)
{
if (attributes == null)
this.attributes.Clear();
else
{
foreach (var attr in attributes)
this.attributes.Remove(attr);
}
return true;
}
public Structure GetAttributes(string[] attributes = null)
{
var st = new Structure();
if (attributes == null)
{
var clone = this.attributes.Keys.ToList();
clone.Add("managers");
attributes = clone.ToArray();// this.attributes.Keys.ToList().Add("managers");
}
foreach(var attr in attributes)
{
if (attr == "name")
st["name"] = this.name;
else if (attr == "managers")
{
var mngrs = new List<Structure>();
foreach (var manager in this.managers)
mngrs.Add(new Structure()
{
["type"] = manager.GetType().FullName + "," + manager.GetType().GetTypeInfo().Assembly.GetName().Name,
["settings"] = manager.Settings
});
st["managers"] = mngrs.ToArray();
}
else if (attr == "parents")
{
st["parents"] = parents.ToArray();
}
else if (attr == "children")
{
st["children"] = children.ToArray();
}
else if (attr == "childrenCount")
{
st["childrenCount"] = children.Count;
}
else if (attr == "type")
{
st["type"] = resource.GetType().FullName;
}
else
st[attr] = this.attributes[attr];
}
return st;
}
public bool SetAttributes(Structure attributes, bool clearAttributes = false)
{
try
{
if (clearAttributes)
this.attributes.Clear();
foreach (var attr in attributes)
if (attr.Key == "name")
this.name = attr.Value as string;
else if (attr.Key == "managers")
{
this.managers.Clear();
var mngrs = attr.Value as object[];
foreach (var mngr in mngrs)
{
var m = mngr as Structure;
var type = Type.GetType(m["type"] as string);
if (Codec.ImplementsInterface(type, typeof(IPermissionsManager)))
{
var settings = m["settings"] as Structure;
var manager = Activator.CreateInstance(type) as IPermissionsManager;
manager.Initialize(settings, this.resource);
this.managers.Add(manager);
}
else
return false;
}
}
else
{
this.attributes[attr.Key] = attr.Value;
}
}
catch
{
return false;
}
return true;
}
/*
public Structure GetAttributes()
{
var st = new Structure();
foreach (var a in attributes.Keys)
st[a] = attributes[a];
st["name"] = name;
var mngrs = new List<Structure>();
foreach (var manager in managers)
{
var mngr = new Structure();
mngr["settings"] = manager.Settings;
mngr["type"] = manager.GetType().FullName;
mngrs.Add(mngr);
}
st["managers"] = mngrs;
return st;
}*/
/// <summary>
/// Get the age of a given property index.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <returns>Age.</returns>
public uint GetAge(byte index)
public ulong GetAge(byte index)
{
if (index < ages.Count)
return ages[index];
@ -67,12 +213,107 @@ namespace Esiur.Resource
}
/// <summary>
/// Age of the instance, increments by 1 in every modification.
/// Set the age of a property.
/// </summary>
public uint Age
/// <param name="index">Zero-based property index.</param>
/// <param name="value">Age.</param>
public void SetAge(byte index, ulong value)
{
get { return age; }
internal set { age = value; }
if (index < ages.Count)
{
ages[index] = value;
if (value > instanceAge)
instanceAge = value;
}
}
/// <summary>
/// Set the modification date of a property.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <param name="value">Modification date.</param>
public void SetModificationDate(byte index, DateTime value)
{
if (index < modificationDates.Count)
{
modificationDates[index] = value;
if (value > instanceModificationDate)
instanceModificationDate = value;
}
}
/// <summary>
/// Get modification date of a specific property.
/// </summary>
/// <param name="index">Zero-based property index</param>
/// <returns>Modification date.</returns>
public DateTime GetModificationDate(byte index)
{
if (index < modificationDates.Count)
return modificationDates[index];
else
return DateTime.MinValue;
}
/// <summary>
/// Load property value (used by stores)
/// </summary>
/// <param name="name">Property name</param>
/// <param name="age">Property age</param>
/// <param name="value">Property value</param>
/// <returns></returns>
public bool LoadProperty(string name, ulong age, DateTime modificationDate, object value)
{
var pt = template.GetPropertyTemplate(name);
if (pt == null)
return false;
#if NETSTANDARD1_5
var pi = resource.GetType().GetTypeInfo().GetProperty(name);
#else
var pi = resource.GetType().GetProperty(pt.Name);
#endif
if (pi.PropertyType == typeof(DistributedPropertyContext))
return false;
try
{
if (pi.CanWrite)
pi.SetValue(resource, DC.CastConvert(value, pi.PropertyType));
}
catch(Exception ex)
{
//Console.WriteLine(resource.ToString() + " " + name);
Global.Log(ex);
}
SetAge(pt.Index, age);
SetModificationDate(pt.Index, modificationDate);
return true;
}
/// <summary>
/// Age of the instance, incremented by 1 in every modification.
/// </summary>
public ulong Age
{
get { return instanceAge; }
internal set { instanceAge = value; }
}
/// <summary>
/// Last modification date.
/// </summary>
public DateTime ModificationDate
{
get
{
return instanceModificationDate;
}
}
/// <summary>
@ -88,18 +329,18 @@ namespace Esiur.Resource
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
public bool Deserialize(object[] properties)
{
foreach (var pt in template.Properties)
public bool Deserialize(PropertyValue[] properties)
{
for (byte i = 0; i < properties.Length; i++)
{
#if NETSTANDARD1_5
var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = resource.GetType().GetProperty(pt.Name);
#endif
if (!(properties[pt.Index] is NotModified))
pi.SetValue(resource, properties[pt.Index]);
var pt = this.template.GetPropertyTemplate(i);
if (pt != null)
{
var pv = properties[i];
LoadProperty(pt.Name, pv.Age, pv.Date, pv.Value);
}
}
return true;
}
@ -107,9 +348,9 @@ namespace Esiur.Resource
/// Export all properties with ResourceProperty attributed as bytes array.
/// </summary>
/// <returns></returns>
public object[] Serialize()
public PropertyValue[] Serialize()
{
List<object> props = new List<object>();
List<PropertyValue> props = new List<PropertyValue>();
foreach (var pt in template.Properties)
{
@ -119,7 +360,7 @@ namespace Esiur.Resource
var pi = resource.GetType().GetProperty(pt.Name);
#endif
var rt = pi.GetValue(resource, null);
props.Add(rt);
props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index]));
}
return props.ToArray();
@ -191,10 +432,10 @@ namespace Esiur.Resource
}
*/
/// <summary>
/// If True, the instance can be stored to disk.
/// </summary>
/// <returns></returns>
/// <summary>
/// If True, the instance can be stored to disk.
/// </summary>
/// <returns></returns>
public bool IsStorable()
{
#if NETSTANDARD1_5
@ -207,27 +448,47 @@ namespace Esiur.Resource
}
internal void EmitModification(PropertyTemplate pt, object value)
{
instanceAge++;
var now = DateTime.UtcNow;
ages[pt.Index] = instanceAge;
modificationDates[pt.Index] = now;
if (pt.Storage == StorageMode.NonVolatile)
{
store.Modify(resource, pt.Name, value, ages[pt.Index], now);
}
else if (pt.Storage == StorageMode.Recordable)
{
store.Record(resource, pt.Name, value, ages[pt.Index], now);
}
ResourceModified?.Invoke(resource, pt.Name, value);
}
/// <summary>
/// Notify listeners that a property was modified.
/// </summary>
/// <param name="propertyName"></param>
/// <param name="newValue"></param>
/// <param name="oldValue"></param>
public void Modified([CallerMemberName] string propertyName = "", object newValue = null, object oldValue = null)
public void Modified([CallerMemberName] string propertyName = "")//, object newValue = null)//, object oldValue = null)
{
if (newValue == null)
object value;
if (GetPropertyValue(propertyName, out value))
{
object val;
if (GetPropertyValue(propertyName, out val))
ResourceModified?.Invoke(resource, propertyName, val, oldValue);
var pt = template.GetPropertyTemplate(propertyName);
EmitModification(pt, value);
}
else
ResourceModified?.Invoke(resource, propertyName, newValue, oldValue);
}
internal void EmitResourceEvent(string name, string[] receivers, object[] args)
// internal void EmitResourceEvent(string name, string[] users, DistributedConnection[] connections, object[] args)
internal void EmitResourceEvent(object issuer, Session[] receivers, string name, object[] args)
{
ResourceEventOccured?.Invoke(resource, name, receivers, args);
ResourceEventOccurred?.Invoke(resource, issuer, receivers, name, args);
}
/// <summary>
@ -259,7 +520,7 @@ namespace Esiur.Resource
{
value = pi.GetValue(resource, null);
//if (value is Func<IManager, object>)
// value = (value as Func<IManager, object>)(sender);
// value = (value as Func<IManager, object>)(sender);
return true;
}
}
@ -269,10 +530,11 @@ namespace Esiur.Resource
}
/*
public bool Inherit
{
get { return inherit; }
}
}*/
/// <summary>
/// List of parents.
@ -289,7 +551,7 @@ namespace Esiur.Resource
{
get { return store; }
}
/// <summary>
/// List of children.
/// </summary>
@ -335,6 +597,7 @@ namespace Esiur.Resource
public string Name
{
get { return name; }
set { name = value; }
}
@ -352,38 +615,86 @@ namespace Esiur.Resource
public ResourceTemplate Template
{
get { return template; }
/*
internal set
{
template = Warehouse.GetTemplate(resource.GetType());
// set ages
for (byte i = 0; i < template.Properties.Length; i++)
{
ages.Add(0);
modificationDates.Add(DateTime.MinValue);
}
}
*/
}
/// <summary>
/// Create new instance.
/// </summary>
/// <param name="id">Instance Id.</param>
/// <param name="name">Name of the instance.</param>
/// <param name="resource">Resource to manage.</param>
/// <param name="store">Store responsible for the resource.</param>
public Instance(uint id, string name, IResource resource, IStore store)
/// <summary>
/// Check for permission.
/// </summary>
/// <param name="session">Caller sessions.</param>
/// <param name="action">Action type</param>
/// <param name="member">Function, property or event to check for permission.</param>
/// <param name="inquirer">Permission inquirer.</param>
/// <returns>Ruling.</returns>
public Ruling Applicable(Session session, ActionType action, MemberTemplate member, object inquirer = null)
{
foreach (IPermissionsManager manager in managers)
{
var r = manager.Applicable(this.resource, session, action, member, inquirer);
if (r != Ruling.DontCare)
return r;
}
return Ruling.DontCare;
}
/// <summary>
/// Execution managers.
/// </summary>
public AutoList<IPermissionsManager, Instance> Managers => managers;
/// <summary>
/// Create new instance.
/// </summary>
/// <param name="id">Instance Id.</param>
/// <param name="name">Name of the instance.</param>
/// <param name="resource">Resource to manage.</param>
/// <param name="store">Store responsible for the resource.</param>
public Instance(uint id, string name, IResource resource, IStore store, ResourceTemplate customTemplate = null, ulong age = 0)
{
this.store = store;
this.resource = resource;
this.id = id;
this.name = name;
this.instanceAge = age;
this.attributes = new KeyList<string, object>(this);
children = new AutoList<IResource, Instance>(this);
parents = new AutoList<IResource, Instance>(this);
managers = new AutoList<IPermissionManager, Instance>(this);
managers = new AutoList<IPermissionsManager, Instance>(this);
children.OnAdd += Children_OnAdd;
children.OnRemoved += Children_OnRemoved;
parents.OnAdd += Parents_OnAdd;
parents.OnRemoved += Parents_OnRemoved;
resource.OnDestroy += Resource_OnDestroy;
template = Warehouse.GetTemplate(resource.GetType());
// set ages
if (customTemplate != null)
this.template = customTemplate;
else
this.template = Warehouse.GetTemplate(resource.GetType());
// set ages
for (byte i = 0; i < template.Properties.Length; i++)
{
ages.Add(0);
modificationDates.Add(DateTime.MinValue);
}
// connect events
Type t = resource.GetType();
@ -396,16 +707,62 @@ namespace Esiur.Resource
foreach (var evt in events)
{
if (evt.EventHandlerType != typeof(ResourceEventHanlder))
continue;
//if (evt.EventHandlerType != typeof(ResourceEventHanlder))
// continue;
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
if (evt.EventHandlerType == typeof(ResourceEventHanlder))
{
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
ResourceEventHanlder proxyDelegate = (args) => EmitResourceEvent(null, null, evt.Name, args);
evt.AddEventHandler(resource, proxyDelegate);
}
else if (evt.EventHandlerType == typeof(CustomResourceEventHanlder))
{
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
CustomResourceEventHanlder proxyDelegate = (issuer, receivers, args) => EmitResourceEvent(issuer, receivers, evt.Name, args);
evt.AddEventHandler(resource, proxyDelegate);
}
/*
else if (evt.EventHandlerType == typeof(CustomUsersEventHanlder))
{
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
CustomUsersEventHanlder proxyDelegate = (users, args) => EmitResourceEvent(evt.Name, users, null, args);
evt.AddEventHandler(resource, proxyDelegate);
}
else if (evt.EventHandlerType == typeof(CustomConnectionsEventHanlder))
{
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
CustomConnectionsEventHanlder proxyDelegate = (connections, args) => EmitResourceEvent(evt.Name, null, connections, args);
evt.AddEventHandler(resource, proxyDelegate);
}
else if (evt.EventHandlerType == typeof(CustomReceiversEventHanlder))
{
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
CustomReceiversEventHanlder proxyDelegate = (users, connections, args) => EmitResourceEvent(evt.Name, users, connections, args);
evt.AddEventHandler(resource, proxyDelegate);
}
*/
ResourceEventHanlder proxyDelegate = (receivers, args) => EmitResourceEvent(evt.Name, receivers, args);
evt.AddEventHandler(resource, proxyDelegate);
}
}
@ -416,9 +773,22 @@ namespace Esiur.Resource
private void Children_OnAdd(Instance parent, IResource value)
{
value.Instance.parents.Add(resource);
if (!value.Instance.parents.Contains(resource))
value.Instance.parents.Add(resource);
}
private void Parents_OnRemoved(Instance parent, IResource value)
{
value.Instance.children.Remove(resource);
}
private void Parents_OnAdd(Instance parent, IResource value)
{
if (!value.Instance.children.Contains(resource))
value.Instance.children.Add(resource);
}
private void Resource_OnDestroy(object sender)
{
ResourceDestroyed?.Invoke((IResource)sender);

View File

@ -0,0 +1,47 @@
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Text;
using Esiur.Engine;
namespace Esiur.Resource
{
public class Resource : IResource
{
public Instance Instance { get; set; }
public event DestroyedEvent OnDestroy;
public virtual void Destroy()
{
}
public virtual AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
return new AsyncReply<bool>(true);
}
}
}

View File

@ -1,4 +1,27 @@
using System;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

View File

@ -1,5 +1,30 @@
using Esiur.Data;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using Esiur.Data;
using Esiur.Engine;
using Esiur.Net.IIP;
using Esiur.Security.Authority;
using System;
using System.Collections.Generic;
using System.Linq;
@ -8,6 +33,13 @@ using System.Threading.Tasks;
namespace Esiur.Resource
{
public delegate void ResourceEventHanlder(string[] receivers, params object[] args);
public delegate void ResourceEventHanlder(params object[] args);
// public delegate void CustomUsersEventHanlder(string[] usernames, params object[] args);
//public delegate void CustomReceiversEventHanlder(DistributedConnection[] connections, params object[] args);
//public delegate void CustomInquirerEventHanlder(object inquirer, params object[] args);
public delegate void CustomResourceEventHanlder(object issuer, Session[] receivers, params object[] args);
// public delegate void CustomReceiversEventHanlder(string[] usernames, DistributedConnection[] connections, params object[] args);
}

View File

@ -1,4 +1,28 @@
using System;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

View File

@ -1,4 +1,29 @@
using System;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using Esiur.Resource.Template;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +37,15 @@ namespace Esiur.Resource
{
string readExpansion;
string writeExpansion;
// bool recordable;
//bool storable;
//public bool Recordable => recordable;
//public bool Storable => storable;
StorageMode storage;
public StorageMode Storage => storage;
public string ReadExpansion
{
@ -30,10 +63,11 @@ namespace Esiur.Resource
}
}
public ResourceProperty(string readExpansion = null, string writeExpansion = null)
public ResourceProperty(StorageMode storage = StorageMode.Volatile, string readExpansion = null, string writeExpansion = null)
{
this.readExpansion = readExpansion;
this.writeExpansion = writeExpansion;
this.storage = storage;
}
}
}

View File

@ -1,4 +1,28 @@
using System;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

View File

@ -1,4 +1,28 @@
using Esiur.Data;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using Esiur.Data;
using Esiur.Engine;
using System;
using System.Collections.Generic;

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Esiur.Resource
{
public enum StorageMode : byte
{
Volatile = 0,
NonVolatile,
Recordable
}
}

View File

@ -28,6 +28,11 @@ namespace Esiur.Resource.Template
return BinaryList.ToBytes((byte)0x40, (byte)name.Length, name);
}
public EventTemplate() { Type = MemberType.Event; }
public EventTemplate(ResourceTemplate template, byte index, string name, string expansion)
:base(template, MemberType.Property, index, name)
{
this.Expansion = expansion;
}
}
}

View File

@ -37,6 +37,11 @@ namespace Esiur.Resource.Template
}
public FunctionTemplate() { Type = MemberType.Function; }
public FunctionTemplate(ResourceTemplate template, byte index, string name,bool isVoid, string expansion)
:base(template, MemberType.Property, index, name)
{
this.IsVoid = isVoid;
this.Expansion = expansion;
}
}
}

View File

@ -16,9 +16,26 @@ namespace Esiur.Resource.Template
Event = 2,
}
public byte Index { get; set; }
public string Name { get; set; }
public MemberType Type { get; set; }
public byte Index => index;
public string Name => name;
public MemberType Type => type;
ResourceTemplate template;
string name;
MemberType type;
byte index;
public ResourceTemplate Template => template;
public MemberTemplate(ResourceTemplate template, MemberType type, byte index, string name)
{
this.template = template;
this.type = type;
this.index = index;
this.name = name;
}
public string Fullname => template.ClassName + "." + Name;
public virtual byte[] Compose()
{

View File

@ -16,13 +16,27 @@ namespace Esiur.Resource.Template
ReadWrite
}
//bool ReadOnly;
//IIPTypes::DataType ReturnType;
public PropertyPermission Permission {
get;
set;
}
/*
public bool Recordable
{
get;
set;
}*/
public StorageMode Storage
{
get;
set;
}
public string ReadExpansion
{
get;
@ -35,37 +49,46 @@ namespace Esiur.Resource.Template
set;
}
/*
public bool Storable
{
get;
set;
}
}*/
public override byte[] Compose()
{
var name = base.Compose();
var pv = ((byte)(Permission) << 1) | (Storage == StorageMode.Recordable ? 1 : 0);
if (WriteExpansion != null && ReadExpansion != null)
{
var rexp = DC.ToBytes(ReadExpansion);
var wexp = DC.ToBytes(WriteExpansion);
return BinaryList.ToBytes((byte)(0x38 | (byte)Permission), wexp.Length, wexp, rexp.Length, rexp, (byte)name.Length, name);
return BinaryList.ToBytes((byte)(0x38 | pv), wexp.Length, wexp, rexp.Length, rexp, (byte)name.Length, name);
}
else if (WriteExpansion != null)
{
var wexp = DC.ToBytes(WriteExpansion);
return BinaryList.ToBytes((byte)(0x30 | (byte)Permission), wexp.Length, wexp, (byte)name.Length, name);
return BinaryList.ToBytes((byte)(0x30 | pv), wexp.Length, wexp, (byte)name.Length, name);
}
else if (ReadExpansion != null)
{
var rexp = DC.ToBytes(ReadExpansion);
return BinaryList.ToBytes((byte)(0x28 | (byte)Permission), rexp.Length, rexp, (byte)name.Length, name);
return BinaryList.ToBytes((byte)(0x28 | pv), rexp.Length, rexp, (byte)name.Length, name);
}
else
return BinaryList.ToBytes((byte)(0x20 | (byte)Permission), (byte)name.Length, name);
return BinaryList.ToBytes((byte)(0x20 | pv), (byte)name.Length, name);
}
public PropertyTemplate() { Type = MemberType.Property; }
public PropertyTemplate(ResourceTemplate template, byte index, string name, string read, string write, StorageMode storage)
:base(template, MemberType.Property, index, name)
{
//this.Recordable = recordable;
this.Storage = storage;
this.ReadExpansion = read;
this.WriteExpansion = write;
}
}
}

View File

@ -155,12 +155,8 @@ namespace Esiur.Resource.Template
var ps = (ResourceProperty[])pi.GetCustomAttributes(typeof(ResourceProperty), true);
if (ps.Length > 0)
{
var pt = new PropertyTemplate();
pt.Name = pi.Name;
pt.Index = i++;
pt.ReadExpansion = ps[0].ReadExpansion;
pt.WriteExpansion = ps[0].WriteExpansion;
properties.Add(pt);
var pt = new PropertyTemplate(this, i++, pi.Name, ps[0].ReadExpansion, ps[0].WriteExpansion, ps[0].Storage);
properties.Add(pt);
}
}
@ -171,10 +167,7 @@ namespace Esiur.Resource.Template
var es = (ResourceEvent[])ei.GetCustomAttributes(typeof(ResourceEvent), true);
if (es.Length > 0)
{
var et = new EventTemplate();
et.Name = ei.Name;
et.Index = i++;
et.Expansion = es[0].Expansion;
var et = new EventTemplate(this, i++, ei.Name, es[0].Expansion);
events.Add(et);
}
}
@ -185,11 +178,7 @@ namespace Esiur.Resource.Template
var fs = (ResourceFunction[])mi.GetCustomAttributes(typeof(ResourceFunction), true);
if (fs.Length > 0)
{
var ft = new FunctionTemplate();
ft.Name = mi.Name;
ft.Index = i++;
ft.IsVoid = mi.ReturnType == typeof(void);
ft.Expansion = fs[0].Expansion;
var ft = new FunctionTemplate(this, i++, mi.Name, mi.ReturnType == typeof(void), fs[0].Expansion);
functions.Add(ft);
}
}
@ -258,69 +247,76 @@ namespace Esiur.Resource.Template
if (type == 0) // function
{
var ft = new FunctionTemplate();
ft.Index = functionIndex++;
var expansion = ((data[offset] & 0x10) == 0x10);
ft.IsVoid = ((data[offset++] & 0x08) == 0x08);
ft.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
string expansion = null;
var hasExpansion = ((data[offset] & 0x10) == 0x10);
var isVoid = ((data[offset++] & 0x08) == 0x08);
var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (expansion) // expansion ?
if (hasExpansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
ft.Expansion = data.GetString(offset, cs);
expansion = data.GetString(offset, cs);
offset += cs;
}
var ft = new FunctionTemplate(od, functionIndex++, name, isVoid, expansion);
od.functions.Add(ft);
}
else if (type == 1) // property
{
var pt = new PropertyTemplate();
pt.Index = propertyIndex++;
var readExpansion = ((data[offset] & 0x8) == 0x8);
var writeExpansion = ((data[offset] & 0x10) == 0x10);
pt.Permission = (PropertyTemplate.PropertyPermission)((data[offset++] >> 1) & 0x3);
pt.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
string readExpansion = null, writeExpansion = null;
var hasReadExpansion = ((data[offset] & 0x8) == 0x8);
var hasWriteExpansion = ((data[offset] & 0x10) == 0x10);
var recordable = ((data[offset] & 1) == 1);
var permission = (PropertyTemplate.PropertyPermission)((data[offset++] >> 1) & 0x3);
var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (readExpansion) // expansion ?
if (hasReadExpansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
pt.ReadExpansion = data.GetString(offset, cs);
readExpansion = data.GetString(offset, cs);
offset += cs;
}
if (writeExpansion) // expansion ?
if (hasWriteExpansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
pt.WriteExpansion = data.GetString(offset, cs);
writeExpansion = data.GetString(offset, cs);
offset += cs;
}
var pt = new PropertyTemplate(od, propertyIndex++, name, readExpansion, writeExpansion, recordable ? StorageMode.Recordable : StorageMode.Volatile);
od.properties.Add(pt);
}
else if (type == 2) // Event
{
var et = new EventTemplate();
et.Index = eventIndex++;
var expansion = ((data[offset++] & 0x10) == 0x10);
et.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]);
string expansion = null;
var hasExpansion = ((data[offset++] & 0x10) == 0x10);
var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]);
offset += (uint)data[offset] + 1;
if (expansion) // expansion ?
if (hasExpansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
et.Expansion = data.GetString(offset, cs);
expansion = data.GetString(offset, cs);
offset += cs;
}
var et = new EventTemplate(od, eventIndex++, name, expansion);
od.events.Add(et);
}

View File

@ -1,6 +1,31 @@
using Esiur.Data;
/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using Esiur.Data;
using Esiur.Engine;
using Esiur.Resource.Template;
using Esiur.Security.Permissions;
using System;
using System.Collections.Generic;
using System.Linq;
@ -14,12 +39,20 @@ namespace Esiur.Resource
{
//static byte prefixCounter;
static List<IStore> stores = new List<IStore>();
static AutoList<IResource, Instance> stores = new AutoList<IResource, Instance>(null);
static Dictionary<uint, IResource> resources = new Dictionary<uint, IResource>();
static uint resourceCounter = 0;
static KeyList<Guid, ResourceTemplate> templates = new KeyList<Guid, ResourceTemplate>();
static bool storeIsOpen = false;
public delegate void StoreConnectedEvent(IStore store, string name);
public delegate void StoreDisconnectedEvent(IStore store);
public static event StoreConnectedEvent StoreConnected;
public static event StoreDisconnectedEvent StoreDisconnected;
/// <summary>
/// Get a store by its name.
@ -30,7 +63,7 @@ namespace Esiur.Resource
{
foreach (var s in stores)
if (s.Instance.Name == name)
return s;
return s as IStore;
return null;
}
@ -44,7 +77,7 @@ namespace Esiur.Resource
if (resources.ContainsKey(id))
return new AsyncReply<IResource>(resources[id]);
else
return null;
return new AsyncReply<IResource>(null);
}
/// <summary>
@ -59,22 +92,38 @@ namespace Esiur.Resource
foreach (var store in stores)
bag.Add(store.Trigger(ResourceTrigger.Initialize));
foreach (var store in stores)
bag.Add(store.Trigger(ResourceTrigger.SystemInitialized));
bag.Seal();
var rt = new AsyncReply<bool>();
bag.Then((x) =>
{
foreach(var b in x)
foreach (var b in x)
if (!b)
{
rt.Trigger(false);
return;
}
rt.Trigger(true);
var rBag = new AsyncBag<bool>();
foreach (var rk in resources)
rBag.Add(rk.Value.Trigger(ResourceTrigger.SystemInitialized));
rBag.Seal();
rBag.Then(y =>
{
foreach (var b in y)
if (!b)
{
rt.Trigger(false);
return;
}
rt.Trigger(true);
storeIsOpen = true;
});
});
return rt;
@ -122,6 +171,63 @@ namespace Esiur.Resource
return rt;
}
private static IResource[] QureyIn(string[] path, int index, AutoList<IResource, Instance> resources)
{
var rt = new List<IResource>();
if (index == path.Length - 1)
{
if (path[index] == "")
foreach (IResource child in resources)
rt.Add(child);
else
foreach (IResource child in resources)
if (child.Instance.Name == path[index])
rt.Add(child);
}
else
foreach (IResource child in resources)
if (child.Instance.Name == path[index])
rt.AddRange(QureyIn(path, index+1, child.Instance.Children));
return rt.ToArray();
}
public static AsyncReply<IResource[]> Query(string path)
{
if (path == null || path == "")
{
var roots = stores.Where(s => s.Instance.Parents.Count == 0).ToArray();
return new AsyncReply<IResource[]>(roots);
}
else
{
var rt = new AsyncReply<IResource[]>();
Get(path).Then(x =>
{
var p = path.Split('/');
if (x == null)
{
rt.Trigger(QureyIn(p, 0, stores));
}
else
{
var ar = QureyIn(p, 0, stores).Where(r => r != x).ToList();
ar.Insert(0, x);
rt.Trigger(ar.ToArray());
}
});
return rt;
}
}
/// <summary>
/// Get a resource by its path.
/// Resource path is sperated by '/' character, e.g. "system/http".
@ -169,9 +275,12 @@ namespace Esiur.Resource
/// <param name="name">Resource name.</param>
/// <param name="store">IStore that manages the resource. Can be null if the resource is a store.</param>
/// <param name="parent">Parent resource. if not presented the store becomes the parent for the resource.</param>
public static void Put(IResource resource, string name, IStore store = null, IResource parent = null)
public static void Put(IResource resource, string name, IStore store = null, IResource parent = null, ResourceTemplate customTemplate = null, ulong age = 0, IPermissionsManager manager = null)
{
resource.Instance = new Instance(resourceCounter++, name, resource, store);
resource.Instance = new Instance(resourceCounter++, name, resource, store, customTemplate, age);
if (manager != null)
resource.Instance.Managers.Add(manager);
if (store == parent)
parent = null;
@ -184,19 +293,27 @@ namespace Esiur.Resource
else
parent.Instance.Children.Add(resource);
if (resource is IStore)
{
stores.Add(resource as IStore);
StoreConnected?.Invoke(resource as IStore, name);
}
else
store.Put(resource);
resources.Add(resource.Instance.Id, resource);
if (!storeIsOpen)
resource.Trigger(ResourceTrigger.Initialize);
}
public static T New<T>(string name, IStore store = null, IResource parent = null)
public static T New<T>(string name, IStore store = null, IResource parent = null, IPermissionsManager manager = null)
{
var res = Activator.CreateInstance(typeof(T)) as IResource;
Put(res, name, store, parent);
Put(res, name, store, parent, null, 0, manager);
return (T)res;
}
@ -206,7 +323,7 @@ namespace Esiur.Resource
/// <param name="template">Resource template.</param>
public static void PutTemplate(ResourceTemplate template)
{
if (templates.ContainsKey(template.ClassId))
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
}
@ -254,5 +371,36 @@ namespace Esiur.Resource
return null;
}
public static bool Remove(IResource resource)
{
if (resource.Instance == null)
return false;
if (resources.ContainsKey(resource.Instance.Id))
resources.Remove(resource.Instance.Id);
else
return false;
if (resource is IStore)
{
stores.Remove(resource as IStore);
// remove all objects associated with the store
var toBeRemoved = resources.Values.Where(x => x.Instance.Store == resource);
foreach (var o in toBeRemoved)
Remove(o);
StoreDisconnected?.Invoke(resource as IStore);
}
if (resource.Instance.Store != null)
resource.Instance.Store.Remove(resource);
resource.Destroy();
return true;
}
}
}