2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-05-06 11:32:59 +00:00

EntityCore

This commit is contained in:
Ahmed Zamil 2020-02-15 11:21:28 +03:00
parent 3205499747
commit 7a21f6a928
27 changed files with 962 additions and 830 deletions

View File

@ -0,0 +1,71 @@
/*
Copyright (c) 2020 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 Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
using Esyur.Core;
using Esyur.Resource;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Esyur.Stores.EntityCore
{
public class EntityResource : IResource
{
public event DestroyedEvent OnDestroy;
public event PropertyChangedEventHandler PropertyChanged;
[NotMapped]
public Instance Instance { get; set; }
public EntityResource()
{
}
protected virtual void Create()
{
}
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
Create();
return new AsyncReply<bool>(true);
}
public void Destroy()
{
//throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,153 @@
/*
Copyright (c) 2020 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 Esyur.Core;
using Esyur.Data;
using Esyur.Resource;
using Esyur.Resource.Template;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore.Proxies;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Esyur.Stores.EntityCore
{
public class EntityStore : IStore
{
public Instance Instance { get; set; }
public event DestroyedEvent OnDestroy;
/*
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var extension = optionsBuilder.Options.FindExtension<EsyurExtension>()
?? new EsyurExtension();
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
//optionsBuilder.UseLazyLoadingProxies();
base.OnConfiguring(optionsBuilder);
}
*/
/*
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//modelBuilder.Entity<Series>().ToTable("Series");
//modelBuilder.Entity<Episode>().ToTable("Episodes").;
//modelBuilder.Ignore<Entit>
// modelBuilder.Entity<Series>(x=>x.Property(p=>p.Instance).HasConversion(v=>v.Managers.)
Console.WriteLine("OnModelCreating");
//modelBuilder.Entity()
base.OnModelCreating(modelBuilder);
}*/
public AsyncReply<IResource> Get(string path)
{
throw new NotImplementedException();
}
public async AsyncReply<bool> Put(IResource resource)
{
return true;
}
public string Link(IResource resource)
{
var p = resource.GetType().GetProperty("Id");
if (p != null)
return this.Instance.Name + "/" + resource.GetType().Name + "/" + p.GetValue(resource);
else
return this.Instance.Name + "/" + resource.GetType().Name;
}
public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime)
{
return true;
//throw new NotImplementedException();
}
public bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime)
{
return true;
//throw new NotImplementedException();
}
public new bool Remove(IResource resource)
{
throw new NotImplementedException();
}
public AsyncReply<bool> AddChild(IResource parent, IResource child)
{
throw new NotImplementedException();
}
public AsyncReply<bool> RemoveChild(IResource parent, IResource child)
{
throw new NotImplementedException();
}
public AsyncReply<bool> AddParent(IResource child, IResource parent)
{
throw new NotImplementedException();
}
public AsyncReply<bool> RemoveParent(IResource child, IResource parent)
{
throw new NotImplementedException();
}
public AsyncBag<T> Children<T>(IResource resource, string name) where T : IResource
{
throw new NotImplementedException();
}
public AsyncBag<T> Parents<T>(IResource resource, string name) where T : IResource
{
throw new NotImplementedException();
}
public AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate)
{
throw new NotImplementedException();
}
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
return new AsyncReply<bool>(true);
}
public void Destroy()
{
//throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Esyur.Stores.EntityCore</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Marten" Version="3.10.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Esyur\Esyur.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,114 @@
/*
Copyright (c) 2020 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 Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.EntityFrameworkCore.Proxies.Internal;
using System.Linq;
namespace Esyur.Stores.EntityCore
{
public class EsyurExtensionOptions : IDbContextOptionsExtension
{
private DbContextOptionsExtensionInfo _info;
EntityStore _store;
public DbContextOptionsExtensionInfo Info => _info;
public EntityStore Store => _store;
public void ApplyServices(IServiceCollection services)
{
services.AddEntityFrameworkProxies();
new EntityFrameworkServicesBuilder(services)
.TryAdd<IConventionSetPlugin, EsyurPlugin>();
}
public void Validate(IDbContextOptions options)
{
var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
if (internalServiceProvider != null)
{
var scope = internalServiceProvider.CreateScope();
var conventionPlugins = scope.ServiceProvider.GetService<IEnumerable<IConventionSetPlugin>>();
if (conventionPlugins?.Any(s => s is EsyurPlugin) == false)
{
throw new InvalidOperationException("");
}
}
//throw new NotImplementedException();
}
public EsyurExtensionOptions(EntityStore store)
{
_info = new ExtensionInfo(this);
_store = store;
}
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
private string _logFragment;
public ExtensionInfo(IDbContextOptionsExtension extension)
: base(extension)
{
}
private new EsyurExtensionOptions Extension
=> (EsyurExtensionOptions)base.Extension;
public override bool IsDatabaseProvider => false;
public override string LogFragment => "Esyur";
// => _logFragment ??= Extension.UseLazyLoadingProxies && Extension.UseChangeDetectionProxies
// ? "using lazy-loading and change detection proxies "
// : Extension.UseLazyLoadingProxies
// ? "using lazy-loading proxies "
//: Extension.UseChangeDetectionProxies
//? "using change detection proxies "
//: "";
public override long GetServiceProviderHashCode() => 2312;//541;//2922;// Extension.UseProxies ? : 0;
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
//debugInfo["Proxies:" + nameof(ProxiesExtensions.UseLazyLoadingProxies)]
// = (Extension._useLazyLoadingProxies ? 541 : 0).ToString(CultureInfo.InvariantCulture);
//debugInfo["Proxies:" + nameof(ProxiesExtensions.UseChangeDetectionProxies)]
// = (Extension._useChangeDetectionProxies ? 541 : 0).ToString(CultureInfo.InvariantCulture);
}
}
}
}

View File

@ -0,0 +1,98 @@
/*
Copyright (c) 2020 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 Esyur.Resource;
using Esyur.Security.Permissions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esyur.Stores.EntityCore
{
public static class EsyurExtensions
{
public static T CreateResource<T>(this DbContext dbContext) where T:IResource
{
return dbContext.GetInfrastructure().CreateResource<T>();
}
public static T CreateResource<T>(this IServiceProvider serviceProvider) where T:IResource
{
var options = serviceProvider.GetService<IDbContextOptions>().FindExtension<EsyurExtensionOptions>();
var manager = options.Store.Instance.Managers.Count > 0 ? options.Store.Instance.Managers.First() : null;
return Warehouse.New<T>("", options.Store, null, manager);
}
public static DbContextOptionsBuilder UseEsyur(this DbContextOptionsBuilder optionsBuilder,
string name = null,
IResource parent = null,
IPermissionsManager manager = null
)
{
var extension = optionsBuilder.Options.FindExtension<EsyurExtensionOptions>();
if (extension == null)
{
var store = Warehouse.New<EntityStore>(name, null, parent, manager);
extension = new EsyurExtensionOptions(store);
}
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
public static DbContextOptionsBuilder<TContext> UseEsyur<TContext>(
this DbContextOptionsBuilder<TContext> optionsBuilder,
string name = null,
IResource parent = null,
IPermissionsManager manager = null)
where TContext : DbContext
{
var extension = optionsBuilder.Options.FindExtension<EsyurExtensionOptions>();
if (extension == null)
{
var store = Warehouse.New<EntityStore>(name, null, parent, manager);
extension = new EsyurExtensionOptions(store);
}
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
}
}

View File

@ -0,0 +1,60 @@
/*
Copyright (c) 2020 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 Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Proxies.Internal;
using System;
using System.Collections.Generic;
using System.Text;
namespace Esyur.Stores.EntityCore
{
public class EsyurPlugin : IConventionSetPlugin
{
private readonly IDbContextOptions _options;
private readonly ProviderConventionSetBuilderDependencies _conventionSetBuilderDependencies;
public EsyurPlugin(
IDbContextOptions options,
ProviderConventionSetBuilderDependencies conventionSetBuilderDependencies)
{
_options = options;
_conventionSetBuilderDependencies = conventionSetBuilderDependencies;
}
public ConventionSet ModifyConventions(ConventionSet conventionSet)
{
var extension = _options.FindExtension<EsyurExtensionOptions>();
conventionSet.ModelFinalizedConventions.Add(new EsyurProxyRewrite(
extension,
_conventionSetBuilderDependencies));
return conventionSet;
}
}
}

View File

@ -0,0 +1,120 @@
/*
Copyright (c) 2020 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.Reflection;
using System.Text;
using Esyur.Proxy;
using Esyur.Resource;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Esyur.Stores.EntityCore
{
public class EsyurProxyRewrite : IModelFinalizedConvention
{
private static readonly MethodInfo _createInstance
= typeof(EsyurProxyRewrite).GetTypeInfo().GetDeclaredMethod(nameof(EsyurProxyRewrite.CreateInstance));
private readonly ConstructorBindingConvention _directBindingConvention;
public static object CreateInstance(
IDbContextOptions dbContextOptions,
IEntityType entityType,
ILazyLoader loader,
object[] constructorArguments)
{
var options = dbContextOptions.FindExtension<EsyurExtensionOptions>();
return CreateInstance2(
options,
entityType,
loader,
constructorArguments);
}
public static object CreateInstance2(
EsyurExtensionOptions options,
IEntityType entityType,
ILazyLoader loader,
object[] constructorArguments)
{
var manager = options.Store.Instance.Managers.Count > 0 ? options.Store.Instance.Managers.First() : null;
return Warehouse.New(entityType.ClrType, "", options.Store, null, manager);
}
public EsyurProxyRewrite(EsyurExtensionOptions ext, ProviderConventionSetBuilderDependencies conventionSetBuilderDependencies)
{
_directBindingConvention = new ConstructorBindingConvention(conventionSetBuilderDependencies);
}
public void ProcessModelFinalized(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
{
var proxyType = ResourceProxy.GetProxy(entityType.ClrType);
var ann = entityType.GetAnnotation(CoreAnnotationNames.ConstructorBinding);
var binding = (InstantiationBinding)entityType[CoreAnnotationNames.ConstructorBinding];
if (binding == null)
_directBindingConvention.ProcessModelFinalized(modelBuilder, context);
binding = (InstantiationBinding)entityType[CoreAnnotationNames.ConstructorBinding];
try
{
entityType.SetAnnotation(
CoreAnnotationNames.ConstructorBinding,
new FactoryMethodBinding(
_createInstance,
new List<ParameterBinding>
{
new DependencyInjectionParameterBinding(typeof(IDbContextOptions), typeof(IDbContextOptions)),
new EntityTypeParameterBinding(),
new DependencyInjectionParameterBinding(typeof(ILazyLoader), typeof(ILazyLoader)),
new ObjectArrayParameterBinding(binding.ParameterBindings)
},
proxyType));
}
catch
{
}
}
}
}
}

View File

@ -1,4 +1,28 @@
using Esyur.Resource;
/*
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 Esyur.Resource;
using System;
using Esyur.Core;
using MongoDB.Driver.Core;
@ -61,7 +85,7 @@ namespace Esyur.Stores.MongoDB
public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime date)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
var objectId = resource.Instance.Variables["objectId"].ToString();
//var bsonObjectId = new BsonObjectId(new ObjectId(objectId));
var record = this.database.GetCollection<BsonDocument>("record_" + objectId);
@ -88,7 +112,7 @@ namespace Esyur.Stores.MongoDB
[ResourceFunction]
public bool Remove(IResource resource)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
var objectId = resource.Instance.Variables["objectId"].ToString();
var filter = Builders<BsonDocument>.Filter.Eq("_id", new BsonObjectId(new ObjectId(objectId)));
this.database.DropCollection("record_" + objectId);
@ -148,8 +172,8 @@ namespace Esyur.Stores.MongoDB
// var bag = new AsyncBag<object>();
resource.Instance.Attributes.Add("children", children.Select(x => x.AsString).ToArray());
resource.Instance.Attributes.Add("parents", parents.Select(x => x.AsString).ToArray());
resource.Instance.Variables.Add("children", children.Select(x => x.AsString).ToArray());
resource.Instance.Variables.Add("parents", parents.Select(x => x.AsString).ToArray());
// Apply store managers
foreach (var m in this.Instance.Managers)
@ -275,7 +299,7 @@ namespace Esyur.Stores.MongoDB
public string Link(IResource resource)
{
return this.Instance.Name + "/id/" + (string)resource.Instance.Attributes["objectId"];
return this.Instance.Name + "/id/" + (string)resource.Instance.Variables["objectId"];
}
public async AsyncReply<bool> Put(IResource resource)
@ -285,13 +309,12 @@ namespace Esyur.Stores.MongoDB
if (resource == this)
return true;
var attrs = resource.Instance.GetAttributes();
foreach (var kv in resources)
if (kv.Value.Target == resource)
{
resource.Instance.Attributes.Add("objectId", kv.Key);
resource.Instance.Variables.Add("objectId", kv.Key);
return true;
}
@ -309,7 +332,7 @@ namespace Esyur.Stores.MongoDB
};
resourcesCollection.InsertOne(document);
resource.Instance.Attributes["objectId"] = document["_id"].ToString();
resource.Instance.Variables["objectId"] = document["_id"].ToString();
// now update the document
@ -321,8 +344,8 @@ namespace Esyur.Stores.MongoDB
var template = resource.Instance.Template;
// setup attributes
resource.Instance.Attributes["children"] = new string[0];
resource.Instance.Attributes["parents"] = new string[] { this.Instance.Link };
resource.Instance.Variables["children"] = new string[0];
resource.Instance.Variables["parents"] = new string[] { this.Instance.Link };
// copy old children (in case we are moving a resource from a store to another.
if (resource.Instance.Store != this)
@ -376,7 +399,7 @@ namespace Esyur.Stores.MongoDB
resources.Add(document["_id"].AsObjectId.ToString(), new WeakReference(resource));
//resource.Instance.Attributes["objectId"] = document["_id"].ToString();
//resource.Instance.Variables["objectId"] = document["_id"].ToString();
ResourceAdded?.Invoke(resource);
@ -390,9 +413,6 @@ namespace Esyur.Stores.MongoDB
}
}
public BsonDocument ComposeStructure(Structure value)
{
var rt = new BsonDocument { { "type", 1 } };
@ -435,9 +455,9 @@ namespace Esyur.Stores.MongoDB
{
rt.Add(new BsonDocument { { "type", 0 }, { "link", r.Instance.Link } });
//if (r.Instance.Attributes.ContainsKey("objectId"))
//if (r.Instance.Variables.ContainsKey("objectId"))
//rt.Add(new BsonObjectId(new ObjectId((string)r.Instance.Attributes["objectId"])));
//rt.Add(new BsonObjectId(new ObjectId((string)r.Instance.Variables["objectId"])));
}
return rt;
@ -461,7 +481,7 @@ namespace Esyur.Stores.MongoDB
return new BsonDocument { { "type", 0 }, { "link", (value as IResource).Instance.Link } };
//return new BsonObjectId(new ObjectId((string)(value as IResource).Instance.Attributes["objectId"]));
//return new BsonObjectId(new ObjectId((string)(value as IResource).Instance.Variables["objectId"]));
case DataType.Structure:
return ComposeStructure((Structure)value);
@ -490,15 +510,21 @@ namespace Esyur.Stores.MongoDB
throw new NotImplementedException();
}
[ResourceAttribute]
public string Connection { get; set; }
[ResourceAttribute]
public string Collection { get; set; }
[ResourceAttribute]
public string Database { get; set; }
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
{
var collectionName = Instance.Attributes["Collection"] as string ?? "resources";
var dbName = Instance.Attributes["Database"] as string ?? "Esyur";
client = new MongoClient(Instance.Attributes["Connection"] as string ?? "mongodb://localhost");
var collectionName = Collection ?? "resources";
var dbName = Database ?? "Esyur";
client = new MongoClient(Connection ?? "mongodb://localhost");
database = client.GetDatabase(dbName);
resourcesCollection = this.database.GetCollection<BsonDocument>(collectionName);
@ -568,7 +594,7 @@ namespace Esyur.Stores.MongoDB
//foreach (IResource c in resource.Instance.Children)
// children.Add(c.Instance.Link);
var plist = resource.Instance.Attributes["parents"] as string[];
var plist = resource.Instance.Variables["parents"] as string[];
foreach (var link in plist)// Parents)
parents.Add(link);
@ -605,7 +631,7 @@ namespace Esyur.Stores.MongoDB
{ "attributes", attrsDoc },
{ "classname", type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name },
{ "name", resource.Instance.Name },
{ "_id", new BsonObjectId(new ObjectId(resource.Instance.Attributes["objectId"].ToString())) },
{ "_id", new BsonObjectId(new ObjectId(resource.Instance.Variables["objectId"].ToString())) },
{"values", values }
};
@ -627,7 +653,7 @@ namespace Esyur.Stores.MongoDB
public AsyncReply<PropertyValue[]> GetPropertyRecordByAge(IResource resource, string propertyName, ulong fromAge, ulong toAge)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
var objectId = resource.Instance.Variables["objectId"].ToString();
var record = this.database.GetCollection<BsonDocument>("record_" + objectId);
var builder = Builders<BsonDocument>.Filter;
@ -663,7 +689,7 @@ namespace Esyur.Stores.MongoDB
public AsyncReply<PropertyValue[]> GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
var objectId = resource.Instance.Variables["objectId"].ToString();
var record = this.database.GetCollection<BsonDocument>("record_" + objectId);
var builder = Builders<BsonDocument>.Filter;
@ -755,7 +781,7 @@ namespace Esyur.Stores.MongoDB
if (resource == this)
return true;
var objectId = resource.Instance.Attributes["objectId"].ToString();
var objectId = resource.Instance.Variables["objectId"].ToString();
var filter = Builders<BsonDocument>.Filter.Eq("_id", new BsonObjectId(new ObjectId(objectId)));
var update = Builders<BsonDocument>.Update
@ -802,7 +828,7 @@ namespace Esyur.Stores.MongoDB
}
else
{
var children = (string[])resource.Instance.Attributes["children"];
var children = (string[])resource.Instance.Variables["children"];
if (children == null)
{
@ -833,7 +859,7 @@ namespace Esyur.Stores.MongoDB
}
else
{
var parents = (string[])resource.Instance.Attributes["parents"];
var parents = (string[])resource.Instance.Variables["parents"];
if (parents == null)
{
@ -862,8 +888,8 @@ namespace Esyur.Stores.MongoDB
public AsyncReply<bool> AddChild(IResource resource, IResource child)
{
var list = (string[])resource.Instance.Attributes["children"];
resource.Instance.Attributes["children"] = list.Concat(new string[] { child.Instance.Link }).ToArray();
var list = (string[])resource.Instance.Variables["children"];
resource.Instance.Variables["children"] = list.Concat(new string[] { child.Instance.Link }).ToArray();
SaveResource(resource);
@ -877,8 +903,8 @@ namespace Esyur.Stores.MongoDB
public AsyncReply<bool> AddParent(IResource resource, IResource parent)
{
var list = (string[])resource.Instance.Attributes["parents"];
resource.Instance.Attributes["parents"] = list.Concat(new string[] { parent.Instance.Link }).ToArray();
var list = (string[])resource.Instance.Variables["parents"];
resource.Instance.Variables["parents"] = list.Concat(new string[] { parent.Instance.Link }).ToArray();
SaveResource(resource);

View File

@ -1,4 +1,28 @@
using Esyur.Core;
/*
Copyright (c) 2020 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 Esyur.Core;
using Esyur.Data;
using Esyur.Proxy;
using Esyur.Resource;

View File

@ -1,23 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Remove="MySqlStore.cs" />
</ItemGroup>
<ItemGroup>
<None Include="MySqlStore.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MySql.Data" Version="8.0.17" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Esyur\Esyur.csproj" />
</ItemGroup>
</Project>

View File

@ -1,718 +0,0 @@
using Esyur.Resource;
using System;
using Esyur.Core;
using Esyur.Data;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Esyur.Resource.Template;
using System.Linq;
using Esyur.Security.Permissions;
using Esyur.Proxy;
using MySql.Data.MySqlClient;
namespace Esyur.Stores.MySql
{
public class MySqlStore : IStore
{
public Instance Instance { get; set; }
public event DestroyedEvent OnDestroy;
string connectionString;
Dictionary<string, IResource> resources = new Dictionary<string, IResource>();
public int Count
{
get { return resources.Count; }
}
public void Destroy()
{
}
public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime date)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
//var bsonObjectId = new BsonObjectId(new ObjectId(objectId));
var record = this.database.GetCollection<BsonDocument>("record_" + objectId);
record.InsertOne(new BsonDocument()
{
{"property", propertyName}, {"age", BsonValue.Create(age) }, {"date", date}, {"value", Compose(value) }
});
//var col = this.database.GetCollection<BsonDocument>(collectionName);
var filter = Builders<BsonDocument>.Filter.Eq("_id", new BsonObjectId(new ObjectId(objectId)));
var update = Builders<BsonDocument>.Update
.Set("values." + propertyName, new BsonDocument { { "age", BsonValue.Create(age) },
{ "modification", date },
{ "value", Compose(value) } });
resourcesCollection.UpdateOne(filter, update);
return true;
}
public bool Remove(IResource resource)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
var filter = Builders<BsonDocument>.Filter.Eq("_id", new BsonObjectId(new ObjectId(objectId)));
this.database.DropCollection("record_" + objectId);
resourcesCollection.DeleteOne(filter);
return true;
}
AsyncReply<IResource> Fetch(string id)
{
MySqlHelper.
var filter = Builders<BsonDocument>.Filter.Eq("_id", new BsonObjectId(new ObjectId(id)));
var list = resourcesCollection.Find(filter).ToList();
if (list.Count == 0)
return new AsyncReply<IResource>(null);
var document = list[0];
var type = Type.GetType(document["classname"].AsString);
if (type == null)
return new AsyncReply<IResource>(null);
IResource resource = (IResource)Activator.CreateInstance(ResourceProxy.GetProxy(type));
resources.Add(document["_id"].AsObjectId.ToString(), resource);
Warehouse.Put(resource, document["name"].AsString, this);
var parents = document["parents"].AsBsonArray;
var children = document["children"].AsBsonArray;
//var managers = document["managers"].AsBsonArray;
var attributes = Parse(document["attributes"]).Then(x => {
resource.Instance.SetAttributes(x as Structure);
});
var bag = new AsyncBag<object>();
foreach (var p in parents)
{
var ap = Warehouse.Get(p.AsString);
bag.Add(ap);
ap.Then((x) =>
{
if (!resource.Instance.Parents.Contains(x))
resource.Instance.Parents.Add(x);
});
}
foreach (var c in children)
{
var ac = Warehouse.Get(c.AsString);
bag.Add(ac);
ac.Then((x) =>
{
if (!resource.Instance.Children.Contains(x))
resource.Instance.Children.Add(x);
});
}
// Apply store managers
foreach (var m in this.Instance.Managers)
resource.Instance.Managers.Add(m);
/*
// load managers
foreach(var m in managers)
{
IPermissionsManager pm = (IPermissionsManager)Activator.CreateInstance(Type.GetType(m["classname"].AsString));
var sr = Parse(m["settings"]);
bag.Add(sr);
sr.Then((x) =>
{
pm.Initialize((Structure)x, resource);
resource.Instance.Managers.Add(pm);
});
}
*/
// Load values
var values = document["values"].AsBsonDocument;
foreach (var v in values)
{
var valueInfo = v.Value as BsonDocument;
var av = Parse(valueInfo["value"]);
bag.Add(av);
av.Then((x) =>
{
resource.Instance.LoadProperty(v.Name, (ulong)valueInfo["age"].AsInt64, valueInfo["modification"].ToUniversalTime(), x);
});
}
bag.Seal();
var rt = new AsyncReply<IResource>();
bag.Then((x) =>
{
rt.Trigger(resource);
});
return rt;
}
IAsyncReply<object> Parse(BsonValue value)
{
if (value.BsonType == BsonType.Document)
{
var doc = value.AsBsonDocument;
if (doc["type"] == 0)
{
return Warehouse.Get(doc["link"].AsString);
} // structure
else if (doc["type"] == 1)
{
var bag = new AsyncBag<object>();
var rt = new AsyncReply<Structure>();
var bs = (BsonDocument)doc["values"].AsBsonDocument;
var s = new Structure();
foreach (var v in bs)
bag.Add(Parse(v.Value));
bag.Seal();
bag.Then((x) =>
{
for (var i = 0; i < x.Length; i++)
s[bs.GetElement(i).Name] = x[i];
rt.Trigger(s);
});
return rt;
}
else
return new AsyncReply<object>(null);
}
else if (value.BsonType == BsonType.Array)
{
var array = value.AsBsonArray;
var bag = new AsyncBag<object>();
foreach (var v in array)
bag.Add(Parse(v));
bag.Seal();
return bag;
}
else if (value.BsonType == BsonType.DateTime)
{
return new AsyncReply<object>(value.ToUniversalTime());
}
else
{
return new AsyncReply<object>(value.RawValue);
}
}
public AsyncReply<IResource> Get(string path)
{
var p = path.Split('/');
if (p.Length == 2)
if (p[0] == "id")
{
// load from Id
if (resources.ContainsKey(p[1]))
return new AsyncReply<IResource>(resources[p[1]]);
else
return Fetch(p[1]);
}
return new AsyncReply<IResource>(null);
}
public string Link(IResource resource)
{
return this.Instance.Name + "/id/" + (string)resource.Instance.Attributes["objectId"];
}
string MakeTable(Type type)
{
var props = type.GetTypeInfo().GetProperties();
foreach(var p in props)
{
var rp = p.GetCustomAttribute<ResourceProperty>();
if (rp == null)
continue;
}
}
public bool Put(IResource resource)
{
var attrs = resource.Instance.GetAttributes();
foreach (var kv in resources)
if (kv.Value == resource)
{
resource.Instance.Attributes.Add("objectId", kv.Key);
return true;
}
var type = ResourceProxy.GetBaseType(resource);
// insert the document
var document = new BsonDocument
{
{ "classname", type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name },
{ "name", resource.Instance.Name },
};
resourcesCollection.InsertOne(document);
resource.Instance.Attributes["objectId"] = document["_id"].ToString();
// now update the document
// * insert first to get the object id, update values, attributes, children and parents after in case the same resource has a property references self
var parents = new BsonArray();
var children = new BsonArray();
var template = resource.Instance.Template;
foreach (IResource c in resource.Instance.Children)
children.Add(c.Instance.Link);
foreach (IResource p in resource.Instance.Parents)
parents.Add(p.Instance.Link);
var attrsDoc = ComposeStructure(attrs);
var values = new BsonDocument();
foreach (var pt in template.Properties)
{
var rt = pt.Info.GetValue(resource, null);
values.Add(pt.Name,
new BsonDocument { { "age", BsonValue.Create(resource.Instance.GetAge(pt.Index)) },
{ "modification", resource.Instance.GetModificationDate(pt.Index) },
{ "value", Compose(rt) } });
}
// var filter = Builders<BsonDocument>.Filter.Eq("_id", document["_id"]);
// var update = Builders<BsonDocument>.Update
// .Set("values", values);
// col.UpdateOne(filter, update);
/*
var document = new BsonDocument
{
{ "parents", parents },
{ "children", children },
{ "attributes", attrsDoc },
{ "classname", resource.GetType().FullName + "," + resource.GetType().GetTypeInfo().Assembly.GetName().Name },
{ "name", resource.Instance.Name },
{ "values", values }
};
*/
var filter = Builders<BsonDocument>.Filter.Eq("_id", document["_id"]);
var update = Builders<BsonDocument>.Update
.Set("values", values).Set("parents", parents).Set("children", children).Set("attributes", attrsDoc);
resourcesCollection.UpdateOne(filter, update);
//resource.Instance.Attributes["objectId"] = document["_id"].ToString();
return true;
}
public BsonDocument ComposeStructure(Structure value)
{
var rt = new BsonDocument { { "type", 1 } };
var values = new BsonDocument();
foreach (var i in value)
values.Add(i.Key, Compose(i.Value));
rt.Add("values", values);
return rt;
}
public BsonArray ComposeVarArray(object[] array)
{
var rt = new BsonArray();
for (var i = 0; i < array.Length; i++)
rt.Add(Compose(array[i]));
return rt;
}
BsonArray ComposeStructureArray(Structure[] structures)
{
var rt = new BsonArray();
if (structures == null || structures?.Length == 0)
return rt;
foreach (var s in structures)
rt.Add(ComposeStructure(s));
return rt;
}
BsonArray ComposeResourceArray(IResource[] array)
{
var rt = new BsonArray();
foreach (var r in array)
{
rt.Add(new BsonDocument { { "type", 0 }, { "link", r.Instance.Link } });
//if (r.Instance.Attributes.ContainsKey("objectId"))
//rt.Add(new BsonObjectId(new ObjectId((string)r.Instance.Attributes["objectId"])));
}
return rt;
}
private BsonValue Compose(object value)
{
var type = Codec.GetDataType(value, null);
switch (type)
{
case DataType.Void:
// nothing to do;
return BsonNull.Value;
case DataType.String:
return new BsonString((string)value);
case DataType.Resource:
case DataType.DistributedResource:
return new BsonDocument { { "type", 0 }, { "link", (value as IResource).Instance.Link } };
//return new BsonObjectId(new ObjectId((string)(value as IResource).Instance.Attributes["objectId"]));
case DataType.Structure:
return ComposeStructure((Structure)value);
case DataType.VarArray:
return ComposeVarArray((object[])value);
case DataType.ResourceArray:
if (value is IResource[])
return ComposeResourceArray((IResource[])value);
else
return ComposeResourceArray((IResource[])DC.CastConvert(value, typeof(IResource[])));
case DataType.StructureArray:
return ComposeStructureArray((Structure[])value);
default:
return BsonValue.Create(value);
}
}
public AsyncReply<IResource> Retrieve(uint iid)
{
throw new NotImplementedException();
}
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
{
var collectionName = Instance.Attributes["Collection"] as string ?? "resources";
var dbName = Instance.Attributes["Database"] as string ?? "Esyur";
client = new MongoClient(Instance.Attributes["Connection"] as string ?? "mongodb://localhost");
database = client.GetDatabase(dbName);
resourcesCollection = this.database.GetCollection<BsonDocument>(collectionName);
// return new AsyncReply<bool>(true);
var filter = new BsonDocument();
var list = resourcesCollection.Find(filter).ToList();
// if (list.Count == 0)
// return new AsyncBag<IResource>(new IResource[0]);
var bag = new AsyncBag<IResource>();
for (var i = 0; i < list.Count; i++)
{
//Console.WriteLine("Loading {0}/{1}", i, list.Count);
bag.Add(Get("id/" + list[i]["_id"].AsObjectId.ToString()));
}
bag.Seal();
var rt = new AsyncReply<bool>();
bag.Then((x) => { rt.Trigger(true); });
return rt;
}
else if (trigger == ResourceTrigger.Terminate)
{
// save all resources
foreach (var resource in resources.Values)
SaveResource(resource);
return new AsyncReply<bool>(true);
}
else
return new AsyncReply<bool>(true);
}
public void SaveResource(IResource resource)
{
var attrs = resource.Instance.GetAttributes();
var parents = new BsonArray();
var children = new BsonArray();
var template = resource.Instance.Template;
foreach (IResource c in resource.Instance.Children)
children.Add(c.Instance.Link);
foreach (IResource p in resource.Instance.Parents)
parents.Add(p.Instance.Link);
var values = new BsonDocument();
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 = pt.Info.GetValue(resource, null);
values.Add(pt.Name,
new BsonDocument { { "age", BsonValue.Create(resource.Instance.GetAge(pt.Index)) },
{ "modification", resource.Instance.GetModificationDate(pt.Index) },
{ "value", Compose(rt) } });
}
var attrsDoc = ComposeStructure(attrs);
var type = ResourceProxy.GetBaseType(resource);
var document = new BsonDocument
{
{ "parents", parents },
{ "children", children },
{"attributes", attrsDoc },
{ "classname", type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name },
{ "name", resource.Instance.Name },
{ "_id", new BsonObjectId(new ObjectId(resource.Instance.Attributes["objectId"].ToString())) },
{"values", values }
};
var filter = Builders<BsonDocument>.Filter.Eq("_id", document["_id"]);
/*
var update = Builders<BsonDocument>.Update
.Set("values", values);
var update = Builders<BsonDocument>.Update.Set("values", values).Set("parents", parents;
col.UpdateOne(filter, update);
*/
resourcesCollection.ReplaceOne(filter, document);
}
public AsyncReply<PropertyValue[]> GetPropertyRecordByAge(IResource resource, string propertyName, ulong fromAge, ulong toAge)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
var record = this.database.GetCollection<BsonDocument>("record_" + objectId);
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Gte("age", fromAge) & builder.Lte("age", toAge) & builder.Eq("property", propertyName);
var reply = new AsyncReply<PropertyValue[]>();
record.FindAsync(filter).ContinueWith((x) =>
{
var values = ((Task<IAsyncCursor<BsonDocument>>)x).Result.ToList();
var bag = new AsyncBag<object>();
foreach (var v in values)
bag.Add(Parse(v["value"]));
bag.Seal();
bag.Then((results) =>
{
var list = new List<PropertyValue>();
for (var i = 0; i < results.Length; i++)
list.Add(new PropertyValue(results[i], (ulong)values[i]["age"].AsInt64, values[i]["date"].ToUniversalTime()));
reply.Trigger(list.ToArray());
});
});
return reply;
}
public AsyncReply<PropertyValue[]> GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate)
{
var objectId = resource.Instance.Attributes["objectId"].ToString();
var record = this.database.GetCollection<BsonDocument>("record_" + objectId);
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Gte("date", fromDate) & builder.Lte("date", toDate) & builder.Eq("property", propertyName);
var reply = new AsyncReply<PropertyValue[]>();
record.FindAsync(filter).ContinueWith((x) =>
{
var values = ((Task<IAsyncCursor<BsonDocument>>)x).Result.ToList();
var bag = new AsyncBag<object>();
foreach (var v in values)
bag.Add(Parse(v["value"]));
bag.Seal();
bag.Then((results) =>
{
var list = new List<PropertyValue>();
for (var i = 0; i < results.Length; i++)
list.Add(new PropertyValue(results[i], (ulong)values[i]["age"].AsInt64, values[i]["date"].ToUniversalTime()));
reply.Trigger(list.ToArray());
});
});
return reply;
}
AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecordByAge(IResource resource, ulong fromAge, ulong toAge)
{
var properties = resource.Instance.Template.Properties.Where(x => x.Storage == StorageMode.Recordable).ToList();
var reply = new AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>>();
AsyncBag<PropertyValue[]> bag = new AsyncBag<PropertyValue[]>();
foreach (var p in properties)
bag.Add(GetPropertyRecordByAge(resource, p.Name, fromAge, toAge));
bag.Seal();
bag.Then(x =>
{
var list = new KeyList<PropertyTemplate, PropertyValue[]>();
for (var i = 0; i < x.Length; i++)
list.Add(properties[i], x[i]);
reply.Trigger(list);
});
return reply;
}
public AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate)
{
var properties = resource.Instance.Template.Properties.Where(x => x.Storage == StorageMode.Recordable).ToList();
var reply = new AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>>();
AsyncBag<PropertyValue[]> bag = new AsyncBag<PropertyValue[]>();
foreach (var p in properties)
bag.Add(GetPropertyRecordByDate(resource, p.Name, fromDate, toDate));
bag.Seal();
bag.Then(x =>
{
var list = new KeyList<PropertyTemplate, PropertyValue[]>();
for (var i = 0; i < x.Length; i++)
list.Add(properties[i], x[i]);
reply.Trigger(list);
});
return reply;
}
public bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime)
{
var sql = $"UPDATE `{resource.Instance.Template.ClassName}` SET `{propertyName}` = @value, `{propertyName}_age` = @age, `{propertyName}_date` = @date WHERE `_id` = @id";
MySqlHelper.ExecuteNonQuery(connectionString, sql,
new MySqlParameter("@value", value),
new MySqlParameter("@age", age),
new MySqlParameter("@date", dateTime),
new MySqlParameter("@id", resource.Instance.Attributes["objectId"]));
return true;
}
}
}

