diff --git a/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj b/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj
index 874d12a..1567d44 100644
--- a/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj
+++ b/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj
@@ -9,7 +9,7 @@
Esiur Entity Framework Extension
true
Esiur.Stores.EntityCore
- 1.1.0
+ 1.2.1
diff --git a/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs b/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs
index 8f04468..5feb859 100644
--- a/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs
+++ b/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs
@@ -37,6 +37,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Esiur.Data;
namespace Esiur.Stores.EntityCore
{
@@ -57,6 +58,7 @@ namespace Esiur.Stores.EntityCore
public static object CreateInstance(
IDbContextOptions dbContextOptions,
IEntityType entityType,
+ //object id
object[] properties
// ILazyLoader loader,
@@ -64,7 +66,6 @@ namespace Esiur.Stores.EntityCore
//DbContext context,
)
{
- ///var id = constructorArguments.Last();
var id = properties.First();
var options = dbContextOptions.FindExtension();
@@ -75,15 +76,23 @@ namespace Esiur.Stores.EntityCore
if (cache != null)
return cache;
- // check if the object exists
- var obj = Warehouse.New(entityType.ClrType).Wait() as IResource;//, "", options.Store, null, manager);
- //obj._PrimaryId = id;
- options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id);
- Warehouse.Put(id.ToString(), obj, options.Store, null, null, 0, manager).Wait();
+ if (Codec.ImplementsInterface(entityType.ClrType, typeof(IResource)))
+ {
+ // check if the object exists
+ var obj = Warehouse.New(entityType.ClrType).Wait() as IResource;
+ options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id);
+ Warehouse.Put(id.ToString(), obj, options.Store, null, null, 0, manager).Wait();
+ return obj;
- // obj.Instance.IntVal = id;//.Variables.Add("eid", id);
+ }
+ else
+ {
+ // record
+ var obj = Activator.CreateInstance(entityType.ClrType);
+ options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id);
- return obj;
+ return obj;
+ }
}
@@ -98,6 +107,10 @@ namespace Esiur.Stores.EntityCore
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
{
+
+ if (!Codec.ImplementsInterface(entityType.ClrType, typeof(IResource)))
+ continue;
+
var proxyType = ResourceProxy.GetProxy(entityType.ClrType);
// var ann = entityType.GetAnnotation(CoreAnnotationNames.ConstructorBinding);
@@ -114,8 +127,14 @@ namespace Esiur.Stores.EntityCore
try
-
{
+
+ var key = entityType.FindPrimaryKey().Properties.First();
+ if (key == null)
+ continue;
+
+ //var keys = entityType.FindPrimaryKey().Properties.Select(x=>new PropertyParameterBinding(x));
+
entityType.SetAnnotation(
#pragma warning disable EF1001 // Internal EF Core API usage.
CoreAnnotationNames.ConstructorBinding,
@@ -126,12 +145,14 @@ namespace Esiur.Stores.EntityCore
{
new DependencyInjectionParameterBinding(typeof(IDbContextOptions), typeof(IDbContextOptions)),
new EntityTypeParameterBinding(),
+ //new PropertyParameterBinding(key)
// constructor arguments
//new ObjectArrayParameterBinding(binding.ParameterBindings),
//new ContextParameterBinding(typeof(DbContext)),
- new ObjectArrayParameterBinding(new ParameterBinding[]{
- new PropertyParameterBinding(entityType.FindPrimaryKey().Properties.FirstOrDefault())
- })
+ //new ObjectArrayParameterBinding(entityType.FindPrimaryKey().Properties.Select(x=>new PropertyParameterBinding(x)).ToArray())
+ new ObjectArrayParameterBinding(new ParameterBinding[]{
+ new PropertyParameterBinding(key) })
+ //})
// new Microsoft.EntityFrameworkCore.Metadata.ObjectArrayParameterBinding(),
//new ObjectArrayParameterBinding()
@@ -141,6 +162,7 @@ namespace Esiur.Stores.EntityCore
}
catch
{
+
}
}
diff --git a/Esiur/Data/Codec.cs b/Esiur/Data/Codec.cs
index 607227c..a91419d 100644
--- a/Esiur/Data/Codec.cs
+++ b/Esiur/Data/Codec.cs
@@ -81,6 +81,29 @@ namespace Esiur.Data
return types;
}
+ ///
+ /// Compare two records
+ ///
+ /// Initial record to compare with
+ /// Next record to compare with the initial
+ /// DistributedConnection is required in case a structure holds items at the other end
+ public static RecordComparisonResult Compare(IRecord initial, IRecord next)
+ {
+ if (next == null)
+ return RecordComparisonResult.Null;
+
+ if (initial == null)
+ return RecordComparisonResult.Record;
+
+ if (next == initial)
+ return RecordComparisonResult.Same;
+
+ if (next.GetType() == initial.GetType())
+ return RecordComparisonResult.RecordSameType;
+
+ return RecordComparisonResult.Record;
+ }
+
///
/// Compare two structures
///
@@ -231,6 +254,184 @@ namespace Esiur.Data
return reply;
}
+
+ public static AsyncBag ParseRecordArray(byte[] data, uint offset, uint length, DistributedConnection connection)
+ {
+
+ var reply = new AsyncBag();
+ if (length == 0)
+ {
+ reply.Seal();
+ return reply;
+ }
+
+ var end = offset + length;
+
+ var result = (RecordComparisonResult)data[offset++];
+
+ AsyncReply previous = null;
+ Guid? classId = null;
+
+ if (result == RecordComparisonResult.Null)
+ previous = new AsyncReply(null);
+ else if (result == RecordComparisonResult.Record)
+ {
+ uint cs = data.GetUInt32(offset);
+ uint recordLength = cs - 16;
+ offset += 4;
+ classId = data.GetGuid(offset);
+ offset += 16;
+ previous = ParseRecord(data, offset, recordLength, connection, classId);
+ offset += recordLength;
+ }
+
+ reply.Add(previous);
+
+
+ while (offset < end)
+ {
+ result = (RecordComparisonResult)data[offset++];
+
+ if (result == RecordComparisonResult.Null)
+ previous = new AsyncReply(null);
+ else if (result == RecordComparisonResult.Record)
+ {
+ uint cs = data.GetUInt32(offset);
+ uint recordLength = cs - 16;
+ offset += 4;
+ classId = data.GetGuid(offset);
+ offset += 16;
+ previous = ParseRecord(data, offset, recordLength, connection, classId);
+ offset += recordLength;
+ }
+ else if (result == RecordComparisonResult.RecordSameType)
+ {
+ uint cs = data.GetUInt32(offset);
+ offset += 4;
+ previous = ParseRecord(data, offset, cs, connection, classId);
+ offset += cs;
+ }
+ else if (result == RecordComparisonResult.Same)
+ {
+ // do nothing
+ }
+
+ reply.Add(previous);
+ }
+
+ reply.Seal();
+ return reply;
+ }
+
+ public static AsyncReply ParseRecord(byte[] data, uint offset, uint length, DistributedConnection connection, Guid? classId = null)
+ {
+ var reply = new AsyncReply();
+
+ if (classId == null)
+ {
+ classId = data.GetGuid(offset);
+
+ offset += 16;
+ length -= 16;
+ }
+
+ var template = Warehouse.GetTemplateByClassId((Guid)classId);
+
+ if (template != null)
+ {
+ ParseVarArray(data, offset, length, connection).Then(ar =>
+ {
+ if (template.ResourceType != null)
+ {
+ var record = Activator.CreateInstance(template.ResourceType) as IRecord;
+ for (var i = 0; i < template.Properties.Length; i++)
+ template.Properties[i].PropertyInfo.SetValue(record, ar[i]);
+
+ reply.Trigger(record);
+ }
+ else
+ {
+ var record = new Record();
+
+ for (var i = 0; i < template.Properties.Length; i++)
+ record.Add(template.Properties[i].Name, ar[i]);
+
+ reply.Trigger(record);
+ }
+ });
+ }
+ else
+ {
+ connection.GetTemplate((Guid)classId).Then(tmp => {
+ ParseVarArray(data, offset, length, connection).Then(ar =>
+ {
+ var record = new Record();
+
+ for (var i = 0; i < tmp.Properties.Length; i++)
+ record.Add(tmp.Properties[i].Name, ar[i]);
+
+ reply.Trigger(record);
+ });
+ }).Error(x=>reply.TriggerError(x));
+ }
+
+ return reply;
+ }
+
+ public static byte[] ComposeRecord(IRecord record, DistributedConnection connection, bool includeClassId = true, bool prependLength = false)
+ {
+ var rt = new BinaryList();
+
+ var template = Warehouse.GetTemplateByType(record.GetType());
+
+ if (includeClassId)
+ rt.AddGuid(template.ClassId);
+
+ foreach (var pt in template.Properties)
+ {
+ var value = pt.PropertyInfo.GetValue(record, null);
+ rt.AddUInt8Array(Compose(value, connection));
+ }
+
+ if (prependLength)
+ rt.InsertInt32(0, rt.Length);
+
+ return rt.ToArray();
+ }
+
+ public static byte[] ComposeRecordArray(IRecord[] records, DistributedConnection connection, bool prependLength = false)
+ {
+
+ if (records == null || records?.Length == 0)
+ return prependLength ? new byte[] { 0, 0, 0, 0 } : new byte[0];
+
+ var rt = new BinaryList();
+ var comparsion = Compare(null, records[0]);
+
+ rt.AddUInt8((byte)comparsion);
+
+
+ if (comparsion == RecordComparisonResult.Record)
+ rt.AddUInt8Array(ComposeRecord(records[0], connection, true, true));
+
+ for (var i = 1; i < records.Length; i++)
+ {
+ comparsion = Compare(records[i - 1], records[i]);
+ rt.AddUInt8((byte)comparsion);
+
+ if (comparsion == RecordComparisonResult.Record)
+ rt.AddUInt8Array(ComposeRecord(records[i], connection, true, true));
+ else if (comparsion == RecordComparisonResult.RecordSameType)
+ rt.AddUInt8Array(ComposeRecord(records[i], connection, false, true));
+ }
+
+ if (prependLength)
+ rt.InsertInt32(0, rt.Length);
+
+
+ return rt.ToArray();
+ }
+
///
/// Compose a structure into an array of bytes
///
@@ -474,6 +675,9 @@ namespace Esiur.Data
case DataType.Structure:
return ParseStructureArray(data, offset, contentLength, connection);
+
+ case DataType.Record:
+ return ParseRecordArray(data, offset, contentLength, connection);
}
}
else
@@ -536,6 +740,9 @@ namespace Esiur.Data
case DataType.Structure:
return ParseStructure(data, offset, contentLength, connection);
+
+ case DataType.Record:
+ return ParseRecord(data, offset, contentLength, connection);
}
}
@@ -572,22 +779,6 @@ namespace Esiur.Data
return connection.Fetch(iid);// Warehouse.Get(iid);
}
- public enum ResourceComparisonResult
- {
- Null,
- Distributed,
- Local,
- Same
- }
-
- public enum StructureComparisonResult : byte
- {
- Null,
- Structure,
- StructureSameKeys,
- StructureSameTypes,
- Same
- }
///
/// Check if a resource is local to a given connection.
@@ -648,7 +839,6 @@ namespace Esiur.Data
/// DistributedConnection is required to check locality.
/// If True, prepend the length of the output at the beginning.
/// Array of bytes in the network byte order.
-
public static byte[] ComposeResourceArray(IResource[] resources, DistributedConnection connection, bool prependLength = false)
{
if (resources == null || resources?.Length == 0)
@@ -1041,6 +1231,10 @@ namespace Esiur.Data
rt.AddUInt8Array(ComposeVarArray((Array)value, connection, true));
break;
+ case DataType.Record:
+ rt.AddUInt8Array(ComposeRecord((IRecord)value, connection, true, true));
+ break;
+
case DataType.ResourceArray:
if (value is IResource[])
rt.AddUInt8Array(ComposeResourceArray((IResource[])value, connection, true));
@@ -1052,6 +1246,10 @@ namespace Esiur.Data
rt.AddUInt8Array(ComposeStructureArray((Structure[])value, connection, true));
break;
+ case DataType.RecordArray:
+ rt.AddUInt8Array(ComposeRecordArray((IRecord[])value, connection, true));
+ break;
+
default:
rt.Add(type, value);
if (type.IsArray())
@@ -1240,7 +1438,7 @@ namespace Esiur.Data
DataType type;
-
+
if (t == typeof(bool))
type = DataType.Bool;
else if (t == typeof(char))
@@ -1284,6 +1482,8 @@ namespace Esiur.Data
return (IsLocalResource((IResource)value, connection) ? DataType.Resource : DataType.DistributedResource, value);
}
}
+ else if (ImplementsInterface(t, typeof(IRecord)))
+ type = DataType.Record;
else
type = DataType.Void;
diff --git a/Esiur/Data/DataType.cs b/Esiur/Data/DataType.cs
index 88a5f7a..e3dc564 100644
--- a/Esiur/Data/DataType.cs
+++ b/Esiur/Data/DataType.cs
@@ -53,6 +53,7 @@ namespace Esiur.Data
ResourceLink,
String,
Structure,
+ Record,
//Stream,
//Array = 0x80,
VarArray = 0x80,
@@ -75,6 +76,7 @@ namespace Esiur.Data
ResourceLinkArray,
StringArray,
StructureArray,
+ RecordArray,
NotModified = 0x7f,
Unspecified = 0xff,
}
@@ -113,7 +115,5 @@ namespace Esiur.Data
return -1;
}
}
-
-
}
}
diff --git a/Esiur/Data/IRecord.cs b/Esiur/Data/IRecord.cs
new file mode 100644
index 0000000..dfba44a
--- /dev/null
+++ b/Esiur/Data/IRecord.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Data
+{
+ public interface IRecord
+ {
+
+ }
+}
diff --git a/Esiur/Data/Record.cs b/Esiur/Data/Record.cs
new file mode 100644
index 0000000..aec3495
--- /dev/null
+++ b/Esiur/Data/Record.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Data
+{
+ public class Record: KeyList, IRecord
+ {
+
+ }
+}
diff --git a/Esiur/Data/RecordComparisonResult.cs b/Esiur/Data/RecordComparisonResult.cs
new file mode 100644
index 0000000..7318c33
--- /dev/null
+++ b/Esiur/Data/RecordComparisonResult.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Data
+{
+ public enum RecordComparisonResult : byte
+ {
+ Null,
+ Record,
+ RecordSameType,
+ Same
+ }
+}
diff --git a/Esiur/Data/ResourceComparisonResult.cs b/Esiur/Data/ResourceComparisonResult.cs
new file mode 100644
index 0000000..b6b7a12
--- /dev/null
+++ b/Esiur/Data/ResourceComparisonResult.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Data
+{
+ public enum ResourceComparisonResult
+ {
+ Null,
+ Distributed,
+ Local,
+ Same
+ }
+}
diff --git a/Esiur/Data/StructureComparisonResult.cs b/Esiur/Data/StructureComparisonResult.cs
new file mode 100644
index 0000000..c76d973
--- /dev/null
+++ b/Esiur/Data/StructureComparisonResult.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Data
+{
+ public enum StructureComparisonResult : byte
+ {
+ Null,
+ Structure,
+ StructureSameKeys,
+ StructureSameTypes,
+ Same
+ }
+}
diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj
index e7afce7..9ae0cc4 100644
--- a/Esiur/Esiur.csproj
+++ b/Esiur/Esiur.csproj
@@ -7,7 +7,7 @@
https://github.com/Esiur/Esiur-dotnet/blob/master/LICENSE
http://www.esiur.com
true
- 1.8.1
+ 1.8.2.18
https://github.com/esiur/esiur-dotnet
Ahmed Kh. Zamil
1.8.1.0
@@ -41,6 +41,7 @@
+
@@ -53,6 +54,7 @@
+
@@ -79,6 +81,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/Esiur/Net/IIP/DistributedConnection.cs b/Esiur/Net/IIP/DistributedConnection.cs
index 9bcca8f..95e8102 100644
--- a/Esiur/Net/IIP/DistributedConnection.cs
+++ b/Esiur/Net/IIP/DistributedConnection.cs
@@ -1050,7 +1050,7 @@ namespace Esiur.Net.IIP
var host = Instance.Name.Split(':');
var address = host[0];
- var port = ushort.Parse(host[1]);
+ var port = host.Length > 1 ? ushort.Parse(host[1]) : (ushort) 10518;
// assign domain from hostname if not provided
var domain = Domain != null ? Domain : address;
diff --git a/Esiur/Net/IIP/DistributedConnectionProtocol.cs b/Esiur/Net/IIP/DistributedConnectionProtocol.cs
index a02b3fa..d11d3fe 100644
--- a/Esiur/Net/IIP/DistributedConnectionProtocol.cs
+++ b/Esiur/Net/IIP/DistributedConnectionProtocol.cs
@@ -1079,7 +1079,6 @@ namespace Esiur.Net.IIP
void IIPRequestLinkTemplates(uint callback, string resourceLink)
{
- Console.WriteLine("IIPRequestLinkTemplates " + DateTime.UtcNow);
Action queryCallback = (r) =>
{
if (r == null)
@@ -1123,7 +1122,7 @@ namespace Esiur.Net.IIP
void IIPRequestTemplateFromClassName(uint callback, string className)
{
- Warehouse.GetTemplate(className).Then((t) =>
+ Warehouse.GetTemplateByClassName(className).Then((t) =>
{
if (t != null)
SendReply(IIPPacket.IIPPacketAction.TemplateFromClassName, callback)
@@ -1140,7 +1139,7 @@ namespace Esiur.Net.IIP
void IIPRequestTemplateFromClassId(uint callback, Guid classId)
{
- var t = Warehouse.GetTemplate(classId);
+ var t = Warehouse.GetTemplateByClassId(classId);
if (t != null)
SendReply(IIPPacket.IIPPacketAction.TemplateFromClassId, callback)
@@ -2209,7 +2208,7 @@ namespace Esiur.Net.IIP
if (resource == null)
{
- var template = Warehouse.GetTemplate((Guid)rt[0]);
+ var template = Warehouse.GetTemplateByClassId((Guid)rt[0], true);
if (template?.ResourceType != null)
dr = Activator.CreateInstance(template.ResourceType, this, id, (ulong)rt[1], (string)rt[2]) as DistributedResource;
else
diff --git a/Esiur/Net/IIP/DistributedServer.cs b/Esiur/Net/IIP/DistributedServer.cs
index 81006cf..6f4ef15 100644
--- a/Esiur/Net/IIP/DistributedServer.cs
+++ b/Esiur/Net/IIP/DistributedServer.cs
@@ -65,7 +65,7 @@ namespace Esiur.Net.IIP
{
get;
set;
- }
+ } = 10518;
[Attribute]
diff --git a/Esiur/Net/NetworkConnection.cs b/Esiur/Net/NetworkConnection.cs
index b2f9382..4098f54 100644
--- a/Esiur/Net/NetworkConnection.cs
+++ b/Esiur/Net/NetworkConnection.cs
@@ -69,7 +69,7 @@ namespace Esiur.Net
//sock.OnClose -= Socket_OnClose;
//sock.OnConnect -= Socket_OnConnect;
//sock.OnReceive -= Socket_OnReceive;
- sock.Destroy();
+ sock?.Destroy();
//Receiver = null;
Close();
sock = null;
diff --git a/Esiur/Proxy/ResourceGenerator.cs b/Esiur/Proxy/ResourceGenerator.cs
index d297e9a..c75bf0b 100644
--- a/Esiur/Proxy/ResourceGenerator.cs
+++ b/Esiur/Proxy/ResourceGenerator.cs
@@ -21,10 +21,9 @@ namespace Esiur.Proxy
{
- private static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)");
private KeyList cache = new();
- // private List inProgress = new();
+ // private List inProgress = new();
public void Initialize(GeneratorInitializationContext context)
{
@@ -33,148 +32,52 @@ namespace Esiur.Proxy
context.RegisterForSyntaxNotifications(() => new ResourceGeneratorReceiver());
}
- string GetTypeName(TemplateDataType templateDataType, ResourceTemplate[] templates)
- {
-
- if (templateDataType.Type == DataType.Resource)
- return templates.First(x => x.ClassId == templateDataType.TypeGuid).ClassName;
- else if (templateDataType.Type == DataType.ResourceArray)
- return templates.First(x => x.ClassId == templateDataType.TypeGuid).ClassName + "[]";
-
- var name = templateDataType.Type switch
- {
- DataType.Bool => "bool",
- DataType.BoolArray => "bool[]",
- DataType.Char => "char",
- DataType.CharArray => "char[]",
- DataType.DateTime => "DateTime",
- DataType.DateTimeArray => "DateTime[]",
- DataType.Decimal => "decimal",
- DataType.DecimalArray => "decimal[]",
- DataType.Float32 => "float",
- DataType.Float32Array => "float[]",
- DataType.Float64 => "double",
- DataType.Float64Array => "double[]",
- DataType.Int16 => "short",
- DataType.Int16Array => "short[]",
- DataType.Int32 => "int",
- DataType.Int32Array => "int[]",
- DataType.Int64 => "long",
- DataType.Int64Array => "long[]",
- DataType.Int8 => "sbyte",
- DataType.Int8Array => "sbyte[]",
- DataType.String => "string",
- DataType.StringArray => "string[]",
- DataType.Structure => "Structure",
- DataType.StructureArray => "Structure[]",
- DataType.UInt16 => "ushort",
- DataType.UInt16Array => "ushort[]",
- DataType.UInt32 => "uint",
- DataType.UInt32Array => "uint[]",
- DataType.UInt64 => "ulong",
- DataType.UInt64Array => "ulong[]",
- DataType.UInt8 => "byte",
- DataType.UInt8Array => "byte[]",
- DataType.VarArray => "object[]",
- DataType.Void => "object",
- _ => "object"
- };
-
- return name;
- }
-
+
void ReportError(GeneratorExecutionContext context, string title, string msg, string category)
{
context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("MySG001", title, msg, category, DiagnosticSeverity.Error, true), Location.None));
}
- string GenerateClass(ResourceTemplate template, ResourceTemplate[] templates)
- {
- var cls = template.ClassName.Split('.');
-
- var nameSpace = string.Join(".", cls.Take(cls.Length - 1));
- var className = cls.Last();
-
- var rt = new StringBuilder();
-
- rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;");
- rt.AppendLine($"namespace { nameSpace} {{");
- rt.AppendLine($"public class {className} : DistributedResource {{");
-
- rt.AppendLine($"public {className}(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}");
- rt.AppendLine($"public {className}() {{}}");
-
- foreach (var f in template.Functions)
- {
- var rtTypeName = GetTypeName(f.ReturnType, templates);
- rt.Append($"public AsyncReply<{rtTypeName}> {f.Name}(");
- rt.Append(string.Join(",", f.Arguments.Select(x => GetTypeName(x.Type, templates) + " " + x.Name)));
-
- rt.AppendLine(") {");
- rt.AppendLine($"var rt = new AsyncReply<{rtTypeName}>();");
- rt.AppendLine($"_InvokeByArrayArguments({f.Index}, new object[] {{ { string.Join(", ", f.Arguments.Select(x => x.Name)) } }})");
- rt.AppendLine($".Then(x => rt.Trigger(({rtTypeName})x))");
- rt.AppendLine($".Error(x => rt.TriggerError(x))");
- rt.AppendLine($".Chunk(x => rt.TriggerChunk(x));");
- rt.AppendLine("return rt; }");
- }
-
- foreach (var p in template.Properties)
- {
- var ptTypeName = GetTypeName(p.ValueType, templates);
- rt.AppendLine($"public {ptTypeName} {p.Name} {{");
- rt.AppendLine($"get => ({ptTypeName})properties[{p.Index}];");
- rt.AppendLine($"set => _Set({p.Index}, value);");
- rt.AppendLine("}");
- }
-
- if (template.Events.Length > 0)
- {
- rt.AppendLine("protected override void _EmitEventByIndex(byte index, object args) {");
- rt.AppendLine("switch (index) {");
-
- var eventsList = new StringBuilder();
-
- foreach (var e in template.Events)
- {
- var etTypeName = GetTypeName(e.ArgumentType, templates);
- rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;");
- eventsList.AppendLine($"public event ResourceEventHanlder<{etTypeName}> {e.Name};");
- }
-
- rt.AppendLine("}}");
-
- rt.AppendLine(eventsList.ToString());
-
- }
-
- rt.AppendLine("\r\n}\r\n}");
-
- return rt.ToString();
- }
-
+
+
void GenerateModel(GeneratorExecutionContext context, ResourceTemplate[] templates)
{
foreach (var tmp in templates)
{
- var source = GenerateClass(tmp, templates);
- //File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source);
- context.AddSource(tmp.ClassName + "_esiur.cs", source);
+ if (tmp.Type == TemplateType.Resource)
+ {
+ var source = TemplateGenerator.GenerateClass(tmp, templates);
+ // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source);
+ context.AddSource(tmp.ClassName + ".Generated.cs", source);
+ }
+ else if (tmp.Type == TemplateType.Record)
+ {
+ var source = TemplateGenerator.GenerateRecord(tmp, templates);
+ // File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source);
+ context.AddSource(tmp.ClassName + ".Generated.cs", source);
+ }
}
// generate info class
- var gen = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Types {get;} = new Type[]{ " +
- string.Join(",", templates.Select(x => $"typeof({x.ClassName})"))
- + " }; \r\n } \r\n}";
+
+ var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " +
+ string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})"))
+ + " }; \r\n public static Type[] Records { get; } = new Type[] { " +
+ string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})"))
+ + " }; " +
+
+ "\r\n } \r\n}";
//File.WriteAllText($@"C:\gen\Esiur.Generated.cs", gen);
- context.AddSource("Esiur.Generated.cs", gen);
+ context.AddSource("Esiur.Generated.cs", typesFile);
}
+
+
public void Execute(GeneratorExecutionContext context)
{
@@ -188,7 +91,7 @@ namespace Esiur.Proxy
foreach (var path in receiver.Imports)
{
- if (!urlRegex.IsMatch(path))
+ if (!TemplateGenerator.urlRegex.IsMatch(path))
continue;
@@ -206,7 +109,7 @@ namespace Esiur.Proxy
//inProgress.Add(path);
- var url = urlRegex.Split(path);
+ var url = TemplateGenerator.urlRegex.Split(path);
try
@@ -222,7 +125,7 @@ namespace Esiur.Proxy
}
catch (Exception ex)
{
- ReportError(context, ex.Source, ex.Message, "Esiur");
+ ReportError(context, ex.Source, ex.Message, "Esiur");
System.IO.File.AppendAllText("c:\\gen\\error.log", ex.ToString() + "\r\n");
}
@@ -273,12 +176,12 @@ public virtual void Destroy() {{ OnDestroy?.Invoke(this); }}
code += "}}\r\n";
//System.IO.File.WriteAllText("c:\\gen\\" + ci.Name + "_esiur.cs", code);
- context.AddSource(ci.Name + "_esiur.cs", code);
+ context.AddSource(ci.Name + ".Generated.cs", code);
}
catch (Exception ex)
{
- System.IO.File.AppendAllText("c:\\gen\\error.log", ci.Name + " " + ex.ToString() + "\r\n");
+ //System.IO.File.AppendAllText("c:\\gen\\error.log", ci.Name + " " + ex.ToString() + "\r\n");
}
}
}
diff --git a/Esiur/Proxy/ResourceProxy.cs b/Esiur/Proxy/ResourceProxy.cs
index fd3d319..2f2026d 100644
--- a/Esiur/Proxy/ResourceProxy.cs
+++ b/Esiur/Proxy/ResourceProxy.cs
@@ -1,4 +1,5 @@
-using Esiur.Resource;
+using Esiur.Data;
+using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -56,6 +57,12 @@ namespace Esiur.Proxy
return type;
}
+ if (!Codec.ImplementsInterface(type, typeof(IResource)))
+ {
+ cache.Add(type, type);
+ return type;
+ }
+
#if NETSTANDARD
var typeInfo = type.GetTypeInfo();
diff --git a/Esiur/Proxy/TemplateGenerator.cs b/Esiur/Proxy/TemplateGenerator.cs
new file mode 100644
index 0000000..8cd7dc3
--- /dev/null
+++ b/Esiur/Proxy/TemplateGenerator.cs
@@ -0,0 +1,231 @@
+using Esiur.Data;
+using Esiur.Resource.Template;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Text.RegularExpressions;
+using Esiur.Resource;
+using Esiur.Net.IIP;
+using System.Diagnostics;
+
+namespace Esiur.Proxy
+{
+ public static class TemplateGenerator
+ {
+ internal static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)");
+
+ internal static string GenerateRecord(ResourceTemplate template, ResourceTemplate[] templates)
+ {
+ var cls = template.ClassName.Split('.');
+
+ var nameSpace = string.Join(".", cls.Take(cls.Length - 1));
+ var className = cls.Last();
+
+ var rt = new StringBuilder();
+
+ rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;");
+ rt.AppendLine($"namespace { nameSpace} {{");
+ rt.AppendLine($"public class {className} : IRecord {{");
+
+
+ foreach (var p in template.Properties)
+ {
+ var ptTypeName = GetTypeName(p.ValueType, templates);
+ rt.AppendLine($"public {ptTypeName} {p.Name} {{ get; set; }}");
+ rt.AppendLine();
+ }
+
+ rt.AppendLine("\r\n}\r\n}");
+
+ return rt.ToString();
+ }
+
+ static string GetTypeName(TemplateDataType templateDataType, ResourceTemplate[] templates)
+ {
+
+ if (templateDataType.Type == DataType.Resource)
+ return templates.First(x => x.ClassId == templateDataType.TypeGuid).ClassName;
+ else if (templateDataType.Type == DataType.ResourceArray)
+ return templates.First(x => x.ClassId == templateDataType.TypeGuid).ClassName + "[]";
+
+ var name = templateDataType.Type switch
+ {
+ DataType.Bool => "bool",
+ DataType.BoolArray => "bool[]",
+ DataType.Char => "char",
+ DataType.CharArray => "char[]",
+ DataType.DateTime => "DateTime",
+ DataType.DateTimeArray => "DateTime[]",
+ DataType.Decimal => "decimal",
+ DataType.DecimalArray => "decimal[]",
+ DataType.Float32 => "float",
+ DataType.Float32Array => "float[]",
+ DataType.Float64 => "double",
+ DataType.Float64Array => "double[]",
+ DataType.Int16 => "short",
+ DataType.Int16Array => "short[]",
+ DataType.Int32 => "int",
+ DataType.Int32Array => "int[]",
+ DataType.Int64 => "long",
+ DataType.Int64Array => "long[]",
+ DataType.Int8 => "sbyte",
+ DataType.Int8Array => "sbyte[]",
+ DataType.String => "string",
+ DataType.StringArray => "string[]",
+ DataType.Structure => "Structure",
+ DataType.StructureArray => "Structure[]",
+ DataType.UInt16 => "ushort",
+ DataType.UInt16Array => "ushort[]",
+ DataType.UInt32 => "uint",
+ DataType.UInt32Array => "uint[]",
+ DataType.UInt64 => "ulong",
+ DataType.UInt64Array => "ulong[]",
+ DataType.UInt8 => "byte",
+ DataType.UInt8Array => "byte[]",
+ DataType.VarArray => "object[]",
+ DataType.Void => "object",
+ _ => "object"
+ };
+
+ return name;
+ }
+
+ public static string GetTemplate(string url, string dir = null, string username= null, string password = null)
+ {
+ try
+ {
+
+ if (!urlRegex.IsMatch(url))
+ throw new Exception("Invalid IIP URL");
+
+ var path = urlRegex.Split(url);
+ var con = Warehouse.Get(path[1] + "://" + path[2],
+ !string.IsNullOrEmpty( username) && !string.IsNullOrEmpty( password) ? new { Username = username, Password = password } : null
+ ).Wait(20000);
+
+ if (con == null)
+ throw new Exception("Can't connect to server");
+
+ if (string.IsNullOrEmpty(dir))
+ dir = path[2].Replace(":", "_");
+
+ var templates = con.GetLinkTemplates(path[3]).Wait(60000);
+
+ var tempDir = new DirectoryInfo(Path.GetTempPath() + Path.DirectorySeparatorChar
+ + Misc.Global.GenerateCode(20) + Path.DirectorySeparatorChar + dir);
+
+ if (!tempDir.Exists)
+ tempDir.Create();
+ else
+ {
+ foreach (FileInfo file in tempDir.GetFiles())
+ file.Delete();
+ }
+
+ // make sources
+ foreach (var tmp in templates)
+ {
+ if (tmp.Type == TemplateType.Resource)
+ {
+ var source = GenerateClass(tmp, templates);
+ File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source);
+ }
+ else if (tmp.Type == TemplateType.Record)
+ {
+ var source = GenerateRecord(tmp, templates);
+ File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + tmp.ClassName + ".Generated.cs", source);
+ }
+ }
+
+ // generate info class
+
+ var typesFile = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Resources {get;} = new Type[] { " +
+ string.Join(",", templates.Where(x => x.Type == TemplateType.Resource).Select(x => $"typeof({x.ClassName})"))
+ + " }; \r\n public static Type[] Records { get; } = new Type[] { " +
+ string.Join(",", templates.Where(x => x.Type == TemplateType.Record).Select(x => $"typeof({x.ClassName})"))
+ + " }; " +
+
+ "\r\n } \r\n}";
+
+
+ File.WriteAllText(tempDir.FullName + Path.DirectorySeparatorChar + "Esiur.Generated.cs", typesFile);
+
+ return tempDir.FullName;
+
+ }
+ catch(Exception ex)
+ {
+ //File.WriteAllText("C:\\gen\\gettemplate.err", ex.ToString());
+ throw ex;
+ }
+ }
+
+ internal static string GenerateClass(ResourceTemplate template, ResourceTemplate[] templates)
+ {
+ var cls = template.ClassName.Split('.');
+
+ var nameSpace = string.Join(".", cls.Take(cls.Length - 1));
+ var className = cls.Last();
+
+ var rt = new StringBuilder();
+
+ rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;");
+ rt.AppendLine($"namespace { nameSpace} {{");
+ rt.AppendLine($"public class {className} : DistributedResource {{");
+
+ rt.AppendLine($"public {className}(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}");
+ rt.AppendLine($"public {className}() {{}}");
+
+ foreach (var f in template.Functions)
+ {
+ var rtTypeName = GetTypeName(f.ReturnType, templates);
+ rt.Append($"public AsyncReply<{rtTypeName}> {f.Name}(");
+ rt.Append(string.Join(",", f.Arguments.Select(x => GetTypeName(x.Type, templates) + " " + x.Name)));
+
+ rt.AppendLine(") {");
+ rt.AppendLine($"var rt = new AsyncReply<{rtTypeName}>();");
+ rt.AppendLine($"_InvokeByArrayArguments({f.Index}, new object[] {{ { string.Join(", ", f.Arguments.Select(x => x.Name)) } }})");
+ rt.AppendLine($".Then(x => rt.Trigger(({rtTypeName})x))");
+ rt.AppendLine($".Error(x => rt.TriggerError(x))");
+ rt.AppendLine($".Chunk(x => rt.TriggerChunk(x));");
+ rt.AppendLine("return rt; }");
+ }
+
+ foreach (var p in template.Properties)
+ {
+ var ptTypeName = GetTypeName(p.ValueType, templates);
+ rt.AppendLine($"public {ptTypeName} {p.Name} {{");
+ rt.AppendLine($"get => ({ptTypeName})properties[{p.Index}];");
+ rt.AppendLine($"set => _Set({p.Index}, value);");
+ rt.AppendLine("}");
+ }
+
+ if (template.Events.Length > 0)
+ {
+ rt.AppendLine("protected override void _EmitEventByIndex(byte index, object args) {");
+ rt.AppendLine("switch (index) {");
+
+ var eventsList = new StringBuilder();
+
+ foreach (var e in template.Events)
+ {
+ var etTypeName = GetTypeName(e.ArgumentType, templates);
+ rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;");
+ eventsList.AppendLine($"public event ResourceEventHanlder<{etTypeName}> {e.Name};");
+ }
+
+ rt.AppendLine("}}");
+
+ rt.AppendLine(eventsList.ToString());
+
+ }
+
+ rt.AppendLine("\r\n}\r\n}");
+
+ return rt.ToString();
+ }
+
+ }
+}
diff --git a/Esiur/Resource/Instance.cs b/Esiur/Resource/Instance.cs
index 231e3d1..36b172f 100644
--- a/Esiur/Resource/Instance.cs
+++ b/Esiur/Resource/Instance.cs
@@ -875,7 +875,7 @@ namespace Esiur.Resource
if (customTemplate != null)
this.template = customTemplate;
else
- this.template = Warehouse.GetTemplate(resource.GetType());
+ this.template = Warehouse.GetTemplateByType(resource.GetType());
// set ages
for (byte i = 0; i < template.Properties.Length; i++)
diff --git a/Esiur/Resource/Template/RecordTemplate.cs b/Esiur/Resource/Template/RecordTemplate.cs
new file mode 100644
index 0000000..6bb33e6
--- /dev/null
+++ b/Esiur/Resource/Template/RecordTemplate.cs
@@ -0,0 +1,277 @@
+using Esiur.Data;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace Esiur.Resource.Template
+{
+ public class RecordTemplate : ResourceTemplate
+ {
+ //Guid classId;
+ //public Guid ClassId => classId;
+
+ //string className;
+ //public string ClassName => className;
+
+ public RecordTemplate()
+ {
+
+ }
+
+ public new static RecordTemplate Parse(byte[] data, uint offset, uint contentLength)
+ {
+
+ uint ends = offset + contentLength;
+
+ uint oOffset = offset;
+
+ // start parsing...
+
+ var od = new RecordTemplate();
+ od.content = data.Clip(offset, contentLength);
+
+ od.classId = data.GetGuid(offset);
+ offset += 16;
+ od.className = data.GetString(offset + 1, data[offset]);
+ offset += (uint)data[offset] + 1;
+
+ od.version = data.GetInt32(offset);
+ offset += 4;
+
+ ushort methodsCount = data.GetUInt16(offset);
+ offset += 2;
+
+
+
+ for (int i = 0; i < methodsCount; i++)
+ {
+ var type = data[offset] >> 5;
+
+ if (type == 0) // function
+ {
+ string expansion = null;
+ var hasExpansion = ((data[offset++] & 0x10) == 0x10);
+
+ var name = data.GetString(offset + 1, data[offset]);
+ offset += (uint)data[offset] + 1;
+
+ // return type
+ var (rts, returnType) = TemplateDataType.Parse(data, offset);
+ offset += rts;
+
+ // arguments count
+ var argsCount = data[offset++];
+ List arguments = new();
+
+ for (var a = 0; a < argsCount; a++)
+ {
+ var (cs, argType) = ArgumentTemplate.Parse(data, offset);
+ arguments.Add(argType);
+ offset += cs;
+ }
+
+ // arguments
+ if (hasExpansion) // expansion ?
+ {
+ var cs = data.GetUInt32(offset);
+ offset += 4;
+ expansion = data.GetString(offset, cs);
+ offset += cs;
+ }
+
+ var ft = new FunctionTemplate(od, functionIndex++, name, arguments.ToArray(), returnType, expansion);
+
+ od.functions.Add(ft);
+ }
+ else if (type == 1) // property
+ {
+
+ 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;
+
+ var (dts, valueType) = TemplateDataType.Parse(data, offset);
+
+ offset += dts;
+
+ if (hasReadExpansion) // expansion ?
+ {
+ var cs = data.GetUInt32(offset);
+ offset += 4;
+ readExpansion = data.GetString(offset, cs);
+ offset += cs;
+ }
+
+ if (hasWriteExpansion) // expansion ?
+ {
+ var cs = data.GetUInt32(offset);
+ offset += 4;
+ writeExpansion = data.GetString(offset, cs);
+ offset += cs;
+ }
+
+ var pt = new PropertyTemplate(od, propertyIndex++, name, valueType, readExpansion, writeExpansion, recordable);
+
+ od.properties.Add(pt);
+ }
+ else if (type == 2) // Event
+ {
+
+ string expansion = null;
+ var hasExpansion = ((data[offset] & 0x10) == 0x10);
+ var listenable = ((data[offset++] & 0x8) == 0x8);
+
+ var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]);
+ offset += (uint)data[offset] + 1;
+
+ var (dts, argType) = TemplateDataType.Parse(data, offset);
+
+ offset += dts;
+
+ if (hasExpansion) // expansion ?
+ {
+ var cs = data.GetUInt32(offset);
+ offset += 4;
+ expansion = data.GetString(offset, cs);
+ offset += cs;
+ }
+
+ var et = new EventTemplate(od, eventIndex++, name, argType, expansion, listenable);
+
+ od.events.Add(et);
+
+ }
+ }
+
+ // append signals
+ for (int i = 0; i < od.events.Count; i++)
+ od.members.Add(od.events[i]);
+ // append slots
+ for (int i = 0; i < od.functions.Count; i++)
+ od.members.Add(od.functions[i]);
+ // append properties
+ for (int i = 0; i < od.properties.Count; i++)
+ od.members.Add(od.properties[i]);
+
+
+ //od.isReady = true;
+ /*
+ var oo = owner.Socket.Engine.GetObjectDescription(od.GUID);
+ if (oo != null)
+ {
+ Console.WriteLine("Already there ! description");
+ return oo;
+ }
+ else
+ {
+ owner.Socket.Engine.AddObjectDescription(od);
+ return od;
+ }
+ */
+
+ return od;
+ }
+
+ public RecordTemplate(Type type)
+ {
+ if (!Codec.ImplementsInterface(type, typeof(IRecord)))
+ throw new Exception("Type is not a record.");
+
+ className = type.FullName;
+ classId = ResourceTemplate.GetTypeGuid(className);
+
+#if NETSTANDARD
+ PropertyInfo[] propsInfo = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance);
+#else
+ PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
+#endif
+
+ bool classIsPublic = type.GetCustomAttribute() != null;
+
+ byte i = 0;
+
+ if (classIsPublic)
+ {
+ foreach (var pi in propsInfo)
+ {
+ var privateAttr = pi.GetCustomAttribute(true);
+
+ if (privateAttr == null)
+ continue;
+
+ var annotationAttr = pi.GetCustomAttribute(true);
+ var storageAttr = pi.GetCustomAttribute(true);
+
+ var pt = new PropertyTemplate(this, i++, pi.Name, TemplateDataType.FromType(pi.PropertyType));
+
+ if (storageAttr != null)
+ pt.Recordable = storageAttr.Mode == StorageMode.Recordable;
+
+ if (annotationAttr != null)
+ pt.ReadExpansion = annotationAttr.Annotation;
+ else
+ pt.ReadExpansion = pi.PropertyType.Name;
+
+ pt.PropertyInfo = pi;
+ //pt.Serilize = publicAttr.Serialize;
+ properties.Add(pt);
+ members.Add(pt);
+ }
+ }
+ else
+ {
+ foreach (var pi in propsInfo)
+ {
+ var publicAttr = pi.GetCustomAttribute(true);
+
+ if (publicAttr == null)
+ continue;
+
+
+ var annotationAttr = pi.GetCustomAttribute(true);
+ var storageAttr = pi.GetCustomAttribute(true);
+ var valueType = TemplateDataType.FromType(pi.PropertyType);
+
+ var pt = new PropertyTemplate(this, i++, pi.Name, valueType);//, rp.ReadExpansion, rp.WriteExpansion, rp.Storage);
+ if (storageAttr != null)
+ pt.Recordable = storageAttr.Mode == StorageMode.Recordable;
+
+ if (annotationAttr != null)
+ pt.ReadExpansion = annotationAttr.Annotation;
+ else
+ pt.ReadExpansion = pi.PropertyType.Name;
+
+ pt.PropertyInfo = pi;
+ //pt.Serilize = publicAttr.Serialize;
+ properties.Add(pt);
+ members.Add(pt);
+ }
+ }
+
+
+
+ // bake it binarily
+ var b = new BinaryList();
+ b.AddGuid(classId)
+ .AddUInt8((byte)className.Length)
+ .AddString(className)
+ .AddInt32(version)
+ .AddUInt16((ushort)members.Count);
+
+
+ foreach (var pt in properties)
+ b.AddUInt8Array(pt.Compose());
+
+ content = b.ToArray();
+
+
+ }
+ }
+}
diff --git a/Esiur/Resource/Template/ResourceTemplate.cs b/Esiur/Resource/Template/ResourceTemplate.cs
index 144dcec..124dc1c 100644
--- a/Esiur/Resource/Template/ResourceTemplate.cs
+++ b/Esiur/Resource/Template/ResourceTemplate.cs
@@ -12,28 +12,42 @@ using Esiur.Net.IIP;
namespace Esiur.Resource.Template
{
+ //public enum TemplateType
+ //{
+ // Resource,
+ // Record
+ //}
+
public class ResourceTemplate
{
- Guid classId;
- string className;
- List members = new List();
- List functions = new List();
- List events = new List();
- List properties = new List();
- List attributes = new List();
- int version;
+ protected Guid classId;
+ protected string className;
+ protected List members = new List();
+ protected List functions = new List();
+ protected List events = new List();
+ protected List properties = new List();
+ protected List attributes = new List();
+ protected int version;
+ protected TemplateType templateType;
+
+ // protected TemplateType
//bool isReady;
- byte[] content;
+ protected byte[] content;
public byte[] Content
{
get { return content; }
}
+ public TemplateType Type => templateType;
+
+
public Type ResourceType { get; set; }
+
+
public MemberTemplate GetMemberTemplate(MemberInfo member)
{
if (member is MethodInfo)
@@ -174,7 +188,7 @@ namespace Esiur.Resource.Template
// functions
foreach (var f in tmp.functions)
{
- var frtt = Warehouse.GetTemplate(GetElementType(f.MethodInfo.ReturnType));
+ var frtt = Warehouse.GetTemplateByType(GetElementType(f.MethodInfo.ReturnType));
if (frtt != null)
{
if (!bag.Contains(frtt))
@@ -188,7 +202,7 @@ namespace Esiur.Resource.Template
for(var i = 0; i < args.Length - 1; i++)
{
- var fpt = Warehouse.GetTemplate(GetElementType(args[i].ParameterType));
+ var fpt = Warehouse.GetTemplateByType(GetElementType(args[i].ParameterType));
if (fpt != null)
{
if (!bag.Contains(fpt))
@@ -205,7 +219,7 @@ namespace Esiur.Resource.Template
var last = args.Last();
if (last.ParameterType != typeof(DistributedConnection))
{
- var fpt = Warehouse.GetTemplate(GetElementType(last.ParameterType));
+ var fpt = Warehouse.GetTemplateByType(GetElementType(last.ParameterType));
if (fpt != null)
{
if (!bag.Contains(fpt))
@@ -222,7 +236,7 @@ namespace Esiur.Resource.Template
// properties
foreach (var p in tmp.properties)
{
- var pt = Warehouse.GetTemplate(GetElementType(p.PropertyInfo.PropertyType));
+ var pt = Warehouse.GetTemplateByType(GetElementType(p.PropertyInfo.PropertyType));
if (pt != null)
{
if (!bag.Contains(pt))
@@ -236,7 +250,7 @@ namespace Esiur.Resource.Template
// events
foreach (var e in tmp.events)
{
- var et = Warehouse.GetTemplate(GetElementType(e.EventInfo.EventHandlerType.GenericTypeArguments[0]));
+ var et = Warehouse.GetTemplateByType(GetElementType(e.EventInfo.EventHandlerType.GenericTypeArguments[0]));
if (et != null)
{
@@ -255,8 +269,18 @@ namespace Esiur.Resource.Template
public ResourceTemplate(Type type)
{
- if (!Codec.ImplementsInterface(type, typeof(IResource)))
- throw new Exception("Type is not a resource.");
+ if (Codec.ImplementsInterface(type, typeof(IRecord)))
+ templateType = TemplateType.Record;
+ else if (Codec.ImplementsInterface(type, typeof(IResource)))
+ templateType = TemplateType.Resource;
+ else
+ throw new Exception("Type is neither a resource nor a record.");
+
+ //if (isRecord && isResource)
+ // throw new Exception("Type can't have both IResource and IRecord interfaces");
+
+ //if (!(isResource || isRecord))
+ // throw new Exception("Type is neither a resource nor a record.");
type = ResourceProxy.GetBaseType(type);
@@ -322,65 +346,68 @@ namespace Esiur.Resource.Template
}
}
- i = 0;
-
- foreach (var ei in eventsInfo)
+ if (templateType == TemplateType.Resource)
{
- var privateAttr = ei.GetCustomAttribute(true);
- if (privateAttr == null)
+ i = 0;
+
+ foreach (var ei in eventsInfo)
{
- var annotationAttr = ei.GetCustomAttribute(true);
- var listenableAttr = ei.GetCustomAttribute(true);
-
- var argType = ei.EventHandlerType.GenericTypeArguments[0];
- var et = new EventTemplate(this, i++, ei.Name, TemplateDataType.FromType(argType));
- et.EventInfo = ei;
-
- if (annotationAttr != null)
- et.Expansion = annotationAttr.Annotation;
-
- if (listenableAttr != null)
- et.Listenable = true;
-
- events.Add(et);
- }
- }
-
- i = 0;
- foreach (MethodInfo mi in methodsInfo)
- {
- var privateAttr = mi.GetCustomAttribute(true);
- if (privateAttr == null)
- {
- var annotationAttr = mi.GetCustomAttribute(true);
-
- var returnType = TemplateDataType.FromType(mi.ReturnType);
-
- var args = mi.GetParameters();
-
- if (args.Length > 0)
+ var privateAttr = ei.GetCustomAttribute(true);
+ if (privateAttr == null)
{
- if (args.Last().ParameterType == typeof(DistributedConnection))
- args = args.Take(args.Count() - 1).ToArray();
+ var annotationAttr = ei.GetCustomAttribute(true);
+ var listenableAttr = ei.GetCustomAttribute(true);
+
+ var argType = ei.EventHandlerType.GenericTypeArguments[0];
+ var et = new EventTemplate(this, i++, ei.Name, TemplateDataType.FromType(argType));
+ et.EventInfo = ei;
+
+ if (annotationAttr != null)
+ et.Expansion = annotationAttr.Annotation;
+
+ if (listenableAttr != null)
+ et.Listenable = true;
+
+ events.Add(et);
}
+ }
- var arguments = args.Select(x => new ArgumentTemplate()
- {
- Name = x.Name,
- Type = TemplateDataType.FromType(x.ParameterType),
- ParameterInfo = x
- })
- .ToArray();
+ i = 0;
+ foreach (MethodInfo mi in methodsInfo)
+ {
+ var privateAttr = mi.GetCustomAttribute(true);
+ if (privateAttr == null)
+ {
+ var annotationAttr = mi.GetCustomAttribute(true);
- var ft = new FunctionTemplate(this, i++, mi.Name, arguments, returnType);// mi.ReturnType == typeof(void));
+ var returnType = TemplateDataType.FromType(mi.ReturnType);
- if (annotationAttr != null)
- ft.Expansion = annotationAttr.Annotation;
- else
- ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
+ var args = mi.GetParameters();
- ft.MethodInfo = mi;
- functions.Add(ft);
+ if (args.Length > 0)
+ {
+ if (args.Last().ParameterType == typeof(DistributedConnection))
+ args = args.Take(args.Count() - 1).ToArray();
+ }
+
+ var arguments = args.Select(x => new ArgumentTemplate()
+ {
+ Name = x.Name,
+ Type = TemplateDataType.FromType(x.ParameterType),
+ ParameterInfo = x
+ })
+ .ToArray();
+
+ var ft = new FunctionTemplate(this, i++, mi.Name, arguments, returnType);// mi.ReturnType == typeof(void));
+
+ if (annotationAttr != null)
+ ft.Expansion = annotationAttr.Annotation;
+ else
+ ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
+
+ ft.MethodInfo = mi;
+ functions.Add(ft);
+ }
}
}
}
@@ -422,65 +449,68 @@ namespace Esiur.Resource.Template
}
}
- i = 0;
-
- foreach (var ei in eventsInfo)
+ if (templateType == TemplateType.Resource)
{
- var publicAttr = ei.GetCustomAttribute(true);
- if (publicAttr != null)
+ i = 0;
+
+ foreach (var ei in eventsInfo)
{
- var annotationAttr = ei.GetCustomAttribute(true);
- var listenableAttr = ei.GetCustomAttribute(true);
-
- var argType = ei.EventHandlerType.GenericTypeArguments[0];
-
- var et = new EventTemplate(this, i++, ei.Name, TemplateDataType.FromType(argType));
- et.EventInfo = ei;
-
- if (annotationAttr != null)
- et.Expansion = annotationAttr.Annotation;
-
- if (listenableAttr != null)
- et.Listenable = true;
-
- events.Add(et);
- }
- }
-
- i = 0;
- foreach (MethodInfo mi in methodsInfo)
- {
- var publicAttr = mi.GetCustomAttribute(true);
- if (publicAttr != null)
- {
- var annotationAttr = mi.GetCustomAttribute(true);
- var returnType = TemplateDataType.FromType(mi.ReturnType);
-
- var args = mi.GetParameters();
-
- if (args.Length > 0)
+ var publicAttr = ei.GetCustomAttribute(true);
+ if (publicAttr != null)
{
- if (args.Last().ParameterType == typeof(DistributedConnection))
- args = args.Take(args.Count() - 1).ToArray();
+ var annotationAttr = ei.GetCustomAttribute(true);
+ var listenableAttr = ei.GetCustomAttribute(true);
+
+ var argType = ei.EventHandlerType.GenericTypeArguments[0];
+
+ var et = new EventTemplate(this, i++, ei.Name, TemplateDataType.FromType(argType));
+ et.EventInfo = ei;
+
+ if (annotationAttr != null)
+ et.Expansion = annotationAttr.Annotation;
+
+ if (listenableAttr != null)
+ et.Listenable = true;
+
+ events.Add(et);
}
+ }
- var arguments = args.Select(x => new ArgumentTemplate()
- {
- Name = x.Name,
- Type = TemplateDataType.FromType(x.ParameterType),
- ParameterInfo = x
- })
- .ToArray();
+ i = 0;
+ foreach (MethodInfo mi in methodsInfo)
+ {
+ var publicAttr = mi.GetCustomAttribute(true);
+ if (publicAttr != null)
+ {
+ var annotationAttr = mi.GetCustomAttribute(true);
+ var returnType = TemplateDataType.FromType(mi.ReturnType);
- var ft = new FunctionTemplate(this, i++, mi.Name, arguments, returnType);// mi.ReturnType == typeof(void));
+ var args = mi.GetParameters();
- if (annotationAttr != null)
- ft.Expansion = annotationAttr.Annotation;
- else
- ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x=>x.ParameterType != typeof(DistributedConnection)).Select(x=> "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
+ if (args.Length > 0)
+ {
+ if (args.Last().ParameterType == typeof(DistributedConnection))
+ args = args.Take(args.Count() - 1).ToArray();
+ }
- ft.MethodInfo = mi;
- functions.Add(ft);
+ var arguments = args.Select(x => new ArgumentTemplate()
+ {
+ Name = x.Name,
+ Type = TemplateDataType.FromType(x.ParameterType),
+ ParameterInfo = x
+ })
+ .ToArray();
+
+ var ft = new FunctionTemplate(this, i++, mi.Name, arguments, returnType);// mi.ReturnType == typeof(void));
+
+ if (annotationAttr != null)
+ ft.Expansion = annotationAttr.Annotation;
+ else
+ ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
+
+ ft.MethodInfo = mi;
+ functions.Add(ft);
+ }
}
}
}
@@ -497,7 +527,8 @@ namespace Esiur.Resource.Template
// bake it binarily
var b = new BinaryList();
- b.AddGuid(classId)
+ b.AddUInt8((byte)templateType)
+ .AddGuid(classId)
.AddUInt8((byte)className.Length)
.AddString(className)
.AddInt32(version)
@@ -533,6 +564,8 @@ namespace Esiur.Resource.Template
var od = new ResourceTemplate();
od.content = data.Clip(offset, contentLength);
+ od.templateType = (TemplateType)data[offset++];
+
od.classId = data.GetGuid(offset);
offset += 16;
od.className = data.GetString(offset + 1, data[offset]);
diff --git a/Esiur/Resource/Template/TemplateDataType.cs b/Esiur/Resource/Template/TemplateDataType.cs
index 068bf55..7ceb11a 100644
--- a/Esiur/Resource/Template/TemplateDataType.cs
+++ b/Esiur/Resource/Template/TemplateDataType.cs
@@ -10,7 +10,7 @@ namespace Esiur.Resource.Template
{
public DataType Type { get; set; }
//public string TypeName { get; set; }
- public ResourceTemplate TypeTemplate => TypeGuid == null ? null : Warehouse.GetTemplate((Guid)TypeGuid);
+ public ResourceTemplate TypeTemplate => TypeGuid == null ? null : Warehouse.GetTemplateByClassId((Guid)TypeGuid);
public Guid? TypeGuid { get; set; }
//public TemplateDataType(DataType type, string typeName)
@@ -51,20 +51,14 @@ namespace Esiur.Resource.Template
_ when t == typeof(IResource) => DataType.Void, // Dynamic resource (unspecified type)
_ when typeof(Structure).IsAssignableFrom(t) || t == typeof(ExpandoObject) => DataType.Structure,
_ when Codec.ImplementsInterface(t, typeof(IResource)) => DataType.Resource,
+ _ when Codec.ImplementsInterface(t, typeof(IRecord)) => DataType.Record,
_ => DataType.Void
};
- //string tn = dt switch
- //{
- // DataType.Resource => t.FullName,
- // DataType.Structure when t != typeof(Structure) => t.FullName,
- // _ => null
- //};
-
Guid? typeGuid = null;
- if (dt == DataType.Resource)
+ if (dt == DataType.Resource || dt == DataType.Record)
typeGuid = ResourceTemplate.GetTypeGuid(t);
if (type.IsArray)
@@ -80,11 +74,9 @@ namespace Esiur.Resource.Template
public byte[] Compose()
{
if (Type == DataType.Resource ||
- Type == DataType.ResourceArray)//||
- //Type == DataType.DistributedResource ||
- //Type == DataType.DistributedResourceArray ||
- //Type == DataType.Structure ||
- //Type == DataType.StructureArray)
+ Type == DataType.ResourceArray ||
+ Type == DataType.Record ||
+ Type == DataType.RecordArray)
{
var guid = DC.ToBytes((Guid)TypeGuid);
return new BinaryList()
@@ -102,11 +94,9 @@ namespace Esiur.Resource.Template
{
var type = (DataType)data[offset++];
if (type == DataType.Resource ||
- type == DataType.ResourceArray)//||
- // type == DataType.DistributedResource ||
- // type == DataType.DistributedResourceArray)// ||
- // type == DataType.Structure ||
- // type == DataType.StructureArray)
+ type == DataType.ResourceArray ||
+ type == DataType.Record ||
+ type == DataType.RecordArray)
{
var guid = data.GetGuid(offset);
return (17, new TemplateDataType() { Type = type, TypeGuid = guid });
diff --git a/Esiur/Resource/Template/TemplateType.cs b/Esiur/Resource/Template/TemplateType.cs
new file mode 100644
index 0000000..58d39a7
--- /dev/null
+++ b/Esiur/Resource/Template/TemplateType.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Resource.Template
+{
+ public enum TemplateType:byte
+ {
+ Resource,
+ Record,
+ }
+}
diff --git a/Esiur/Resource/Warehouse.cs b/Esiur/Resource/Warehouse.cs
index d7ee258..4712f88 100644
--- a/Esiur/Resource/Warehouse.cs
+++ b/Esiur/Resource/Warehouse.cs
@@ -54,6 +54,7 @@ namespace Esiur.Resource
static uint resourceCounter = 0;
static KeyList templates = new KeyList();
+ static KeyList wrapperTemplates = new KeyList();
static bool warehouseIsOpen = false;
@@ -120,8 +121,14 @@ namespace Esiur.Resource
var generatedType = assembly.GetType("Esiur.Generated");
if (generatedType != null)
{
- var types = (Type[])generatedType.GetProperty("Types").GetValue(null);
- foreach (var t in types)
+ var resourceTypes = (Type[])generatedType.GetProperty("Resources").GetValue(null);
+ foreach (var t in resourceTypes)
+ {
+ PutTemplate(new ResourceTemplate(t), true);
+ }
+
+ var recordTypes = (Type[])generatedType.GetProperty("Records").GetValue(null);
+ foreach (var t in recordTypes)
{
PutTemplate(new ResourceTemplate(t));
}
@@ -730,9 +737,14 @@ namespace Esiur.Resource
/// Put a resource template in the templates warehouse.
///
/// Resource template.
- public static void PutTemplate(ResourceTemplate template)
+ public static void PutTemplate(ResourceTemplate template, bool wrapper = false)
{
- if (!templates.ContainsKey(template.ClassId))
+ if (wrapper)
+ {
+ if (!wrapperTemplates.ContainsKey(template.ClassId))
+ wrapperTemplates.Add(template.ClassId, template);
+ }
+ else if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
}
@@ -742,18 +754,19 @@ namespace Esiur.Resource
///
/// .Net type.
/// Resource template.
- public static ResourceTemplate GetTemplate(Type type)
+ public static ResourceTemplate GetTemplateByType(Type type)
{
- if (!Codec.ImplementsInterface(type, typeof(IResource)))
+ if (!(Codec.ImplementsInterface(type, typeof(IResource))
+ || Codec.ImplementsInterface(type, typeof(IRecord))))
return null;
var baseType = ResourceProxy.GetBaseType(type);
- if (baseType == typeof(IResource) )
+ if (baseType == typeof(IResource)
+ || baseType == typeof(IRecord))
return null;
-
// loaded ?
foreach (var t in templates.Values)
if (t.ClassName == baseType.FullName)
@@ -770,10 +783,16 @@ namespace Esiur.Resource
///
/// Class Id.
/// Resource template.
- public static ResourceTemplate GetTemplate(Guid classId)
+ public static ResourceTemplate GetTemplateByClassId(Guid classId, bool wrapper = false)
{
- if (templates.ContainsKey(classId))
+ if (wrapper)
+ {
+ if (wrapperTemplates.ContainsKey(classId))
+ return wrapperTemplates[classId];
+ }
+ else if (templates.ContainsKey(classId))
return templates[classId];
+
return null;
}
@@ -782,7 +801,7 @@ namespace Esiur.Resource
///
/// Class name.
/// Resource template.
- public static AsyncReply GetTemplate(string className)
+ public static AsyncReply GetTemplateByClassName(string className)
{
foreach (var t in templates.Values)
if (t.ClassName == className)
diff --git a/Esiur/Tools/Esiur.psd1 b/Esiur/Tools/Esiur.psd1
new file mode 100644
index 0000000..06ef1a2
Binary files /dev/null and b/Esiur/Tools/Esiur.psd1 differ
diff --git a/Esiur/Tools/Esiur.psm1 b/Esiur/Tools/Esiur.psm1
new file mode 100644
index 0000000..7f1a4d2
--- /dev/null
+++ b/Esiur/Tools/Esiur.psm1
@@ -0,0 +1,115 @@
+function Get-Template($url, $dir, $username, $password)
+{
+
+ $lib = Resolve-Path -Path "$($PSScriptRoot)\..\lib\netstandard2.0\Esiur.dll"
+ #write-host "Lib is at $($lib)"
+
+ $assembly = [Reflection.Assembly]::LoadFile($lib)
+ $tempPath = [Esiur.Proxy.TemplateGenerator]::GetTemplate($url, $dir, $username,$password);
+
+ $startupProject = GetStartupProject
+
+ $activeConfiguration = $startupProject.ConfigurationManager.ActiveConfiguration
+ if ($activeConfiguration -eq $null)
+ {
+ throw "Unable to read project configuration settings of project '$($startupProject.ProjectName)' for the " +
+ 'active solution configuration. Try closing Package Manager Console and restarting Visual Studio. If the ' +
+ 'problem persists, use Help > Send Feedback > Report a Problem.'
+ }
+
+
+ $startupProject.ProjectItems.AddFromDirectory($tempPath) | Out-Null
+}
+
+function GetStartupProject($name, $fallbackProject)
+{
+ if ($name)
+ {
+ return Get-Project $name
+ }
+
+ $startupProjectPaths = $DTE.Solution.SolutionBuild.StartupProjects
+ if ($startupProjectPaths)
+ {
+ if ($startupProjectPaths.Length -eq 1)
+ {
+ $startupProjectPath = $startupProjectPaths[0]
+ if (![IO.Path]::IsPathRooted($startupProjectPath))
+ {
+ $solutionPath = Split-Path (GetProperty $DTE.Solution.Properties 'Path')
+ $startupProjectPath = [IO.Path]::GetFullPath((Join-Path $solutionPath $startupProjectPath))
+ }
+
+ $startupProject = GetSolutionProjects | ?{
+ try
+ {
+ $fullName = $_.FullName
+ }
+ catch [NotImplementedException]
+ {
+ return $false
+ }
+
+ if ($fullName -and $fullName.EndsWith('\'))
+ {
+ $fullName = $fullName.Substring(0, $fullName.Length - 1)
+ }
+
+ return $fullName -eq $startupProjectPath
+ }
+ if ($startupProject)
+ {
+ return $startupProject
+ }
+
+ Write-Warning "Unable to resolve startup project '$startupProjectPath'."
+ }
+ else
+ {
+ Write-Warning 'Multiple startup projects set.'
+ }
+ }
+ else
+ {
+ Write-Warning 'No startup project set.'
+ }
+
+ Write-Warning "Using project '$($fallbackProject.ProjectName)' as the startup project."
+
+ return $fallbackProject
+}
+
+function GetProperty($properties, $propertyName)
+{
+ try
+ {
+ return $properties.Item($propertyName).Value
+ }
+ catch
+ {
+ return $null
+ }
+}
+
+function GetSolutionProjects()
+{
+ $projects = New-Object 'System.Collections.Stack'
+
+ $DTE.Solution.Projects | %{
+ $projects.Push($_)
+ }
+
+ while ($projects.Count)
+ {
+ $project = $projects.Pop();
+
+ <# yield return #> $project
+
+ if ($project.ProjectItems)
+ {
+ $project.ProjectItems | ?{ $_.SubProject } | %{
+ $projects.Push($_.SubProject)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Esiur/Tools/init.ps1 b/Esiur/Tools/init.ps1
new file mode 100644
index 0000000..fa97933
--- /dev/null
+++ b/Esiur/Tools/init.ps1
@@ -0,0 +1,36 @@
+
+param($installPath, $toolsPath, $package, $project)
+
+# NB: Not set for scripts in PowerShell 2.0
+if (!$PSScriptRoot)
+{
+ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
+}
+
+if ($PSVersionTable.PSVersion -lt '3.0')
+{
+ # Import a "dummy" module that contains matching functions that throw on PS2
+ Import-Module (Join-Path $PSScriptRoot 'Esiur.psd1') -DisableNameChecking
+
+ return
+}
+
+$importedModule = Get-Module 'Esiur'
+$moduleToImport = Test-ModuleManifest (Join-Path $PSScriptRoot 'Esiur.psd1')
+$import = $true
+if ($importedModule)
+{
+ if ($importedModule.Version -le $moduleToImport.Version)
+ {
+ Remove-Module 'Esiur'
+ }
+ else
+ {
+ $import = $false
+ }
+}
+
+if ($import)
+{
+ Import-Module $moduleToImport -DisableNameChecking
+}