В теории должен работать обновленные бэкэнд

This commit is contained in:
2025-03-08 19:46:59 +03:00
parent 6aa11ba7c0
commit 4863010b47
9 changed files with 264 additions and 69 deletions
+3
View File
@@ -10,6 +10,8 @@ namespace Example.Backend
{ {
public string Name; public string Name;
public decimal Resource { get; set; }
public string GetName() public string GetName()
{ {
return Name; return Name;
@@ -30,5 +32,6 @@ namespace Example.Backend
Console.WriteLine( Console.WriteLine(
"Dispose printer with name {0}, Dispose works, Dispose works, Dispose works, Dispose works,", Name); "Dispose printer with name {0}, Dispose works, Dispose works, Dispose works, Dispose works,", Name);
} }
} }
} }
+1 -9
View File
@@ -8,16 +8,8 @@ namespace Example.Frontend
{ {
public class ClientBasedPrinter : IPrinter public class ClientBasedPrinter : IPrinter
{ {
public int Prop { get; set; } public decimal Resource { get; set; }
public int get_Prop()
{
return 1;
}
public int set_Prop(int value)
{
return 1;
}
public string GetName() public string GetName()
{ {
Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!! Vova likes that:)"); Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!! Vova likes that:)");
+1
View File
@@ -9,6 +9,7 @@ namespace Example.Shared
[SharedObjectInterface] [SharedObjectInterface]
public interface IPrinter : IDisposable, IShared public interface IPrinter : IDisposable, IShared
{ {
decimal Resource { get; set; }
string GetName(); string GetName();
Task<IPage> Print(string text, bool someParameter, CancellationToken cancellationToken); Task<IPage> Print(string text, bool someParameter, CancellationToken cancellationToken);
event Action<IPage> OnPrint; event Action<IPage> OnPrint;
+162 -17
View File
@@ -3,9 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -33,6 +31,7 @@ namespace mROA.Codegen
var frontendContextRepo = new List<string>(); var frontendContextRepo = new List<string>();
List<IMethodSymbol> innerMethods = new List<IMethodSymbol>(); List<IMethodSymbol> innerMethods = new List<IMethodSymbol>();
List<string> invokers = new List<string>();
var declarations = classes.ToList().OrderBy(i => i.Identifier.Text).ToList(); var declarations = classes.ToList().OrderBy(i => i.Identifier.Text).ToList();
foreach (var classDeclarationSyntax in declarations) foreach (var classDeclarationSyntax in declarations)
{ {
@@ -43,7 +42,7 @@ namespace mROA.Codegen
var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
var className = classDeclarationSyntax.Identifier.Text; var className = classDeclarationSyntax.Identifier.Text;
innerMethods = CollectMembers(classSymbol); innerMethods.AddRange(CollectMembers(classSymbol));
var associated = innerMethods.Select(i => i.AssociatedSymbol).Where(i => i != null) var associated = innerMethods.Select(i => i.AssociatedSymbol).Where(i => i != null)
.Distinct(SymbolEqualityComparer.Default).Cast<ISymbol>().ToList(); .Distinct(SymbolEqualityComparer.Default).Cast<ISymbol>().ToList();
@@ -58,16 +57,16 @@ namespace mROA.Codegen
var declaredMethods = new List<string>(); var declaredMethods = new List<string>();
var propertiesAccessMethods = new List<(string, IMethodSymbol)>(); var propertiesAccessMethods = new List<(string, IMethodSymbol)>();
var propertiesImplementations = new List<string>(associated.Count);
foreach (var method in innerMethods) foreach (var method in innerMethods)
{ {
switch (method.MethodKind) switch (method.MethodKind)
{ {
case MethodKind.PropertyGet or MethodKind.PropertySet: case MethodKind.PropertyGet or MethodKind.PropertySet:
GeneratePropertyMethod(method, innerMethods, propertiesAccessMethods); GeneratePropertyMethod(method, innerMethods, propertiesAccessMethods, invokers);
continue; continue;
default: default:
GenerateDeclaretedMethod(method, declaredMethods, innerMethods); GenerateDeclaredMethod(method, declaredMethods, innerMethods, invokers);
break; break;
} }
} }
@@ -135,7 +134,8 @@ namespace {namespaceName}
if (innerMethods.Count != 0) if (innerMethods.Count != 0)
{ {
var methodsStringed = Array.Empty<string>(); var methodsStringed = invokers;
var coCodegenRepoCode = @$"// <auto-generated/> var coCodegenRepoCode = @$"// <auto-generated/>
using System.Collections.Generic; using System.Collections.Generic;
@@ -143,6 +143,7 @@ using System.Reflection;
using mROA.Abstract; using mROA.Abstract;
using mROA.Implementation; using mROA.Implementation;
using System; using System;
using System.Threading;
namespace mROA.Codegen namespace mROA.Codegen
{{ {{
@@ -169,7 +170,9 @@ namespace mROA.Codegen
}} }}
}} }}
"; ";
#if !DONT_ADD
context.AddSource("CoCodegenMethodRepository.g.cs", SourceText.From(coCodegenRepoCode, Encoding.UTF8)); context.AddSource("CoCodegenMethodRepository.g.cs", SourceText.From(coCodegenRepoCode, Encoding.UTF8));
#endif
} }
if (frontendContextRepo.Count != 0) if (frontendContextRepo.Count != 0)
@@ -198,8 +201,17 @@ namespace mROA.Codegen
} }
} }
private void GenerateDeclaretedMethod(IMethodSymbol method, List<string> declaretedMethods, public static string Caster(ITypeSymbol type, string inner)
List<IMethodSymbol> methods) {
if (!type.IsValueType)
return inner +
" as " +
type.ToDisplayString();
return $"({type.ToDisplayString()})" + inner;
}
private void GenerateDeclaredMethod(IMethodSymbol method, List<string> declaredMethods,
List<IMethodSymbol> methods, List<string> invokers)
{ {
var index = methods.IndexOf(method); var index = methods.IndexOf(method);
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -259,40 +271,173 @@ namespace mROA.Codegen
sb.AppendLine("\t\t}"); sb.AppendLine("\t\t}");
declaretedMethods.Add(sb.ToString()); declaredMethods.Add(sb.ToString());
var parameterTypes = string.Join(", ",
$"{string.Join(", ", parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}");
var level = "\t\t\t";
var parametersInsertList = new List<string>();
for (var i = 0; i < method.Parameters.Length; i++)
{
var parameter = method.Parameters[i];
switch (parameter.Type.Name)
{
case "CancellationToken":
parametersInsertList.Add("(CancellationToken)special[1]");
break;
case "RequestContext":
parametersInsertList.Add("special[1] as RequestContext");
break;
default:
parametersInsertList.Add(Caster(parameter.Type,
$"parameters[{parameters.IndexOf(parameter)}]"));
break;
}
}
var parametersInsert = string.Join(", ", parametersInsertList);
var backend = string.Empty;
var funcInvoking = string.Empty;
if (isAsync && !isVoid)
funcInvoking =
$"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(t.Result); }})";
else if (isAsync && isVoid)
funcInvoking =
$"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(null); }})";
else if (!isAsync && isVoid)
{
funcInvoking = $@"{{
{level} (i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert});
{level} return null;
{level} }}";
}
else
{
funcInvoking = $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert})";
}
if (isAsync)
backend = $@"new mROA.Implementation.AsyncMethodInvoker
{level}{{
{level} IsVoid = {isVoid.ToString().ToLower()},
{(isVoid ? String.Empty : (level + "\t" + "ReturnType = typeof(" + ExtractTaskType(method.ReturnType)) + "),")}
{level} ParameterTypes = new Type[] {{ {parameterTypes} }},
{level} Invoking = (i, parameters, special, post) => {funcInvoking},
{level}}}";
else
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = {isVoid.ToString().ToLower()},
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} ParameterTypes = new Type[] {{ {parameterTypes} }},
{level} Invoking = (i, parameters, special) => {funcInvoking}
{level}}}";
invokers.Add(backend);
} }
private static Predicate<IParameterSymbol> ParameterFilter = private static Predicate<IParameterSymbol> ParameterFilter =
i => i.Type.Name is "CancellationToken" or "RequestContext"; i => i.Type.Name is "CancellationToken" or "RequestContext";
public void GeneratePropertyMethod(IMethodSymbol method, List<IMethodSymbol> methods, public void GeneratePropertyMethod(IMethodSymbol method, List<IMethodSymbol> methods,
List<(string, IMethodSymbol)> propsCollection) List<(string, IMethodSymbol)> propsCollection, List<string> invokers)
{ {
var level = "\t\t\t";
var index = methods.IndexOf(method); var index = methods.IndexOf(method);
var sb = ""; string frontend;
string backend = string.Empty;
if (method.MethodKind == MethodKind.PropertyGet) if (method.MethodKind == MethodKind.PropertyGet)
{ {
var parametersArray = ""; var parametersArray = "";
if (method.Parameters.Length != 0) if (method.Parameters.Length != 0)
{ {
parametersArray = $", new object[] {{{string.Join(", ", method.Parameters.Select(p => p.Name))}}}"; parametersArray = $", new object[] {{{string.Join(", ", method.Parameters.Select(p => p.Name))}}}";
var parameterTypes = string.Join(", ",
$"{string.Join(", ", method.Parameters.Select(p => "typeof(" + p.Type.ToDisplayString() + ")"))}");
var parameterInserts = string.Join(", ",
method.Parameters.Select(p =>
{
return Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]");
// if (!p.Type.IsValueType)
// return "parameters[" + method.Parameters.IndexOf(p) + "] as " +
// p.Type.ToDisplayString();
// return $"({p.Type.ToDisplayString()})parameters[{method.Parameters.IndexOf(p)}]";
}
));
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} ParameterTypes = new Type[] {{ {parameterTypes} }},
{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}],
{level}}}";
}
else
{
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} Invoking = (i, _, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name},
{level}}}";
} }
sb = frontend =
$"get => GetResultAsync<{method.ReturnType.ToDisplayString()}>({index}{parametersArray}).GetAwaiter().GetResult();"; $"get => GetResultAsync<{method.ReturnType.ToDisplayString()}>({index}{parametersArray}).GetAwaiter().GetResult();";
} }
else else
{ {
var parametersArray = "value"; var parametersArray = "value";
if (method.Parameters.Length != 0) if (method.Parameters.Length != 1)
{ {
parametersArray = string.Join(", ", method.Parameters.Select(p => p.Name)); parametersArray = string.Join(", ", method.Parameters.Select(p => p.Name));
var parameterTypes = string.Join(", ",
$"{string.Join(", ", method.Parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}");
var parameterInserts = string.Join(", ",
method.Parameters.Take(method.Parameters.Length - 1).Select(p =>
{
return Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]");
// if (!p.Type.IsValueType)
// return "parameters[" + method.Parameters.IndexOf(p) + "] as " +
// p.Type.ToDisplayString();
// return $"({p.Type.ToDisplayString()})parameters[{method.Parameters.IndexOf(p)}]";
}
));
// var valueInsert = !method.Parameters.Last().Type.IsValueType
// ? "parameters[" + (method.Parameters.Length - 1) + "] as " +
// method.Parameters.Last().Type.ToDisplayString()
// : $"({method.Parameters.Last().Type.ToDisplayString()})parameters[{method.Parameters.Length - 1}]";
var valueInsert = Caster(method.Parameters.Last().Type,
"parameters[" + (method.Parameters.Length - 1) + "]");
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} ParameterTypes = new Type[] {{ {parameterTypes} }},
{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}] = {valueInsert},
{level}}}";
}
else
{
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, "parameters[0]")},
{level}}}";
} }
sb = $"set => CallAsync({index}, new object[] {{ {parametersArray} }}).Wait();"; frontend = $"set => CallAsync({index}, new object[] {{ {parametersArray} }}).Wait();";
} }
propsCollection.Add((sb.ToString(), method)); propsCollection.Add((frontend, method));
invokers.Add(backend);
} }
private static string ToFullString(IParameterSymbol parameter) private static string ToFullString(IParameterSymbol parameter)
@@ -307,7 +452,7 @@ namespace mROA.Codegen
private string ExtractTaskType(ITypeSymbol taskType) private string ExtractTaskType(ITypeSymbol taskType)
{ {
return (taskType as INamedTypeSymbol).TypeParameters[0].ToDisplayString(); return (taskType as INamedTypeSymbol).TypeArguments[0].ToDisplayString();
} }
public void Initialize(GeneratorInitializationContext context) public void Initialize(GeneratorInitializationContext context)
-2
View File
@@ -4,11 +4,9 @@ namespace mROA.Abstract
{ {
public interface IMethodInvoker public interface IMethodInvoker
{ {
bool IsAsync { get; }
bool IsVoid { get; } bool IsVoid { get; }
Type[] ParameterTypes { get; } Type[] ParameterTypes { get; }
Type? ReturnType { get; } Type? ReturnType { get; }
object? Invoke(object instance, object?[]? parameters, object[] special);
Type SuitableType { get; } Type SuitableType { get; }
} }
} }
@@ -90,15 +90,16 @@ namespace mROA.Implementation.Backend
var execContext = new RequestContext(command.Id, representationModule.Id); var execContext = new RequestContext(command.Id, representationModule.Id);
if (invoker is { IsAsync: true, IsVoid: false }) if (invoker is AsyncMethodInvoker { IsVoid: false } asyncNonVoidMethodInvoker)
return TypedExecuteAsync(invoker, context, castedParams, command, _cancellationRepo, return TypedExecuteAsync(asyncNonVoidMethodInvoker, context, castedParams, command,
_cancellationRepo,
representationModule, execContext); representationModule, execContext);
if (invoker.IsAsync) if (invoker is AsyncMethodInvoker asyncMethodInvoker)
return ExecuteAsync(invoker, context, castedParams, command, _cancellationRepo, return ExecuteAsync(asyncMethodInvoker, context, castedParams, command, _cancellationRepo,
representationModule, execContext); representationModule, execContext);
var result = Execute(invoker, context, castedParams, command, execContext); var result = Execute((invoker as MethodInvoker)!, context, castedParams!, command, execContext);
if (command.CommandId == -1) if (command.CommandId == -1)
{ {
#if TRACE #if TRACE
@@ -119,7 +120,7 @@ namespace mROA.Implementation.Backend
} }
} }
private static ICommandExecution Execute(IMethodInvoker invoker, object instance, object?[] parameter, private static ICommandExecution Execute(MethodInvoker invoker, object instance, object?[] parameter,
ICallRequest command, RequestContext executionContext) ICallRequest command, RequestContext executionContext)
{ {
try try
@@ -150,7 +151,7 @@ namespace mROA.Implementation.Backend
} }
} }
private ICommandExecution ExecuteAsync(IMethodInvoker invoker, object instance, object?[]? parameters, private ICommandExecution ExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters,
ICallRequest command, ICancellationRepository cancellationRepository, ICallRequest command, ICancellationRepository cancellationRepository,
IRepresentationModule representationModule, RequestContext executionContext) IRepresentationModule representationModule, RequestContext executionContext)
{ {
@@ -162,10 +163,7 @@ namespace mROA.Implementation.Backend
#endif #endif
try try
{ {
var result = (Task)invoker.Invoke(instance, parameters, new object[] { executionContext, token })!; invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ =>
result.ContinueWith(_ =>
{ {
if (token.IsCancellationRequested) if (token.IsCancellationRequested)
return; return;
@@ -182,7 +180,26 @@ namespace mROA.Implementation.Backend
multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id);
representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload); representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload);
multiClientOwnershipRepository?.FreeOwnership(); multiClientOwnershipRepository?.FreeOwnership();
}, token); });
// result.ContinueWith(_ =>
// {
// if (token.IsCancellationRequested)
// return;
//
// var payload = new FinalCommandExecution
// {
// Id = command.Id
// };
// _cancellationRepo?.FreeCancelation(command.Id);
//
// var multiClientOwnershipRepository =
// TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository;
//
// multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id);
// representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload);
// multiClientOwnershipRepository?.FreeOwnership();
// }, token);
return new AsyncCommandExecution return new AsyncCommandExecution
{ {
@@ -199,7 +216,7 @@ namespace mROA.Implementation.Backend
} }
} }
private ICommandExecution TypedExecuteAsync(IMethodInvoker invoker, object instance, object?[]? parameters, private ICommandExecution TypedExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters,
ICallRequest command, ICancellationRepository cancellationRepository, ICallRequest command, ICancellationRepository cancellationRepository,
IRepresentationModule representationModule, RequestContext executionContext) IRepresentationModule representationModule, RequestContext executionContext)
{ {
@@ -209,12 +226,9 @@ namespace mROA.Implementation.Backend
var token = tokenSource.Token; var token = tokenSource.Token;
try try
{ {
var result = invoker.Invoke(instance, parameters, new object[] { executionContext, token },
(Task)invoker.Invoke(instance, parameters, new object[] { executionContext, token })!; finalResult =>
result.ContinueWith(t =>
{ {
var finalResult = t.GetType().GetProperty("Result")?.GetValue(t);
var payload = new FinalCommandExecution<object> var payload = new FinalCommandExecution<object>
{ {
Id = command.Id, Id = command.Id,
@@ -225,9 +239,29 @@ namespace mROA.Implementation.Backend
var multiClientOwnershipRepository = var multiClientOwnershipRepository =
TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository;
multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id);
representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload); representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution,
payload);
multiClientOwnershipRepository?.FreeOwnership(); multiClientOwnershipRepository?.FreeOwnership();
}, token); });
// result.ContinueWith(t =>
// {
// var finalResult = t.GetType().GetProperty("Result")?.GetValue(t);
// var payload = new FinalCommandExecution<object>
// {
// Id = command.Id,
// Result = finalResult
// };
// _cancellationRepo!.FreeCancelation(command.Id);
//
// var multiClientOwnershipRepository =
// TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository;
// multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id);
// representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload);
// multiClientOwnershipRepository?.FreeOwnership();
// }, token);
return new AsyncCommandExecution return new AsyncCommandExecution
{ {
+16 -2
View File
@@ -5,7 +5,6 @@ namespace mROA.Implementation
{ {
public class MethodInvoker : IMethodInvoker public class MethodInvoker : IMethodInvoker
{ {
public bool IsAsync { get; set; }
public bool IsVoid { get; set; } public bool IsVoid { get; set; }
public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; public Type[] ParameterTypes { get; set; } = Type.EmptyTypes;
public Type? ReturnType { get; set; } public Type? ReturnType { get; set; }
@@ -20,7 +19,6 @@ namespace mROA.Implementation
public static readonly IMethodInvoker Dispose = new MethodInvoker public static readonly IMethodInvoker Dispose = new MethodInvoker
{ {
IsAsync = false,
IsVoid = true, IsVoid = true,
ReturnType = null, ReturnType = null,
Invoking = (instance, _, _) => Invoking = (instance, _, _) =>
@@ -31,4 +29,20 @@ namespace mROA.Implementation
SuitableType = typeof(IDisposable) SuitableType = typeof(IDisposable)
}; };
} }
public class AsyncMethodInvoker : IMethodInvoker
{
public bool IsVoid { get; set; }
public Type[] ParameterTypes { get; set; } = Type.EmptyTypes;
public Type? ReturnType { get; set; }
public Type SuitableType { get; set; }
public Action<object, object?[]?, object[], Action<object?>> Invoking { get; set; } =
(_, _, _, post) => { post.Invoke(null); };
public void Invoke(object instance, object?[]? parameters, object[] special, Action<object?> postInvokeAction)
{
Invoking(instance, parameters, special, postInvokeAction);
}
}
} }
+1 -1
View File
@@ -15,6 +15,6 @@ namespace mROA.Implementation
public enum MessageType public enum MessageType
{ {
Unknown, FinishedCommandExecution, ExceptionCommandExecution, AsyncCommandExecution, CallRequest, IdAssigning, CancelRequest Unknown, FinishedCommandExecution, ExceptionCommandExecution, AsyncCommandExecution, CallRequest, IdAssigning, CancelRequest, EventRequest
} }
} }
@@ -1,10 +1,18 @@
using System; using System;
using mROA.Abstract;
namespace mROA.Implementation namespace mROA.Implementation
{ {
#pragma warning disable CS8618, CS9264 #pragma warning disable CS8618, CS9264
public struct UniversalObjectIdentifier : IEquatable<UniversalObjectIdentifier> public struct UniversalObjectIdentifier : IEquatable<UniversalObjectIdentifier>
{ {
private static IMethodInvoker x = new MethodInvoker
{
IsVoid = false,
ReturnType = typeof(void),
ParameterTypes = Type.EmptyTypes
};
public int ContextId; public int ContextId;
public int OwnerId; public int OwnerId;