View File

@ -6,7 +6,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esyur", "Esyur\Esyur.csproj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esyur.Stores.MongoDB", "Esyur.Stores.MongoDB\Esyur.Stores.MongoDB.csproj", "{4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esyur.Stores.MySql", "Esyur.Stores.MySql\Esyur.Stores.MySql.csproj", "{7BD6148A-3335-411C-9189-3803B1824264}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esyur.Stores.EntityCore", "Esyur.Stores.EntityCore\Esyur.Stores.EntityCore.csproj", "{53DE5A30-CFA9-4DE7-A840-77CFF519D31B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -22,10 +22,10 @@ Global
{4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}.Release|Any CPU.Build.0 = Release|Any CPU
{7BD6148A-3335-411C-9189-3803B1824264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7BD6148A-3335-411C-9189-3803B1824264}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BD6148A-3335-411C-9189-3803B1824264}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BD6148A-3335-411C-9189-3803B1824264}.Release|Any CPU.Build.0 = Release|Any CPU
{53DE5A30-CFA9-4DE7-A840-77CFF519D31B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53DE5A30-CFA9-4DE7-A840-77CFF519D31B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53DE5A30-CFA9-4DE7-A840-77CFF519D31B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53DE5A30-CFA9-4DE7-A840-77CFF519D31B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -35,6 +35,7 @@ using System.Linq;
using System.Reflection;
using Esyur.Resource.Template;
using System.Runtime.CompilerServices;
using System.Collections;
namespace Esyur.Data
{
@ -1172,6 +1173,8 @@ namespace Esyur.Data
value = (value as IUserType).Get();
// value = (List<>)value.ToArray();
if (value is Func<DistributedConnection, object>)
//if (connection != null)
value = (value as Func<DistributedConnection, object>)(connection);
@ -1189,7 +1192,21 @@ namespace Esyur.Data
var t = value.GetType();
// Convert ICollection<T> to Array<T>
if (!t.IsArray && typeof(ICollection).IsAssignableFrom(t))
{
var col = t.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>));
if (col.Count() == 0)
return (DataType.Void, null);
var elementType = col.First().GetGenericArguments()[0];
value = new ArrayList((ICollection)value).ToArray(elementType);
t = value.GetType();
}
var isArray = t.IsArray;
if (isArray)
t = t.GetElementType();

