diff --git a/src/YaNco.Abstractions/IConnection.cs b/src/YaNco.Abstractions/IConnection.cs index b047262a..9b632dd9 100644 --- a/src/YaNco.Abstractions/IConnection.cs +++ b/src/YaNco.Abstractions/IConnection.cs @@ -6,11 +6,11 @@ namespace Dbosoft.YaNco { public interface IConnection : IDisposable { - Task> CommitAndWait(); - Task> Commit(); - Task> Rollback(); - Task> CreateFunction(string name); - Task> InvokeFunction(IFunction function); - Task> AllowStartOfPrograms(StartProgramDelegate callback); + EitherAsync CommitAndWait(); + EitherAsync Commit(); + EitherAsync Rollback(); + EitherAsync CreateFunction(string name); + EitherAsync InvokeFunction(IFunction function); + EitherAsync AllowStartOfPrograms(StartProgramDelegate callback); } } \ No newline at end of file diff --git a/src/YaNco.Abstractions/IRfcContext.cs b/src/YaNco.Abstractions/IRfcContext.cs index 78915101..cc1a3a1e 100644 --- a/src/YaNco.Abstractions/IRfcContext.cs +++ b/src/YaNco.Abstractions/IRfcContext.cs @@ -7,11 +7,16 @@ namespace Dbosoft.YaNco { public interface IRfcContext : IDisposable { - Task> CreateFunction(string name); - Task> InvokeFunction(IFunction function); - Task> Ping(); - Task> Commit(); - Task> CommitAndWait(); - Task> Rollback(); + EitherAsync CreateFunction(string name); + EitherAsync InvokeFunction(IFunction function); + EitherAsync Ping(); + EitherAsync Commit(); + EitherAsync CommitAndWait(); + EitherAsync Rollback(); + + Task> PingAsync(); + Task> CommitAsync(); + Task> CommitAndWaitAsync(); + Task> RollbackAsync(); } } \ No newline at end of file diff --git a/src/YaNco.Abstractions/YaNco.Abstractions.csproj b/src/YaNco.Abstractions/YaNco.Abstractions.csproj index 597d14d9..0ec99b05 100644 --- a/src/YaNco.Abstractions/YaNco.Abstractions.csproj +++ b/src/YaNco.Abstractions/YaNco.Abstractions.csproj @@ -20,7 +20,7 @@ This package contains abstraction definitions. - + all runtime; build; native; contentfiles; analyzers diff --git a/src/YaNco.Core/Connection.cs b/src/YaNco.Core/Connection.cs index 3b33190a..c8676244 100644 --- a/src/YaNco.Core/Connection.cs +++ b/src/YaNco.Core/Connection.cs @@ -69,47 +69,46 @@ public Connection( }); } - public static Task> Create(IDictionary connectionParams, IRfcRuntime runtime) + public static EitherAsync Create(IDictionary connectionParams, IRfcRuntime runtime) { - return runtime.OpenConnection(connectionParams).Map(handle => (IConnection) new Connection(handle, runtime)).AsTask(); + return runtime.OpenConnection(connectionParams).ToAsync().Map(handle => (IConnection) new Connection(handle, runtime)); } - - public Task> CommitAndWait() + public EitherAsync CommitAndWait() { return CreateFunction("BAPI_TRANSACTION_COMMIT") .SetField("WAIT", "X") - .MapAsync(f => f.Apply(InvokeFunction).Map(u => f)) + .Bind(f => f.Apply(InvokeFunction).Map(u => f)) .HandleReturn() - .MapAsync(f => Unit.Default); + .Map(f => Unit.Default); } - public Task> Commit() + public EitherAsync Commit() { return CreateFunction("BAPI_TRANSACTION_COMMIT") - .MapAsync(f => f.Apply(InvokeFunction).Map(u=>f)) + .Bind(f => f.Apply(InvokeFunction).Map(u=>f)) .HandleReturn() - .MapAsync(f => Unit.Default); + .Map(f => Unit.Default); } - public Task> Rollback() + public EitherAsync Rollback() { return CreateFunction("BAPI_TRANSACTION_ROLLBACK") - .BindAsync(InvokeFunction); + .Bind(InvokeFunction); } - public Task> CreateFunction(string name) => - _stateAgent.Tell(new CreateFunctionMessage(name)).MapAsync(r => (IFunction) r); + public EitherAsync CreateFunction(string name) => + _stateAgent.Tell(new CreateFunctionMessage(name)).ToAsync().Map(r => (IFunction) r); - public Task> InvokeFunction(IFunction function) => - _stateAgent.Tell(new InvokeFunctionMessage(function)).MapAsync(r => Unit.Default); + public EitherAsync InvokeFunction(IFunction function) => + _stateAgent.Tell(new InvokeFunctionMessage(function)).ToAsync().Map(r => Unit.Default); - public Task> AllowStartOfPrograms(StartProgramDelegate callback) => - _stateAgent.Tell(new AllowStartOfProgramsMessage(callback)).MapAsync(r => Unit.Default); + public EitherAsync AllowStartOfPrograms(StartProgramDelegate callback) => + _stateAgent.Tell(new AllowStartOfProgramsMessage(callback)).ToAsync().Map(r => Unit.Default); private class AgentMessage diff --git a/src/YaNco.Core/FunctionalDataContainerExtensions.cs b/src/YaNco.Core/FunctionalDataContainerExtensions.cs index 720e66ec..71bf4675 100644 --- a/src/YaNco.Core/FunctionalDataContainerExtensions.cs +++ b/src/YaNco.Core/FunctionalDataContainerExtensions.cs @@ -1,46 +1,39 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using LanguageExt; namespace Dbosoft.YaNco { public static class FunctionalDataContainerExtensions { - public static Either SetField(this Either self, string name, T value) + public static EitherAsync SetField(this EitherAsync self, string name, T value) where TDataContainer : IDataContainer { - return self.Bind(s => s.SetField(name, value).Map(u=> s)); - } - - public static Task> SetField(this Task> self, string name, T value) - where TDataContainer : IDataContainer - { - return self.BindAsync(s => s.SetField(name, value).Map(u => s)); + return self.Bind(s => s.SetField(name, value).Map(u => s).ToAsync()); } - public static Task> SetStructure(this Task> self, string structureName, Func, Either> map) + public static EitherAsync SetStructure(this EitherAsync self, string structureName, Func, Either> map) where TDataContainer : IDataContainer { - return self.BindAsync(dc => dc.GetStructure(structureName).Use(used => used.Apply(map).Map(u => dc))); + return self.Bind(dc => dc.GetStructure(structureName).Use(used => used.Apply(map).Map(u => dc)).ToAsync()); } - public static Task> SetTable( - this Task> self, string tableName, + public static EitherAsync SetTable( + this EitherAsync self, string tableName, IEnumerable inputList, Func, TInput, Either> map) where TDataContainer : IDataContainer => SetTable(self, tableName, () => inputList, map); - public static Task> SetTable(this Task> self, string tableName, Func> inputListFunc, Func, TInput, Either> map) + public static EitherAsync SetTable(this EitherAsync self, string tableName, Func> inputListFunc, Func, TInput, Either> map) where TDataContainer : IDataContainer { - return self.BindAsync(dc => dc.GetTable(tableName).Use(used=> used.Map(table => (dc, table, inputListFunc)) + return self.Bind(dc => dc.GetTable(tableName).Use(used=> used.Map(table => (dc, table, inputListFunc)) .Bind(t => t.inputListFunc().Map( input => t.table.AppendRow().Apply(row=>map(row,input)) - ).Traverse(l => l).Map(_ => dc)))); + ).Traverse(l => l).Map(_ => dc))).ToAsync()); } diff --git a/src/YaNco.Core/FunctionalFunctionsExtensions.cs b/src/YaNco.Core/FunctionalFunctionsExtensions.cs index 5ce74d24..863bb109 100644 --- a/src/YaNco.Core/FunctionalFunctionsExtensions.cs +++ b/src/YaNco.Core/FunctionalFunctionsExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using LanguageExt; @@ -9,21 +10,21 @@ namespace Dbosoft.YaNco public static class FunctionalFunctionsExtensions { - public static Task> Commit(this Task> self, + internal static EitherAsync Commit(this EitherAsync self, IRfcContext context) { - return self.BindAsync(res => context.Commit().MapAsync(u => res)); + return self.Bind(res => context.Commit().Map(u => res)); } - public static Task> CommitAndWait(this Task> self, + internal static EitherAsync CommitAndWait(this EitherAsync self, IRfcContext context) { - return self.BindAsync(res => context.CommitAndWait().MapAsync(u => res)); + return self.Bind(res => context.CommitAndWait().Map(u => res)); } - public static Task> HandleReturn(this Task> self) + public static EitherAsync HandleReturn(this EitherAsync self) { - return self.BindAsync(f => + return self.Bind(f => ( from ret in f.GetStructure("RETURN") from type in ret.GetField("TYPE") from id in ret.GetField("ID") @@ -35,7 +36,7 @@ from v3 in ret.GetField("MESSAGE_V3") from v4 in ret.GetField("MESSAGE_V4") from _ in ErrorOrResult(f, type, id, number, message, v1, v2, v3, v4) - select f); + select f).ToAsync()); } @@ -48,91 +49,63 @@ private static Either ErrorOrResult(TResult resu } // ReSharper disable InconsistentNaming - public static Task> CallFunction(this IRfcContext context, string functionName, Func>, Task>> Input, Func>, Task>> Output) - { - return context.CreateFunction(functionName).Use( - func => func - .Apply(Input).BindAsync(i=>func) - .BindAsync(context.InvokeFunction).Map(i => func) - .Apply(Output)); - - } - public static Task> CallFunction(this IRfcContext context, string functionName, Func>, Task>> Input, Func>, EitherAsync> Output) + public static EitherAsync CallFunction(this IRfcContext context, + string functionName, Func, EitherAsync> Input, + Func> Output) { return context.CreateFunction(functionName).Use( func => func - .Apply(Input).BindAsync(i => func) - .BindAsync(context.InvokeFunction).Map(i => func) - .Apply(f => Output(f).ToEither())); - + .Apply(Input).Bind(i => func) + .Bind(context.InvokeFunction).Bind(i => func) + .Bind(f=> Output(f).ToAsync())); } - public static Task> CallFunction(this IRfcContext context, string functionName, Func>, Task>> Output) + public static Task> CallFunctionAsync(this IRfcContext context, + string functionName, Func, EitherAsync> Input, + Func> Output) { - return context.CreateFunction(functionName).Use( - func => func - .BindAsync(context.InvokeFunction).Map(i => func) - .Apply(Output)); + return CallFunction(context, functionName, Input, Output).ToEither(); } - public static Task> CallFunction(this IRfcContext context, string functionName, Func>, EitherAsync> Output) + public static EitherAsync CallFunction(this IRfcContext context, string functionName, Func> Output) { return context.CreateFunction(functionName).Use( func => func - .BindAsync(context.InvokeFunction).Map(i => func) - .Apply(f => Output(f).ToEither())); + .Bind(context.InvokeFunction).Bind(i => func) + .Bind(f => Output(f).ToAsync())); } - public static Task> CallFunctionAsUnit(this IRfcContext context, string functionName, Func>, Task>> Input) + public static Task> CallFunctionAsync(this IRfcContext context, + string functionName, Func> Output) { - return context.CreateFunction(functionName).Use( - func => func - .Apply(Input).BindAsync(i => func) - .BindAsync(context.InvokeFunction)); + return CallFunction(context, functionName, Output).ToEither(); } - public static Task> CallFunctionAsUnit(this IRfcContext context, string functionName, Func>, Task>> Input, Func>, Task>> Output) + public static EitherAsync CallFunctionAsUnit(this IRfcContext context, string functionName, Func, EitherAsync> Input) { return context.CreateFunction(functionName).Use( func => func - .Apply(Input).BindAsync(i => func) - .BindAsync(context.InvokeFunction).Map(i => func) - .Apply(Output)).MapAsync(f => Unit.Default); - + .Apply(Input).Bind(i => func) + .Bind(context.InvokeFunction)); } - public static Task> CallFunctionAsUnit(this IRfcContext context, string functionName, Func>, Task>> Input, Func>, EitherAsync> Output) + public static Task> CallFunctionAsUnitAsync(this IRfcContext context, + string functionName, Func, EitherAsync> Input) { - return context.CreateFunction(functionName).Use( - func => func - .Apply(Input).BindAsync(i => func) - .BindAsync(context.InvokeFunction).Map(i => func) - .Apply(f => Output(f).ToEither())) - .MapAsync(f => Unit.Default); - + return CallFunctionAsUnit(context, functionName, Input).ToEither(); } - public static Task> CallFunction(this IRfcContext context, string functionName) + public static EitherAsync CallFunction(this IRfcContext context, string functionName) { return context.CreateFunction(functionName).Use( - func => func.BindAsync(context.InvokeFunction)); - - } + func => func.Bind(context.InvokeFunction)); - public static Task> GetField(this Task> self, string name) - { - return self.BindAsync(s => s.GetField(name)); - } - - static public EitherAsync> MapTable(this Task> self, string tableName, Func> mapperFunc) - { - return self.BindAsync(f => f.MapTable(tableName, mapperFunc)).ToAsync(); } - static public EitherAsync MapStructure(this Task> self, string structureName, Func> mapperFunc) + public static Task> CallFunctionAsync(this IRfcContext context, string functionName) { - return self.BindAsync(f => f.MapStructure(structureName, mapperFunc)).ToAsync(); + return CallFunction(context, functionName).ToEither(); } } } diff --git a/src/YaNco.Core/RfcContext.cs b/src/YaNco.Core/RfcContext.cs index 65fdaa1a..0fd6a692 100644 --- a/src/YaNco.Core/RfcContext.cs +++ b/src/YaNco.Core/RfcContext.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using LanguageExt; @@ -8,55 +9,77 @@ namespace Dbosoft.YaNco public class RfcContext : IRfcContext { - private readonly Func>> _connectionBuilder; + private readonly Func> _connectionBuilder; private Option _connection; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1); - public RfcContext(Func>> connectionBuilder) + public RfcContext(IDictionary connectionParams, ILogger logger = null) + { + _connectionBuilder = () => Connection.Create(connectionParams, new RfcRuntime(logger)); + } + + public RfcContext(Func> connectionBuilder) { _connectionBuilder = connectionBuilder; } - private async Task> GetConnection() + private EitherAsync GetConnection() { - await _semaphore.WaitAsync().ConfigureAwait(false); - try - { - return await _connection.MatchAsync(s => Prelude.Right(s), - async () => - { - var res = await _connectionBuilder(); - res.Map(connection => _connection = Prelude.Some(connection)); - return res; - }).ConfigureAwait(false); - } - finally + + async Task> GetConnectionAsync() { - _semaphore.Release(); + await _semaphore.WaitAsync().ConfigureAwait(false); + try + { + return await _connection.MatchAsync(s => Prelude.Right(s), + async () => + { + var res = await _connectionBuilder().ToEither(); + res.Map(connection => _connection = Prelude.Some(connection)); + return res; + }).ConfigureAwait(false); + } + finally + { + _semaphore.Release(); + } } + + return GetConnectionAsync().ToAsync(); } - public Task> InvokeFunction(IFunction function) + public EitherAsync InvokeFunction(IFunction function) { - return GetConnection().BindAsync(conn => conn.InvokeFunction(function)); + return GetConnection() + .Bind(conn => conn.InvokeFunction(function)); } - public Task> Ping() + public EitherAsync Ping() { return CreateFunction("RFC_PING") - .BindAsync(InvokeFunction) - .MapAsync(r => (IRfcContext) this ); + .Bind(InvokeFunction) + .Map(r => (IRfcContext) this ); + } + + public Task> PingAsync() + { + return Ping().ToEither(); } - public Task> CreateFunction(string name) => GetConnection().BindAsync(conn => conn.CreateFunction(name)); - public Task> Commit() => GetConnection().BindAsync(conn => conn.Commit()); + public EitherAsync CreateFunction(string name) => GetConnection().Bind(conn => conn.CreateFunction(name)); + + public EitherAsync Commit() => GetConnection().Bind(conn => conn.Commit()); + + public EitherAsync CommitAndWait() => GetConnection().Bind(conn => conn.CommitAndWait()); - public Task> CommitAndWait() => GetConnection().BindAsync(conn => conn.CommitAndWait()); + public EitherAsync Rollback() => GetConnection().Bind(conn => conn.Rollback()); - public Task> Rollback() => GetConnection().BindAsync(conn => conn.Rollback()); + public Task> CommitAsync() => Commit().ToEither(); + public Task> CommitAndWaitAsync() => CommitAndWait().ToEither(); + public Task> RollbackAsync() => Rollback().ToEither(); public void Dispose() { diff --git a/src/YaNco.Core/YaNco.Core.csproj b/src/YaNco.Core/YaNco.Core.csproj index 04650b1e..e48ff7cd 100644 --- a/src/YaNco.Core/YaNco.Core.csproj +++ b/src/YaNco.Core/YaNco.Core.csproj @@ -22,7 +22,7 @@ This package contains the core implementation. - + all runtime; build; native; contentfiles; analyzers diff --git a/test/SAPSystemTests/Program.cs b/test/SAPSystemTests/Program.cs index 8c165c04..ca008dca 100644 --- a/test/SAPSystemTests/Program.cs +++ b/test/SAPSystemTests/Program.cs @@ -56,15 +56,9 @@ static async Task Main(string[] args) return RfcErrorInfo.Ok(); }; - var runtime = new RfcRuntime(new SimpleConsoleLogger()); - - - Task> ConnFunc() => Connection.Create(settings, runtime); - - using (var context = new RfcContext(ConnFunc)) + using (var context = new RfcContext(settings, new SimpleConsoleLogger())) { - await context.Ping(); - + await context.PingAsync(); long totalTest1 = 0; long totalTest2 = 0; @@ -91,25 +85,20 @@ private static async Task RunPerformanceTest01(IRfcContext context, int ro { var watch = Stopwatch.StartNew(); - var result = await context.CallFunction("ZYANCO_PT_READ_1", + var result = await context.CallFunctionAsync("ZYANCO_PT_READ_1", func => SetRows(func, rows), - func => func.BindAsync(f => - from resultTable in f.GetTable("ET_DATA") - from row in resultTable.Rows.Map(s => - from char40 in s.GetField("FIELD_CHAR40") - from char01 in s.GetField("FIELD_CHAR01") - from int04 in s.GetField("FIELD_INT4") - //from s in s.GetField("FIELD_STRING") - - select new TestData - { - Char01 = char01, - Char40 = char40, - Int04 = int04 - } - ).Traverse(l => l) - - select row)).MapAsync(x => x.ToArray()); + func => func.MapTable("ET_DATA", s => + from char40 in s.GetField("FIELD_CHAR40") + from char01 in s.GetField("FIELD_CHAR01") + from int04 in s.GetField("FIELD_INT4") + //from s in s.GetField("FIELD_STRING") + + select new TestData + { + Char01 = char01, + Char40 = char40, + Int04 = int04 + })); watch.Stop(); return watch.ElapsedMilliseconds; @@ -122,9 +111,7 @@ private static async Task RunPerformanceTest02(IRfcContext context, int ro var result = await context.CallFunction("ZYANCO_PT_READ_2", func => SetRows(func, rows), - func => func.BindAsync(f => - from resultTable in f.GetTable("ET_DATA") - from row in resultTable.Rows.Map(s => + func => func.MapTable("ET_DATA", s => from char40 in s.GetField("FIELD_CHAR40") from char01 in s.GetField("FIELD_CHAR01") from int04 in s.GetField("FIELD_INT4") @@ -137,15 +124,14 @@ from str in s.GetField("FIELD_STRING") Int04 = int04, String = str } - ).Traverse(l => l) - select row)).MapAsync(x => x.ToArray()); + )).ToEither(); watch.Stop(); return watch.ElapsedMilliseconds; } - private static Task> SetRows(Task> func, in int rows) + private static EitherAsync SetRows(EitherAsync func, in int rows) { return rows == 0 ? func : func.SetField("IV_UP_TO", rows); }