View File

@ -260,7 +260,7 @@ namespace Esyur.Net.HTTP
if (resource.Execute(sender))
return;
sender.Response.Number = HTTPResponsePacket.ResponseCode.HTTP_SERVERERROR;
sender.Send("Bad Request");
sender.Close();
}

View File

@ -800,6 +800,15 @@ namespace Esyur.Net.IIP
}
}
[ResourceAttribute]
public string Username { get; set; }
[ResourceAttribute]
public string Password { get; set; }
[ResourceAttribute]
public string Domain { get; set; }
/// <summary>
/// Resource interface
/// </summary>
@ -809,8 +818,8 @@ namespace Esyur.Net.IIP
{
if (trigger == ResourceTrigger.Open)
{
if (Instance.Attributes.ContainsKey("username")
&& Instance.Attributes.ContainsKey("password"))
if (Username != null // Instance.Attributes.ContainsKey("username")
&& Password != null)/// Instance.Attributes.ContainsKey("password"))
{
// assign domain from hostname if not provided
@ -818,16 +827,16 @@ namespace Esyur.Net.IIP
var address = host[0];
var port = ushort.Parse(host[1]);
var username = Instance.Attributes["username"].ToString();
var username = Username;// Instance.Attributes["username"].ToString();
var domain = Instance.Attributes.ContainsKey("domain") ? Instance.Attributes["domain"].ToString() : address;
var domain = Domain != null ? Domain : address;// Instance.Attributes.ContainsKey("domain") ? Instance.Attributes["domain"].ToString() : address;
session = new Session(new ClientAuthentication()
, new HostAuthentication());
session.LocalAuthentication.Domain = domain;
session.LocalAuthentication.Username = username;
localPassword = DC.ToBytes(Instance.Attributes["password"].ToString());
localPassword = DC.ToBytes(Password);// Instance.Attributes["password"].ToString());
openReply = new AsyncReply<bool>();
var sock = new TCPSocket();

View File

@ -413,7 +413,7 @@ namespace Esyur.Net.IIP
{
Fetch(resourceId).Then(resource =>
{
resource.Instance.Attributes["name"] = name.GetString(0, (uint)name.Length);
resource.Instance.Variables["name"] = name.GetString(0, (uint)name.Length);
});
}
@ -451,7 +451,8 @@ namespace Esyur.Net.IIP
r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed;
// r.Instance.Children.OnAdd -= Children_OnAdd;
// r.Instance.Children.OnRemoved -= Children_OnRemoved;
r.Instance.Attributes.OnModified -= Attributes_OnModified;
//r.Instance.Attributes.OnModified -= Attributes_OnModified;
// subscribe
r.Instance.ResourceEventOccurred += Instance_EventOccurred;
@ -459,7 +460,8 @@ namespace Esyur.Net.IIP
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
//r.Instance.Children.OnAdd += Children_OnAdd;
//r.Instance.Children.OnRemoved += Children_OnRemoved;
r.Instance.Attributes.OnModified += Attributes_OnModified;
//r.Instance.Attributes.OnModified += Attributes_OnModified;
// add it to attached resources so GC won't remove it from memory
attachedResources.Add(r);
@ -564,7 +566,8 @@ namespace Esyur.Net.IIP
r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed;
//r.Instance.Children.OnAdd -= Children_OnAdd;
//r.Instance.Children.OnRemoved -= Children_OnRemoved;
r.Instance.Attributes.OnModified -= Attributes_OnModified;
//r.Instance.Attributes.OnModified -= Attributes_OnModified;
// subscribe
r.Instance.ResourceEventOccurred += Instance_EventOccurred;
@ -572,7 +575,8 @@ namespace Esyur.Net.IIP
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
//r.Instance.Children.OnAdd += Children_OnAdd;
//r.Instance.Children.OnRemoved += Children_OnRemoved;
r.Instance.Attributes.OnModified += Attributes_OnModified;
//r.Instance.Attributes.OnModified += Attributes_OnModified;
// reply ok
SendReply(IIPPacket.IIPPacketAction.ReattachResource, callback)

View File

@ -406,8 +406,6 @@ namespace Esyur.Net.Sockets
Console.WriteLine("Level 2 {0}", ex2);
}
Global.Log("TCPSocket", LogType.Error, ex.ToString());
}
}

View File

@ -48,8 +48,8 @@ namespace Esyur.Proxy
#if NETSTANDARD
var typeInfo = type.GetTypeInfo();
if (typeInfo.IsSealed)
throw new Exception("Sealed class can't be proxied.");
if (typeInfo.IsSealed || typeInfo.IsAbstract)
throw new Exception("Sealed/Abastract classes can't be proxied.");
var props = from p in typeInfo.GetProperties()
where p.CanWrite && p.GetSetMethod().IsVirtual &&

View File

@ -28,12 +28,13 @@ using System.Linq;
using System.Text;
using Esyur.Data;
using Esyur.Core;
using System.ComponentModel;
namespace Esyur.Resource
{
public delegate bool QueryFilter<T>(T value);
public interface IResource : IDestructible
public interface IResource : IDestructible///, INotifyPropertyChanged
{
AsyncReply<bool> Trigger(ResourceTrigger trigger);

View File

@ -30,6 +30,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esyur.Security.Permissions;
using Esyur.Security.Authority;
namespace Esyur.Resource
{
@ -43,6 +45,13 @@ namespace Esyur.Resource
bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime);
bool Remove(IResource resource);
//bool RemoveAttributes(IResource resource, string[] attributes = null);
//Structure GetAttributes(IResource resource, string[] attributes = null);
//bool SetAttributes(IResource resource, Structure attributes, bool clearAttributes = false);
AsyncReply<bool> AddChild(IResource parent, IResource child);
AsyncReply<bool> RemoveChild(IResource parent, IResource child);

View File

@ -20,22 +20,14 @@ namespace Esyur.Resource
{
string name;
//IQueryable<IResource> children;//
//AutoList<IResource, Instance> children;// = new AutoList<IResource, Instance>();
WeakReference<IResource> resource;
IStore store;
//AutoList<IResource, Instance> parents;// = new AutoList<IResource>();
//bool inherit;
ResourceTemplate template;
AutoList<IPermissionsManager, Instance> managers;// = new AutoList<IPermissionManager, Instance>();
AutoList<IPermissionsManager, Instance> managers;
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 ResourceDestroyedEvent(IResource resource);
public event ResourceModifiedEvent ResourceModified;
@ -44,7 +36,7 @@ namespace Esyur.Resource
bool loading = false;
KeyList<string, object> attributes;
//KeyList<string, object> attributes;
List<ulong> ages = new List<ulong>();
List<DateTime> modificationDates = new List<DateTime>();
@ -53,17 +45,18 @@ namespace Esyur.Resource
uint id;
public KeyList<string, object> Variables { get; } = new KeyList<string, object>();
/// <summary>
/// Instance attributes are custom properties associated with the instance, a place to store information by IStore.
/// </summary>
public KeyList<string, object> Attributes
{
get
{
return attributes;
}
}
//public KeyList<string, object> Attributes
//{
// get
// {
// return attributes;
// }
//}
public override string ToString()
{
@ -72,6 +65,19 @@ namespace Esyur.Resource
public bool RemoveAttributes(string[] attributes = null)
{
return false;
/*
IResource res;
if (!resource.TryGetTarget(out res))
return false;
return store.RemoveAttributes(res, attributes);
*/
/*
if (attributes == null)
this.attributes.Clear();
else
@ -81,10 +87,31 @@ namespace Esyur.Resource
}
return true;
*/
}
public Structure GetAttributes(string[] attributes = null)
{
// @TODO
Structure rt = new Structure();
if (attributes != null)
{
for (var i = 0; i < attributes.Length; i++)
{
var at = template.GetAttributeTemplate(attributes[i]);
if (at != null)
{
}
}
}
return rt;
/*
var st = new Structure();
if (attributes == null)
@ -132,10 +159,31 @@ namespace Esyur.Resource
}
return st;
*/
}
public bool SetAttributes(Structure attributes, bool clearAttributes = false)
{
// @ TODO
IResource res;
if (resource.TryGetTarget(out res))
{
foreach (var kv in attributes)
{
var at = template.GetAttributeTemplate(kv.Key);
if (at != null)
if (at.Info.CanWrite)
at.Info.SetValue(res, kv.Value);
}
}
return true;
/*
try
{
@ -183,6 +231,7 @@ namespace Esyur.Resource
}
return true;
*/
}
/*
@ -758,12 +807,15 @@ namespace Esyur.Resource
IResource res;
if (this.resource.TryGetTarget(out res))
{
//return store.Applicable(res, session, action, member, inquirer);
foreach (IPermissionsManager manager in managers)
{
var r = manager.Applicable(res, session, action, member, inquirer);
if (r != Ruling.DontCare)
return r;
}
}
return Ruling.DontCare;
@ -790,7 +842,7 @@ namespace Esyur.Resource
this.name = name;
this.instanceAge = age;
this.attributes = new KeyList<string, object>(this);
//this.attributes = new KeyList<string, object>(this);
//children = new AutoList<IResource, Instance>(this);
//parents = new AutoList<IResource, Instance>(this);
managers = new AutoList<IPermissionsManager, Instance>(this);

View File

@ -0,0 +1,42 @@
/*
Copyright (c) 2020 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;
using System.Threading.Tasks;
namespace Esyur.Resource
{
[AttributeUsage(AttributeTargets.Property)]
public class ResourceAttribute : System.Attribute
{
public ResourceAttribute()
{
}
}
}

View File

@ -0,0 +1,26 @@
using Esyur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Esyur.Resource.Template
{
public class AttributeTemplate : MemberTemplate
{
public PropertyInfo Info
{
get;
set;
}
public AttributeTemplate(ResourceTemplate template, byte index, string name)
: base(template, MemberType.Attribute, index, name)
{
}
}
}

View File

@ -14,6 +14,7 @@ namespace Esyur.Resource.Template
Function = 0,
Property = 1,
Event = 2,
Attribute = 3
}
public byte Index => index;

View File

@ -19,6 +19,7 @@ namespace Esyur.Resource.Template
List<FunctionTemplate> functions = new List<FunctionTemplate>();
List<EventTemplate> events = new List<EventTemplate>();
List<PropertyTemplate> properties = new List<PropertyTemplate>();
List<AttributeTemplate> attributes = new List<AttributeTemplate>();
int version;
//bool isReady;
@ -88,6 +89,14 @@ namespace Esyur.Resource.Template
return null;
}
public AttributeTemplate GetAttributeTemplate(string attributeName)
{
foreach (var i in attributes)
if (i.Name == attributeName)
return i;
return null;
}
public Guid ClassId
{
get { return classId; }
@ -156,21 +165,31 @@ namespace Esyur.Resource.Template
foreach (var pi in propsInfo)
{
var ps = (ResourceProperty[])pi.GetCustomAttributes(typeof(ResourceProperty), true);
if (ps.Length > 0)
var rp = pi.GetCustomAttribute<ResourceProperty>(true);
if (rp != null)
{
var pt = new PropertyTemplate(this, i++, pi.Name, ps[0].ReadExpansion, ps[0].WriteExpansion, ps[0].Storage);
var pt = new PropertyTemplate(this, i++, pi.Name, rp.ReadExpansion, rp.WriteExpansion, rp.Storage);
pt.Info = pi;
pt.Serilize = ps[0].Serialize;
pt.Serilize = rp.Serialize;
properties.Add(pt);
}
var ra = pi.GetCustomAttribute<ResourceAttribute>(true);
if (ra != null)
{
var at = new AttributeTemplate(this, i++, pi.Name);
at.Info = pi;
attributes.Add(at);
}
}
i = 0;
foreach (var ei in eventsInfo)
{
var es = (ResourceEvent[])ei.GetCustomAttributes(typeof(ResourceEvent), true);
var es = ei.GetCustomAttributes<ResourceEvent>(true).ToArray();
if (es.Length > 0)
{
var et = new EventTemplate(this, i++, ei.Name, es[0].Expansion);
@ -181,7 +200,7 @@ namespace Esyur.Resource.Template
i = 0;
foreach (MethodInfo mi in methodsInfo)
{
var fs = (ResourceFunction[])mi.GetCustomAttributes(typeof(ResourceFunction), true);
var fs = mi.GetCustomAttributes<ResourceFunction>(true).ToArray();
if (fs.Length > 0)
{
var ft = new FunctionTemplate(this, i++, mi.Name, mi.ReturnType == typeof(void), fs[0].Expansion);

View File

@ -501,10 +501,9 @@ namespace Esyur.Resource
}
public static T New<T>(string name, IStore store = null, IResource parent = null, IPermissionsManager manager = null, Structure attributes = null, Structure arguments = null, Structure properties = null)
where T : IResource
public static IResource New(Type type, string name, IStore store = null, IResource parent = null, IPermissionsManager manager = null, Structure attributes = null, Structure arguments = null, Structure properties = null)
{
var type = ResourceProxy.GetProxy<T>();
type = ResourceProxy.GetProxy(type);
/*
@ -544,7 +543,7 @@ namespace Esyur.Resource
{
foreach (var p in properties)
{
var pi = typeof(T).GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
var pi = type.GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
if (pi != null)
pi.SetValue(res, p.Value);
}
@ -553,7 +552,14 @@ namespace Esyur.Resource
if (store != null || parent != null || res is IStore)
Put(res, name, store, parent, null, 0, manager, attributes);
return (T)res;
return res;
}
public static T New<T>(string name, IStore store = null, IResource parent = null, IPermissionsManager manager = null, Structure attributes = null, Structure arguments = null, Structure properties = null)
where T : IResource
{
return (T)New(typeof(T), name, store, parent, manager, attributes, arguments, properties);
}
/// <summary>

View File

@ -45,8 +45,8 @@ namespace Esyur.Stores
{
resources.Add(resource.Instance.Id, resource);// new WeakReference<IResource>(resource));
resource.Instance.Attributes["children"] = new AutoList<IResource, Instance>(resource.Instance);
resource.Instance.Attributes["parents"] = new AutoList<IResource, Instance>(resource.Instance);
resource.Instance.Variables["children"] = new AutoList<IResource, Instance>(resource.Instance);
resource.Instance.Variables["parents"] = new AutoList<IResource, Instance>(resource.Instance);
return true;
}
@ -94,7 +94,7 @@ namespace Esyur.Stores
{
if (parent.Instance.Store == this)
{
(parent.Instance.Attributes["children"] as AutoList<IResource, Instance>).Add(child);
(parent.Instance.Variables["children"] as AutoList<IResource, Instance>).Add(child);
return new AsyncReply<bool>(true);
}
else
@ -111,7 +111,7 @@ namespace Esyur.Stores
if (resource.Instance.Store == this)
{
(resource.Instance.Attributes["parents"] as AutoList<IResource, Instance>).Add(parent);
(resource.Instance.Variables["parents"] as AutoList<IResource, Instance>).Add(parent);
return new AsyncReply<bool>(true);
}
else
@ -125,7 +125,7 @@ namespace Esyur.Stores
public AsyncBag<T> Children<T>(IResource resource, string name) where T : IResource
{
var children = (resource.Instance.Attributes["children"] as AutoList<IResource, Instance>);
var children = (resource.Instance.Variables["children"] as AutoList<IResource, Instance>);
if (name == null)
return new AsyncBag<T>(children.Where(x=>x is T).Select(x=>(T)x).ToArray());
@ -136,7 +136,7 @@ namespace Esyur.Stores
public AsyncBag<T> Parents<T>(IResource resource, string name) where T : IResource
{
var parents = (resource.Instance.Attributes["parents"] as AutoList<IResource, Instance>);
var parents = (resource.Instance.Variables["parents"] as AutoList<IResource, Instance>);
if (name == null)
return new AsyncBag<T>(parents.Where(x => x is T).Select(x => (T)x).ToArray());