<repo_name>apacherose/pr.reau<file_sep>/PropertyRegister.REAU.Test/Mocks/Extensions.cs
﻿using PropertyRegister.REAU.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public static class Extensions
    {        
    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/ActionDispatcherMock.cs
﻿using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Common;
using Rebus.Activation;
using Rebus.Bus;
using Rebus.Config;
using Rebus.Routing.TypeBased;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class RebusActionDispatcherMock : IActionDispatcher
    {
        private readonly BuiltinHandlerActivator HandlerActivator = new BuiltinHandlerActivator();
        private readonly IBus Bus;

        public RebusActionDispatcherMock()
        {
            Bus = Configure.With(HandlerActivator)
                    .Transport(t => t.UseMsmq("apps_queue"))
                    .Routing(r => r.TypeBased().Map<long>("ApplicationAcceptance"))
                    .Start();
        }

        public Task SendAsync(object actionData)
        {
            //TODO check if {actionname} exists in routing mapping?
            
            return Bus.Send(actionData);
        }

        public void Dispose()
        {
            HandlerActivator.Dispose();
        }
    }

    public class DummyActionDispatcherMock : IActionDispatcher
    {
        private readonly JsonSerializer Serializer;

        public DummyActionDispatcherMock()
        {
            Serializer = new JsonSerializer();
        }

        public Task SendAsync(object actionData)
        {
            JsonSerializer serializer = new JsonSerializer();

            string path = @"c:\tmp\reau_dispacher_queue\" + $"{DateTime.Now.ToString("yyyyMMddhhmmss")}.txt";

            using (StreamWriter sw = new StreamWriter(path))
            {
                using (JsonWriter writer = new JsonTextWriter(sw))
                {
                    Serializer.Serialize(writer, actionData);
                }
            }

            return Task.CompletedTask;
        }

        public void Dispose()
        {
        }
    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/DocumentServiceMock.cs
﻿using PropertyRegister.REAU.Common;
using PropertyRegister.REAU.Common.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class DocumentServiceMock : IDocumentService
    {
        public Task DeleteDocumentAsync(string documentIdentifier)
        {
            throw new NotImplementedException();
        }

        public Task<DocumentData> GetDocumentAsync(string documentIdentifier)
        {
            throw new NotImplementedException();
        }

        public Task<List<DocumentData>> GetDocumentsAsync(List<string> documentIdentifiers)
        {
            throw new NotImplementedException();
        }

        public Task<IEnumerable<DocumentData>> GetDocumentsAsync(List<string> documentIdentifiers, bool loadContent = false)
        {
            throw new NotImplementedException();
        }

        public Task<DocumentData> SaveDocumentAsync(DocumentCreateRequest request)
        {
            return Task.FromResult(new DocumentData() { DocID = 100 });
        }

        Task<DocumentCreateResult> IDocumentService.SaveDocumentAsync(DocumentCreateRequest request)
        {
            throw new NotImplementedException();
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Persistence/ServiceDocumentRepository.cs
﻿using PropertyRegister.REAU.Applications.Models;
using CNSys.Data;

namespace PropertyRegister.REAU.Applications.Persistence
{    
    public interface IServiceDocumentRepository : IRepository<ServiceDocument, long?, object>
    {
    }

    public class ServiceDocumentRepository : RepositoryBase<ServiceDocument, long?, object, ApplicationProcessDataContext>, IServiceDocumentRepository
    {
        public ServiceDocumentRepository() : base(false)
        {
        }

        protected override void CreateInternal(ApplicationProcessDataContext context, ServiceDocument item)
        {
            base.CreateInternal(context, item);
        }

        protected override void UpdateInternal(ApplicationProcessDataContext context, ServiceDocument item)
        {
            base.UpdateInternal(context, item);
        }

        protected override void DeleteInternal(ApplicationProcessDataContext context, ServiceDocument item)
        {
            base.DeleteInternal(context, item);
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Models/ServiceInstanceAction.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Models
{
    public enum ServicеActionTypes
    {
        /// <summary>
        /// пъровначално подаване на заявление
        /// </summary>
        ApplicationAcceptance = 1,

        /// <summary>
        /// наредено задължение
        /// </summary>
        RequestedPayment = 2,

        WaitingRegistrationInPR = 3,

        /// <summary>
        /// Регистриране в ИС на ИР
        /// </summary>
        ApplicationRegistrationInPR = 4,

        /// <summary>
        /// изпълнение на ел.справка с отдалечен достъп
        /// </summary>
        ExecuteRemoteReportInPR = 5,

        PaymentChange = 6
    }


    /// <summary>
    /// Действия по услугите
    /// </summary>
    public class ServiceInstanceAction
    {
        /// <summary>
        /// PK
        /// </summary>
        public long? ServiceActionID { get; set; }

        /// <summary>
        /// Идентификатор на операция в рамките на която е действието
        /// </summary>
        public Guid OperationID { get; set; }

        /// <summary>
        /// Идентификатор на операция в рамките на която е действието
        /// </summary>
        public long? ServiceOperationID { get; set; }

        /// <summary>
        /// Връзка към услугата.
        /// </summary>
        public long ServiceInstanceID { get; set; }

        /// <summary>
        /// Връзка към заявлението
        /// </summary>
        public long ApplicationID { get; set; }

        /// <summary>
        /// Вид на действието
        /// </summary>
        public ServicеActionTypes? ActionTypeID { get; set; }

        /// <summary>
        /// Статус на заявлението, до който води действието.
        /// </summary>
        public ApplicationStatuses ApplicationStatus { get; set; }

        /// <summary>
        /// Връзка към документ-резултат от действието (може и да липсва).
        /// </summary>
        public long? ServiceDocumentID { get; set; }

        /// <summary>
        /// пояснителен текст съпътсващ действието.
        /// </summary>
        public string Description { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Persistence/REAUDbContext.cs
﻿using CNSys.Data;
using CNSys.Security;
using Oracle.ManagedDataAccess.Client;
using System;
using System.Configuration;
using System.Threading;

namespace PropertyRegister.REAU.Persistence
{
    public class REAUDbContext : DbContext
    {
        #region Constructors

        public REAUDbContext(ConnectionStringSettings connectionString, DbContextOptions options)
            : base(connectionString, options)
        {
        }

        #endregion

        #region Overriden Methods

        protected override void InitConnection()
        {            
            var dataSourceUser = Thread.CurrentPrincipal as IDataSourceUser;

            if (dataSourceUser != null)
            {
                int user = int.Parse(dataSourceUser.ClientID);

                const string currentUserKey = "currentUser";

                object currentUser = null;
                bool hasContextItemsUser = this.ConnectionContextItems.TryGetValue(currentUserKey, out currentUser);
                if (!hasContextItemsUser ||
                    int.Parse(currentUser.ToString()) != user)
                {
                    (Connection as OracleConnection).ClientId = user.ToString();

                    if (hasContextItemsUser)
                        ConnectionContextItems[currentUserKey] = user;
                    else
                        ConnectionContextItems.Add(currentUserKey, user);
                }
            }
        }

        #endregion

        #region Static Interface

        public static new REAUDbContext CreateDefaultSerializationContext()
        {
            return CreateSerializationContext(ConnectionStrings.DefaultConnectionString, true);
        }

        public static new REAUDbContext CreateSerializationContext(ConnectionStringSettings connectionStringSettings, bool useTransaction)
        {
            if (useTransaction)
                return CreateContext(connectionStringSettings, DbContextOptions.UseTransaction);
            else
                return CreateContext(connectionStringSettings, DbContextOptions.None);
        }

        public static new REAUDbContext CreateContext(ConnectionStringSettings connectionStringSettings, DbContextOptions options)
        {
            if ((options & DbContextOptions.SuppressConnection) == DbContextOptions.SuppressConnection)
                throw new NotImplementedException("SuppressConnection option is not yet implemented.");

            return new REAUDbContext(connectionStringSettings, options);
        }

        #endregion
    }
}
<file_sep>/PropertyRegister.REAU/Common/Persistence/ServiceOperationRepository.cs
﻿using PropertyRegister.REAU.Common.Models;
using CNSys.Data;
using System;
using System.Collections.Generic;

namespace PropertyRegister.REAU.Common.Persistence
{
    public class ServiceOperationSearchCriteria
    {
        public long? ServiceOperationID { get; set; }
        public Guid? OperationID { get; set; }
        public ServiceOperationTypes? ServiceOperationType { get; set; }
    }

    public interface IServiceOperationRepository : IRepository<ServiceOperation, long?, ServiceOperationSearchCriteria>
    {
    }

    public class ServiceOperationRepository : RepositoryBase<ServiceOperation, long?, ServiceOperationSearchCriteria, CommonDataContext>, IServiceOperationRepository
    {
        public ServiceOperationRepository() 
            : base(false)
        {
        }

        protected override void CreateInternal(CommonDataContext context, ServiceOperation item)
        {
            context.ServiceOperationCreate(item.OperationID.ToString(), (int)item.ServiceOperationType,
                out long serviceOperationID, out bool isCompleted, out string result);

            item.ServiceOperationID = serviceOperationID;
            item.IsCompleted = isCompleted;
            item.Result = result;
        }

        protected override void UpdateInternal(CommonDataContext context, ServiceOperation item) =>
            context.ServiceOperationUpdate(item.ServiceOperationID.Value, item.OperationID.ToString(), item.IsCompleted, item.Result.ToString());

        protected override IEnumerable<ServiceOperation> SearchInternal(CommonDataContext context, ServiceOperationSearchCriteria searchCriteria)
        {
            IEnumerable<ServiceOperation> res;

            using (var data = context.ServiceOperationSearch(searchCriteria.ServiceOperationID, searchCriteria.OperationID?.ToString(), (int?)searchCriteria.ServiceOperationType))
            {
                res = data.ReadToList<ServiceOperation>();
            }

            return res;
        }
    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/TestPrincipal.cs
﻿using CNSys.Security;
using PropertyRegister.REAU.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class TestPrincipal : IPrincipal, IDataSourceUser
    {
        public TestPrincipal(string clientID) => ClientID = clientID;

        public IIdentity Identity => throw new NotImplementedException();

        public string ClientID { get; }

        public string ProxyUserID => throw new NotImplementedException();

        public bool IsInRole(string role)
        {
            throw new NotImplementedException();
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/MessageHandlers/ApplicationAcceptedResultHandler.cs
﻿using CNSys.Security;
using PropertyRegister.REAU.Applications.Results;
using Rebus.Handlers;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.MessageHandlers
{
    public class ApplicationAcceptedResultHandler : IHandleMessages<ApplicationAcceptedResult>
    {
        private readonly IApplicationProcessingService ProcessingService;

        public ApplicationAcceptedResultHandler(IApplicationProcessingService processingService)
        {
            ProcessingService = processingService;
        }

        public async Task Handle(ApplicationAcceptedResult message)
        {
            using (var mti = new ManagedThreadImpersonation(new GenericDataSourceUser("1", Thread.CurrentPrincipal)))
            {
                await ProcessingService.ProcessApplicationAsync(message.ApplicationID);
            }

            Console.WriteLine($"Handled message of {nameof(message)} with value {message.ApplicationNumber}.");
        }
    }

    public class ApplicationProcessedResultHandler : IHandleMessages<ApplicationProcessedResult>
    {
        private readonly IApplicationProcessingService ProcessingService;

        public ApplicationProcessedResultHandler(IApplicationProcessingService processingService)
        {
            ProcessingService = processingService;
        }

        public Task Handle(ApplicationProcessedResult message)
        {
            throw new NotImplementedException("in progress");
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Integration/Objects.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace PropertyRegister.REAU.Applications.Integration
{
    //[XmlType(AnonymousType = true, Namespace = "reau.pr.bg")]
    //[XmlRoot("Application", Namespace = "reau.pr.bg", IsNullable = false)]
    //public class PortalApplication
    //{
    //    public Header Header { get; set; }

    //    [XmlArrayItem("AttachedDocument")]
    //    public AttachedDocument[] AttachedDocuments { get; set; }
    //}

    //public class Header
    //{
    //    public ServiceInfo ServiceInfo { get; set; }
    //    public string OfficeID { get; set; }
    //    public Applicant Applicant { get; set; }
    //}

    //public class ServiceInfo
    //{
    //    public string Code { get; set; }
    //    public decimal? Obligation { get; set; }
    //}

    //public class Applicant
    //{
    //    public string CIN { get; set; }
    //}

    //public class AttachedDocument
    //{
    //    public Guid BackOfficeGUID { get; set; }
    //}
}
<file_sep>/ConsoleApp0/Program.cs
﻿using PropertyRegister.REAU.Applications;
using Rebus.Activation;
using Rebus.Config;
using Rebus.Routing.TypeBased;
using System;
using System.Threading.Tasks;

namespace ConsoleApp0
{
    class Program
    {
        static void Main(string[] args)
        { 
            MainAsync().GetAwaiter().GetResult();            
        }

        static async Task MainAsync()
        {
            string connString = "Data Source=pr_reau; User ID=pr_reau_dev; Password=<PASSWORD>;";

            using (var activator = new BuiltinHandlerActivator())
            {                

                Configure.With(activator)
                    //.Transport(t => t.UseMsmqAsOneWayClient())
                    .Transport(t => t.UseOracleAsOneWayClient(connString, "mbus_messages"))
                    .Routing(r => r.TypeBased().Map<ApplicationReceivedMessage>("consumer.input"))
                    .Start();                

                while (true)
                {
                    Console.WriteLine(@"a) Publish string");

                    var keyChar = char.ToLower(Console.ReadKey(true).KeyChar);
                    var bus = activator.Bus.Advanced.SyncBus;

                    if (keyChar == 'q')
                        goto consideredHarmful;
                    else
                    {
                        bus.Send(new ApplicationReceivedMessage(keyChar));
                    }
                }

            consideredHarmful:;
                Console.WriteLine("Quitting!");
            }
        }
    }
}
<file_sep>/PropertyRegister.REAU/Common/DeferredInitializedStream.cs
﻿using CNSys.Data;
using PropertyRegister.REAU.Persistence;
using System;
using System.IO;

namespace PropertyRegister.REAU.Common
{
    public class DeferredDbStream : Stream
    {
        private bool _isInnerStreamInitialized = false;
        private readonly Func<Tuple<DbContext, Stream>> _streamFactory;
        private Stream _innerStream;
        private DbContext _dbContext;

        public DeferredDbStream(Func<Tuple<DbContext, Stream>> streamFactory)
        {
            _streamFactory = streamFactory ?? throw new ArgumentNullException(nameof(streamFactory));
        }
        
        #region Overriden Methods

        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            EnsureInnerStreamInitialized();

            return _innerStream.BeginRead(buffer, offset, count, callback, state);
        }

        public override int EndRead(IAsyncResult asyncResult)
        {
            EnsureInnerStreamInitialized();
            return _innerStream.EndRead(asyncResult);
        }

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            EnsureInnerStreamInitialized();
            return _innerStream.BeginWrite(buffer, offset, count, callback, state);
        }

        public override void EndWrite(IAsyncResult asyncResult)
        {
            EnsureInnerStreamInitialized();
            _innerStream.EndWrite(asyncResult);
        }

        public override bool CanRead
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.CanRead;
            }
        }

        public override bool CanSeek
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.CanSeek;
            }
        }

        public override bool CanTimeout
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.CanTimeout;
            }
        }

        public override bool CanWrite
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.CanWrite;
            }
        }

        public override void Close()
        {
            base.Close();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (_dbContext != null)
            {
                _dbContext.Complete();
                _dbContext.Dispose();
                _dbContext = null;
            }

            if (_innerStream != null)
            {
                _innerStream.Dispose();
                _innerStream = null;
            }
        }

        public override long Length
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.Length;
            }
        }

        public override long Position
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.Position;
            }
            set
            {
                EnsureInnerStreamInitialized();
                _innerStream.Position = value;
            }
        }

        public override int ReadTimeout
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.ReadTimeout;
            }
            set
            {
                EnsureInnerStreamInitialized();
                _innerStream.ReadTimeout = value;
            }
        }

        public override int WriteTimeout
        {
            get
            {
                EnsureInnerStreamInitialized();
                return _innerStream.WriteTimeout;
            }
            set
            {
                EnsureInnerStreamInitialized();
                _innerStream.WriteTimeout = value;
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            EnsureInnerStreamInitialized();
            return _innerStream.Read(buffer, offset, count);
        }

        public override int ReadByte()
        {
            EnsureInnerStreamInitialized();
            return _innerStream.ReadByte();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            EnsureInnerStreamInitialized();
            _innerStream.Write(buffer, offset, count);
        }

        public override void WriteByte(byte value)
        {
            EnsureInnerStreamInitialized();
            _innerStream.WriteByte(value);
        }

        public override void SetLength(long value)
        {
            EnsureInnerStreamInitialized();
            _innerStream.SetLength(value);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            EnsureInnerStreamInitialized();
            return _innerStream.Seek(offset, origin);
        }

        public override void Flush()
        {
            EnsureInnerStreamInitialized();
            _innerStream.Flush();
        }

        #endregion

        #region Protected Interface

        protected virtual void InitializeInnerStream()
        {
            _isInnerStreamInitialized = true;
            (_dbContext, _innerStream) = _streamFactory();
        }

        protected void EnsureInnerStreamInitialized()
        {
            if (!_isInnerStreamInitialized)
            {
                InitializeInnerStream();
            }
        }

        #endregion
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Persistence/ApplicationDocumentRepository.cs
﻿using PropertyRegister.REAU.Applications.Models;
using CNSys.Data;

namespace PropertyRegister.REAU.Applications.Persistence
{
    public interface IApplicationDocumentRepository : IRepository<ApplicationDocument, long?, object>
    {
    }

    public class ApplicationDocumentRepository : RepositoryBase<ApplicationDocument, long?, object, ApplicationProcessDataContext>, IApplicationDocumentRepository
    {
        public ApplicationDocumentRepository() : base(false)
        {
        }

        protected override void CreateInternal(ApplicationProcessDataContext context, ApplicationDocument item)
        {
            context.ApplicationDocumentCreate(item.ApplicationID, item.DocumentType.Value, item.DocumentID, out long appDocumentID);

            item.ApplicationDocumentID = appDocumentID;
        }

        protected override void DeleteInternal(ApplicationProcessDataContext context, ApplicationDocument item)
        {
            base.DeleteInternal(context, item);
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Results/ApplicationAcceptedResult.cs
﻿using PropertyRegister.REAU.Applications.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Results
{    
    public class ApplicationAcceptedResult
    {
        public string ApplicationNumber { get; set; }
        public ApplicationStatuses? ApplicationStatus { get; set; }
        public DateTime RegistrationTime { get; set; }

        public ICollection<string> Errors { get; set; }
        public long ApplicationID { get; set; }
    }   
}
<file_sep>/PropertyRegister.REAU/Persistence/DataContextHelper.cs
﻿using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace PropertyRegister.REAU.Persistence
{
    public class DataContextHelper
    {
        public static CustomPropertyTypeMap ColumnMap<T>(Dictionary<string, string> columnMappings = null)
        {
            return new CustomPropertyTypeMap(typeof(T),
                                    (type, columnName) =>
                                    {
                                        return type.GetProperties().FirstOrDefault(prop => string.Compare(GetMappedColumnName(prop, columnMappings), columnName, true) == 0);
                                    });
        }

        private static string GetMappedColumnName(MemberInfo member, Dictionary<string, string> columnMappings)
        {
            if (member == null)
                return null;

            var attrib = (DapperColumnAttribute)Attribute.GetCustomAttribute(member, typeof(DapperColumnAttribute), false);

            if (attrib != null)
                return attrib.Name;

            string mappedPropName = null;
            if (columnMappings?.Any() == true
                && columnMappings.TryGetValue(member.Name, out mappedPropName))
                return mappedPropName;

            return null;
        }
    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/Sets.cs
﻿using PropertyRegister.REAU.Applications.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class DbDataSets
    {
        public IEnumerable<Applications.Models.Application> Applications = new List<Applications.Models.Application>()
        {
            new Applications.Models.Application()
            {
                ApplicationID = 100,
                ServiceInstanceID = 100,
                ApplicationIdentifier = "20181109/777",
                Status = REAU.Applications.Models.ApplicationStatuses.Registered,
                ApplicationTypeID = 1,                
            },

            new Applications.Models.Application()
            {
                ApplicationID = 101,
                ServiceInstanceID = 101,
                ApplicationIdentifier = "20181109/778",
                Status = REAU.Applications.Models.ApplicationStatuses.WaitingPayment,
                ApplicationTypeID = 1,
            },        
        };

        public IEnumerable<ServiceInstanceAction> ServiceActions = new List<ServiceInstanceAction>()
        {
            new ServiceInstanceAction()
            {
                OperationID = Guid.NewGuid(),
                ApplicationID = 1000,
                ServiceInstanceID = 1000,
                ActionTypeID = ServicеActionTypes.ApplicationAcceptance,
            }
        };
    }
}
<file_sep>/PropertyRegister.REAU/Applications/ApplicationInfoResolver.cs
﻿using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace PropertyRegister.REAU.Applications
{
    public interface IApplicationInfoResolver
    {
        ApplicationXmlInfo GetApplicationInfoFromXml(Stream xml);
    }

    public class ApplicationXmlInfo
    {
        public string XmlNamespace { get; set; }
        public string MainApplicationNumber { get; set; }
        public decimal? PaymentObligationAmount { get; set; }
        public IEnumerable<string> AttachedDocumentIDs { get; set; }
    }

    public class ApplicationInfoResolver : IApplicationInfoResolver
    {
        private const string _docNamespacePrefix = "__docns";

        public ApplicationXmlInfo GetApplicationInfoFromXml(Stream xml)
        {
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true;
            doc.Load(xml);

            XmlNamespaceManager nsmgr = BuildDocumentNamespaceManager(doc.NameTable, doc.DocumentElement.NamespaceURI);

            string mainAppNumber = doc.SelectSingleNode($"//{_docNamespacePrefix}:MainApplicationNumber", nsmgr)?.InnerText;

            var attDocsNodes = doc.SelectNodes("//att0:AttachedDocument/att1:DocumentUniqueId", nsmgr);

            List<string> docIdentifiers = new List<string>();
            foreach (XmlNode attDoc in attDocsNodes)
            {
                docIdentifiers.Add(attDoc.InnerText);
            }

            return new ApplicationXmlInfo()
            {
                XmlNamespace = doc.DocumentElement.NamespaceURI,
                MainApplicationNumber = mainAppNumber,
                AttachedDocumentIDs = docIdentifiers
            };
        }

        private XmlNamespaceManager BuildDocumentNamespaceManager(XmlNameTable xmlNameTable, string documentNamespace)
        {
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlNameTable);
            nsmgr.AddNamespace(_docNamespacePrefix, documentNamespace);
            nsmgr.AddNamespace("att0", "AttachedDocuments");
            nsmgr.AddNamespace("att1", "AttachedDocument");

            return nsmgr;
        }
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/Controllers/DocumentsController.cs
﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using PropertyRegister.REAU.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Web.Api.Controllers
{
    public class DocumentsController : BaseApiController
    {
        private readonly IDocumentService DocumentService;

        public DocumentsController(IDocumentService documentService)
        {
            DocumentService = documentService;
        }

        [HttpPost]
        public async Task<IActionResult> CreateDocument(IFormFile file)
        {
            var documentResult = await DocumentService.SaveDocumentAsync(new DocumentCreateRequest()
            {
                ContentType = file.ContentType,
                Filename = file.FileName,
                Content = file.OpenReadStream()
            });

            return Ok(new {
                docIdentifier = documentResult.DocumentIdentifier
            });
        }

        [Route("{documentIdentifier}")]
        [HttpDelete]
        public async Task<IActionResult> DeleteDocument(string documentIdentifier)
        {
            await DocumentService.DeleteDocumentAsync(documentIdentifier);

            return Ok();
        }

        [Route("{documentIdentifier}")]
        [HttpGet]
        public async Task<IActionResult> GetDocument(string documentIdentifier)
        {
            var docs = await DocumentService.GetDocumentsAsync(new List<string>() { documentIdentifier }, true);
            var doc = docs.SingleOrDefault();

            if (doc != null)
            {
                return File(fileStream: doc.Content, contentType: doc.ContentType, fileDownloadName: doc.Filename);
            }
            else
            {
                return NotFound();
            }
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Models/ServicePayment.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Models
{
    /// <summary>
    /// статуси на наредено плащане в Модул плащания
    /// </summary>
    public enum PaymentStatuses
    {
        /// Наредено плащане
        Ordered = 1,

        /// Неуспешно обработено
        Error = 2,

        // Отказано
        Cancelled = 3
    }

    /// <summary>
    /// наредено плащане в Модул плащания от РЕАУ по заявление по услуга 
    /// ОЩЕ НЕ Е УТОЧНЕН ТОЗИ МОДЕЛ!!!
    /// </summary>
    public class ServicePayment
    {
        public long? PaymentRequestID { get; set; }

        public long? ServiceInstanceID { get; set; }

        public string PaymentIdentifier { get; set; } // PaymentNumber?

        public PaymentStatuses? Status { get; set; } // ?

        public decimal ObligationAmount { get; set; }

        public decimal ServiceFee { get; set; }

        public DateTime? PaymentDeadline { get; set; }

        public string ErrorDescription { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/ApplicationAcceptanceService.cs
﻿using PropertyRegister.REAU.Applications.Models;
using PropertyRegister.REAU.Applications.Persistence;
using PropertyRegister.REAU.Applications.Results;
using PropertyRegister.REAU.Common;
using PropertyRegister.REAU.Nomenclatures;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications
{
    public interface IApplicationAcceptanceService
    {
        Task<ApplicationAcceptedResult> AcceptApplicationAsync(string portalOperationID, Stream xml);
    }

    public class ApplicationAcceptanceService : IApplicationAcceptanceService
    {
        private readonly INomenclaturesProvider NomenclaturesProvider;
        private readonly IDocumentService DocumentService;
        private readonly IIdempotentOperationExecutor IdempotentOperationExecutor;
        private readonly IActionDispatcher ActionDispatcher;
        private readonly IApplicationInfoResolver ApplicationInfoResolver;

        private readonly IApplicationRepository ApplicationRepository;
        private readonly IApplicationDocumentRepository ApplicationDocumentRepository;
        private readonly IServiceActionRepository ServiceActionRepository;
        private readonly IServiceInstanceRepository ServiceInstanceRepository;

        public ApplicationAcceptanceService(
            IApplicationRepository applicationRepository,
            IServiceInstanceRepository serviceInstanceRepository,
            IApplicationDocumentRepository applicationDocumentRepository,
            IServiceActionRepository serviceActionRepository,

            IDocumentService documentService,
            INomenclaturesProvider nomenclaturesProvider,
            IIdempotentOperationExecutor idempotentOperationExecutor,
            IActionDispatcher actionDispatcher,
            IApplicationInfoResolver applicationInfoResolver)
        {
            ApplicationRepository = applicationRepository;
            ServiceInstanceRepository = serviceInstanceRepository;
            ApplicationDocumentRepository = applicationDocumentRepository;
            ServiceActionRepository = serviceActionRepository;

            DocumentService = documentService;
            NomenclaturesProvider = nomenclaturesProvider;
            IdempotentOperationExecutor = idempotentOperationExecutor;
            ActionDispatcher = actionDispatcher;
            ApplicationInfoResolver = applicationInfoResolver;
        }

        public async Task<ApplicationAcceptedResult> AcceptApplicationAsync(string portalOperationID, Stream xml)
        {
            var operationResult = await IdempotentOperationExecutor.ExecuteAsync(portalOperationID, Common.Models.ServiceOperationTypes.AcceptServiceApplication,
                async (oid) =>
                {
                    var application = await InitialSaveApplication(xml);

                    var action = new ServiceAction()
                    {
                        OperationID = oid,
                        ServiceInstanceID = application.ServiceInstanceID,
                        ApplicationID = application.ApplicationID.Value,
                        ApplicationStatus = application.Status.Value,
                        ActionTypeID = ServicеActionTypes.ApplicationAcceptance
                    };
                    ServiceActionRepository.Create(action);

                    var result = new Results.ApplicationAcceptedResult()
                    {
                        ApplicationID = application.ApplicationID.Value,
                        ApplicationNumber = application.ApplicationIdentifier,
                        ApplicationStatus = application.Status,
                        RegistrationTime = application.RegistrationTime
                    };

                    return result;
                },
                async (r) =>
                {
                    await ActionDispatcher.SendAsync(r);
                });

            return operationResult;
        }

        private async Task<Application> InitialSaveApplication(Stream xml)
        {
            var appInfo = ApplicationInfoResolver.GetApplicationInfoFromXml(xml);

            var serviceType = NomenclaturesProvider.GetApplicationServiceTypes().SingleOrDefault(t => t.XmlNamespace == appInfo.XmlNamespace);

            if (serviceType == null)
                throw new InvalidOperationException("Unknown application type namespace from xml!");

            long? serviceInstanceID = null;
            long? mainApplicationID = null;

            if (string.IsNullOrEmpty(appInfo.MainApplicationNumber))
            {
                var serviceInstance = new ServiceInstance()
                {
                    OfficeID = 1,
                    ApplicantCIN = "100"
                };

                ServiceInstanceRepository.Create(serviceInstance);
                serviceInstanceID = serviceInstance.ServiceInstanceID;
            }
            else
            {
                var mainApp = ApplicationRepository.Search(new ApplicationSearchCriteria() { ApplicationIdentifier = appInfo.MainApplicationNumber }).SingleOrDefault();
                if (mainApp == null)
                    throw new InvalidOperationException("Cannot find initial main application!");

                serviceInstanceID = mainApp.ServiceInstanceID;
                mainApplicationID = mainApp.ApplicationID;
            }

            var application = new Models.Application()
            {
                ServiceInstanceID = serviceInstanceID.Value,
                ApplicationTypeID = serviceType.ApplicationTypeID,
                MainApplicationID = mainApplicationID,
                RegistrationTime = DateTime.UtcNow,
                IsReport = serviceType.IsReport,
                Status = ApplicationStatuses.Accepted
            };

            ApplicationRepository.Create(application);

            await SaveApplicationDocumentsAsync(application.ApplicationID.Value, appInfo, xml);

            return application;
        }

        private async Task SaveApplicationDocumentsAsync(long applicationID, ApplicationXmlInfo applInfo, Stream xml)
        {
            var packageDocumentData = await DocumentService.SaveDocumentAsync(new DocumentCreateRequest()
            {
                Filename = "ApplicationXml",
                ContentType = "application/xml",
                Content = xml
            });

            var packageDocument = new ApplicationDocument()
            {
                ApplicationID = applicationID,
                DocumentType = 1, // main package
                DocumentID = packageDocumentData.DocumentDataID
            };

            List<ApplicationDocument> applicationDocuments = new List<ApplicationDocument>();

            applicationDocuments.Add(packageDocument);

            if (applInfo.AttachedDocumentIDs != null && applInfo.AttachedDocumentIDs.Any())
            {
                var attachedDocs = await DocumentService.GetDocumentsAsync(applInfo.AttachedDocumentIDs.ToList());

                applicationDocuments.AddRange(attachedDocs.Select(d => new ApplicationDocument()
                {
                    ApplicationID = applicationID,
                    DocumentID = d.DocID.Value,
                    DocumentType = 2 // attachment
                }));
            }

            foreach (var applDoc in applicationDocuments)
            {
                ApplicationDocumentRepository.Create(applDoc);
            }
        }
    }
}
<file_sep>/PropertyRegister.REAU/Persistence/BaseDataContext.cs
﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Persistence
{
    public class BaseDataContext : IDisposable
    {
        public BaseDataContext(DbConnection dbConnection)
        {
            DbConnection = dbConnection;
        }

        public DbConnection DbConnection
        {
            get;
            protected set;
        }

        public void Dispose()
        {
        }
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/Controllers/BaseApiController.cs
﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;

namespace PropertyRegister.REAU.Web.Api.Controllers
{
    [Route("api/[controller]")]
    public class BaseApiController : Controller
    {
        protected string RequestOperationID =>
            HttpContext.Request.Headers["Operation-Id"].SingleOrDefault();
    }

    public class RequiredOperationHeaderAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var operationHeader = context.HttpContext.Request.Headers["Operation-Id"].SingleOrDefault();

            if (string.IsNullOrEmpty(operationHeader))
            {
                context.Result = new BadRequestResult();
            }
        }
    }
}
<file_sep>/PropertyRegister.REAU/Payments/PaymentManager.cs
﻿using PropertyRegister.REAU.Applications.Models;
using PropertyRegister.REAU.Domain;
using PropertyRegister.REAU.Integration;
using PropertyRegister.REAU.Nomenclatures;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Payments
{
    public interface IPaymentManager
    {
        Task<ServicePayment> RequestApplicationPaymentAsync(Application application, decimal? filedAmount);
    }

    public class PaymentManager : IPaymentManager
    {
        private readonly IPaymentIntegrationClient PaymentIntegrationClient;
        private readonly INomenclaturesProvider NomenclaturesProvider;

        public PaymentManager(IPaymentIntegrationClient paymentIntegrationClient, INomenclaturesProvider nomenclaturesProvider)
        {
            PaymentIntegrationClient = paymentIntegrationClient;
            NomenclaturesProvider = nomenclaturesProvider;
        }

        public async Task<ServicePayment> RequestApplicationPaymentAsync(Application application, decimal? filedAmount)
        {
            decimal? serviceFee, obligationAmount;
            var serviceType = NomenclaturesProvider.GetApplicationServiceTypes().Single(t => t.ApplicationTypeID == application.ApplicationTypeID);
            
            if (serviceType.IsFree)
            {
                serviceFee = obligationAmount = 0m;
            }
            else
            {
                if (serviceType.PaymentAmount.HasValue)
                {
                    serviceFee = obligationAmount = serviceType.PaymentAmount.Value;
                }
                else
                {
                    serviceFee = 0m;
                    obligationAmount = filedAmount.Value;
                }
            }

            var paymentResponse = serviceType.PaymentAfterRegistration ?
                await PaymentIntegrationClient.InitiateServiceApplicationAsync(new ServiceRequest { ServiceFee = serviceFee.Value }) :
                await PaymentIntegrationClient.InitiatePaymentAsync(new PaymentRequest()
                {
                    PaymentFor = application.ApplicationIdentifier,
                    ServiceFee = serviceFee.Value,
                    ObligationAmount = obligationAmount.Value
                });

            var applPayment = new ServicePayment()
            {
                ServiceInstanceID = application.ApplicationID,
                ServiceFee = serviceFee.Value,
                ObligationAmount = obligationAmount.Value
            };

            if (paymentResponse.Successfull)
            {
                applPayment.Status = PaymentStatuses.Ordered;
                applPayment.PaymentIdentifier = paymentResponse.PaymentIdentifier;

                if (serviceType.PaymentDeadline.HasValue)
                    applPayment.PaymentDeadline = DateTime.Now.Add(serviceType.PaymentDeadline.Value); // как се определя?
            }
            else
            {
                applPayment.Status = PaymentStatuses.Error;
                applPayment.ErrorDescription = paymentResponse.Errors;
            }

            return applPayment;
        }
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/Program.cs
﻿using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Serilog;
using Serilog.Core;
using Serilog.Events;

namespace PropertyRegister.REAU.Web.Api
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Log.Logger = CreateLogger();

            CreateWebHostBuilder(args)
                .Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .UseSerilog();

        public static Logger CreateLogger() =>
            new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .Enrich.FromLogContext()
                .Enrich.With<UserIdentityEnricher>()
                .WriteTo.File(
                    path: @"C:\tmp\EPZEU\PR.REAU.Web.Api\log.txt", 
                    outputTemplate: "---------------------------------------------------------------------- {NewLine} {Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{UserIdentity}] [{Level:u3}] {Message:lj}{NewLine}{Exception}",
                    rollingInterval: RollingInterval.Hour)
                .CreateLogger();
    }

    public class UserIdentityEnricher : ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
             => logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("UserIdentity", @"cnsys\vachev")); 
    }
}
<file_sep>/PropertyRegister.REAU/Common/Persistence/DocumentDataRepository.cs
﻿using CNSys.Data;
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
using PropertyRegister.REAU.Common.Models;
using PropertyRegister.REAU.Persistence;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;

namespace PropertyRegister.REAU.Common.Persistence
{
    public class DocumentDataSearchCriteria
    {
        public List<long> DocumentDataIDs { get; set; }
        public List<string> Identifiers { get; set; }
    }

    public interface IDocumentDataRepository : IRepository<DocumentData, Guid, DocumentDataSearchCriteria>
    {
        Stream ReadContent(string documentIdentifier);
    }

    public class DocumentDataRepository : RepositoryBase<DocumentData, Guid, DocumentDataSearchCriteria, CommonDataContext>, IDocumentDataRepository
    {
        public DocumentDataRepository()
            : base(false)
        {
        }

        protected override void CreateInternal(CommonDataContext context, DocumentData item)
        {
            item.Identifier = item.Identifier ?? Guid.NewGuid().ToString();
            
            using (var blobContent = new OracleBlob(context.DbConnection as OracleConnection))
            {
                item.Content.CopyTo(blobContent);

                context.DocumentDataCreate(item.Identifier, item.ContentType, item.Filename, item.IsTemporal, blobContent, out long documentId);
                item.DocID = documentId;
            }
        }

        protected override void UpdateInternal(CommonDataContext context, DocumentData item) =>
            context.DocumentDataUpdate(item.Identifier, item.ContentType, item.Filename, item.IsTemporal);

        protected override void DeleteInternal(CommonDataContext context, Guid key) =>
            context.DocumentDataDelete(key);
        
        protected override IEnumerable<DocumentData> SearchInternal(CommonDataContext context, DocumentDataSearchCriteria searchCriteria)
        {
            IEnumerable<DocumentData> res;

            using (var data = context.DocumentDataSearch(
                ToStringCollection(searchCriteria.DocumentDataIDs),
                ToStringCollection(searchCriteria.Identifiers), 1, 20, out int? count))
            {
                res = data.ReadToList<DocumentData>();
            }

            return res;
        }

        public Stream ReadContent(string documentIdentifier)
        {
            return new DeferredDbStream(() => {

                var dbContext = CreateDbContext();
                var dataCtx = CreateDataContext(dbContext);

                return new Tuple<DbContext, Stream>(dbContext, new WrappedBinaryStream(dataCtx.DocumentContentGet(documentIdentifier)));
            });
        }

        public static string ToStringCollection(IEnumerable items, char separator = ',')
        {
            if (items == null)
                return null;

            StringBuilder res = new StringBuilder();

            foreach (var item in items)
            {
                res.AppendFormat("{0}{1}", item, separator);
            }

            return
                res.Length > 0 ?
                res.ToString().Trim(separator) : null;
        }
    }   
}
<file_sep>/PropertyRegister.REAU/Applications/ApplicationReceivedMessage.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications
{
    /// <summary>
    /// Dummy - тест за rebus!
    /// </summary>
    public class ApplicationReceivedMessage
    {
        public ApplicationReceivedMessage(long id)
        {
            ID = id;
        }

        public long ID { get; private set; }
    }
}
<file_sep>/PropertyRegister.REAU/Persistence/DbContextHelper.cs
﻿using Dapper;
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Persistence
{
    public static class DbContextHelper
    {
        public static void SPExecute(this IDbConnection connection, string procName, SqlMapper.IDynamicParameters parameters = null)
        {
            connection.Execute(procName, parameters, commandType: CommandType.StoredProcedure);
        }

        public static object SPExecuteScalar(this IDbConnection connection, string procName, SqlMapper.IDynamicParameters parameters = null)
        {
            return connection.ExecuteScalar(string.Format("\"{0}\"", procName), parameters, commandType: CommandType.StoredProcedure);
        }

        public static CnsysGridReader SPExecuteReader(this DbConnection connection, string procName, SqlMapper.IDynamicParameters parameters)
        {
            var reader = connection.ExecuteReader(procName, parameters, commandType: CommandType.StoredProcedure);

            return new CnsysGridReader(reader);
        }

        public static IDataReader SPExecuteReader(this DbConnection connection, string procName, SqlMapper.IDynamicParameters parameters = null, CommandBehavior behavior = CommandBehavior.Default)
        {           
            CommandDefinition commandDefinition = new CommandDefinition(procName, parameters, null, null, CommandType.StoredProcedure, CommandFlags.None);

            var reader = connection.ExecuteReader(commandDefinition, behavior);

            return reader;
        }

        public static async Task<T> TransactionalOperationAsync<T>(Func<Task<T>> func)
        {
            T res = default(T);

            try
            {
                using (var dbContext = REAUDbContext.CreateDefaultSerializationContext())
                {
                    res = await func();

                    dbContext.Complete();
                }
            }
            catch (Exception ex)
            {
                // log ex
                throw;
            }

            return res;
        }

        public static async Task TransactionalOperationAsync(Func<Task> func)
        {
            try
            {
                using (var dbContext = REAUDbContext.CreateDefaultSerializationContext())
                {
                    await func();

                    dbContext.Complete();
                }
            }
            catch (Exception ex)
            {
                // log ex
                throw;
            }
        }
    }

    public class CnsysGridReader : IDisposable
    {
        private IDataReader _reader = null;
        private bool _isFirstCall = true;

        public CnsysGridReader(IDataReader reader)
        {
            _reader = reader;
        }

        public List<T> ReadToList<T>()
        {
            return Read<T>().ToList();
        }

        public IEnumerable<T> Read<T>()
        {
            if (!_isFirstCall)
            {
                _reader.NextResult();
            }
            else
            {
                _isFirstCall = false;
            }

            return _reader.Parse<T>();
        }

        public void Dispose()
        {
            if (_reader != null)
            {
                if (!_reader.IsClosed)
                    _reader.Close();

                _reader.Dispose();
            }
            _reader = null;
        }
    }

    public class OracleDynamicParameters : SqlMapper.IDynamicParameters
    {
        private readonly DynamicParameters dynamicParameters = new DynamicParameters();
        private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();

        public void Add(string name, OracleDbType oracleDbType, object value = null, ParameterDirection? direction = null, int? size = null)
        {
            ParameterDirection p_direction = direction ?? ParameterDirection.Input;

            var oracleParameter = size.HasValue ? new OracleParameter(name, oracleDbType, size.Value, value, p_direction) :
                new OracleParameter(name, oracleDbType, value, p_direction);

            oracleParameters.Add(oracleParameter);
        }

        public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
        {
            var oracleParameter = new OracleParameter(name, oracleDbType, direction);
            oracleParameters.Add(oracleParameter);
        }

        public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
        {
            ((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);

            var oracleCommand = command as OracleCommand;

            if (oracleCommand != null)
            {
                oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
            }
        }

        public T Get<T>(string name)
        {
            var paramInfo = oracleParameters.Single(p => p.ParameterName == name);
            object val = paramInfo.Value;

            if (val == DBNull.Value)
            {
                if (default(T) != null)
                {
                    throw new ApplicationException("Attempting to cast a DBNull to a non nullable type! Note that out/return parameters will not have updated values until the data stream completes (after the 'foreach' for Query(..., buffered: false), or after the GridReader has been disposed for QueryMultiple)");
                }
                return default(T);
            }
            return (T)val;
        }

        public int GetIntNumber(string name) => Get<OracleDecimal>(name).ToInt32();

        public long GetLongNumber(string name) => Get<OracleDecimal>(name).ToInt64();

        public bool GetBoolean(string name) => Get<OracleDecimal>(name).ToByte() == 1;

        public string GetString(string name) =>
            Get<OracleString>(name) != null ? Get<OracleString>(name).Value : null;
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/ServiceCollectionExtensions.cs
﻿using Microsoft.Extensions.Configuration;
using PropertyRegister.REAU.Applications.Results;
using Rebus.Config;
using Rebus.Routing.TypeBased;
using Rebus.ServiceProvider;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddRebusWithOracleTransport(this IServiceCollection services, IConfiguration configuration)
        {
             return services.AddRebus(rconfig => rconfig
                    .Transport(t => t.UseOracleAsOneWayClient(System.Configuration.ConfigurationManager.ConnectionStrings["defaultRWConnectionString"].ConnectionString, "mbus_messages"))
                    .Routing(r => r.TypeBased().Map<ApplicationAcceptedResult>("q_appl_processing")));
        }
    }
}
<file_sep>/PropertyRegister.REAU/Common/WrappedBinaryStream.cs
﻿using System;
using System.Data;
using System.IO;

namespace PropertyRegister.REAU.Common
{
    public class WrappedBinaryStream : Stream
    {
        private readonly IDataReader _reader;

        private int _columnIndex = 0;
        private long _position = 0;

        #region Construcotrs

        public WrappedBinaryStream(IDataReader reader)
        {
            _position = 0;
            _reader = reader;
            _reader.Read();
        }

        #endregion

        #region Overriden Methods

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override long Position
        {
            get { return _position; }
            set { _position = value; }
        }

        public override void Flush()
        {
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            switch (origin)
            {
                case SeekOrigin.Begin:
                    {
                        if ((offset < 0) && (offset > this.Length))
                            throw new ArgumentException("Invalid seek origin.");
                        _position = offset;
                        break;
                    }
                case SeekOrigin.End:
                    {
                        if ((offset > 0) && (offset < -this.Length))
                            throw new ArgumentException("Invalid seek origin.");
                        _position = this.Length - offset;
                        break;
                    }
                case SeekOrigin.Current:
                    {
                        if ((_position + offset > this.Length) || (_position + offset < 0))
                            throw new ArgumentException("Invalid seek origin.");
                        _position = _position + offset;
                        break;
                    }
                default:
                    {
                        throw new ArgumentOutOfRangeException("origin", origin, "Unknown SeekOrigin");
                    }
            }
            return _position;
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int _bytesRead = Convert.ToInt32(_reader.GetBytes(_columnIndex, _position, buffer, offset, count));
            _position += (long)_bytesRead;
            return (int)_bytesRead;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        protected override void Dispose(bool disposing)
        {
            if (_reader != null)
                _reader.Dispose();

            base.Dispose(disposing);
        }

        #endregion
    }
}
<file_sep>/ConsoleApp1/Program.cs
﻿using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Applications.Results;
using Rebus.Activation;
using Rebus.Config;
using Rebus.Handlers;
using Rebus.Retry.Simple;
using Rebus.Routing.TypeBased;
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            MainAsync().GetAwaiter().GetResult();
        }

        static async Task MainAsync()
        {
            string connString = "Data Source=pr_reau; User ID=pr_reau_dev; Password=<PASSWORD>;";

            using (var activator = new BuiltinHandlerActivator())
            {                
                //activator.Handle<ApplicationReceivedMessage>((message) =>
                //{
                //    Console.WriteLine($"Got message of {nameof(message)} with value {(char)message.ID}.");

                //    return Task.CompletedTask;
                //});

                activator.Register(() => new ApplicationAcceptedResultHandler1());

                var bus = Configure.With(activator)                    
                    //.Transport(t => t.UseMsmq("consumer.input"))
                    .Transport(t => t.UseOracle(connString, "mbus_messages", "error"))
                    .Routing(r => r.TypeBased()
                                        .Map<ApplicationReceivedMessage>("consumer.input")
                                        .Map<ApplicationAcceptedResult>("error"))

                    .Options(c => c.SimpleRetryStrategy(maxDeliveryAttempts: 3, errorQueueAddress: "q_error"))
                    .Start();

               //bus.Subscribe<ApplicationReceivedMessage>().Wait();

               Console.WriteLine("This is Console 1: Subscriber");
                Console.WriteLine("Press ENTER to quit");
                Console.ReadLine();
                Console.WriteLine("Quitting...");
            }
        }
    }

    public class ApplicationAcceptedResultHandler1 : IHandleMessages<ApplicationAcceptedResult>
    {        
        public Task Handle(ApplicationAcceptedResult message)
        {
            Console.WriteLine($"Handled message of {nameof(message)} with value {message.ApplicationNumber}.");

            return Task.CompletedTask;
        }
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/Test/NomeclaturesProviderDummy.cs
﻿using PropertyRegister.REAU.Domain;
using PropertyRegister.REAU.Nomenclatures;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Web.Api.Test
{
    public class NomenclaturesProviderDummy : INomenclaturesProvider
    {
        public ICollection<ApplicationServiceType> GetApplicationServiceTypes()
        {
            return new List<ApplicationServiceType>
            {
                new ApplicationServiceType() {
                    ApplicationTypeID = 1,
                    Title = "Услуга платена стандартна",
                    IsFree = false,
                    IsReport = false,
                    PaymentAfterRegistration = false,
                    PaymentAmount = 10m,
                    PaymentDeadline = new TimeSpan(3, 0, 0, 0),
                    XmlNamespace = "https://registryagency.bg/schema/v1/CertificateForPerson"
                },
                new ApplicationServiceType() {
                    ApplicationTypeID = 2,
                    Title = "Услуга безплатна стандартна",
                    IsFree = true,
                    IsReport = false,
                    PaymentAfterRegistration = false,
                    PaymentAmount = 0m,
                    XmlNamespace = "https://registryagency.bg/schema/v1/CertificateForPersonFree"
                },
                new ApplicationServiceType() {
                    ApplicationTypeID = 3,
                    Title = "Корекция",
                    IsFree = true,
                    IsReport = false,
                    PaymentAfterRegistration = false,
                    PaymentAmount = 0m,
                    XmlNamespace = "https://registryagency.bg/schema/v1/Correction"
                },
                new ApplicationServiceType() {
                    ApplicationTypeID = 4,
                    Title = "Справка безплатна стандартна",
                    IsFree = true,
                    IsReport = true,
                    PaymentAfterRegistration = false,
                    PaymentAmount = 10m,
                    PaymentDeadline = new TimeSpan(3, 0, 0, 0),
                    XmlNamespace = "https://registryagency.bg/schema/v1/Report1"
                },

                new ApplicationServiceType() {
                    ApplicationTypeID = 5,
                    Title = "Справка платена стандартна",
                    IsFree = false,
                    IsReport = true,
                    PaymentAfterRegistration = false,
                    PaymentAmount = 10m,
                    PaymentDeadline = new TimeSpan(3, 0, 0, 0),
                    XmlNamespace = "https://registryagency.bg/schema/v1/Report2"
                },

            };
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Persistence/ServiceActionRepository.cs
﻿using CNSys.Data;
using PropertyRegister.REAU.Applications.Models;
using System.Collections.Generic;

namespace PropertyRegister.REAU.Applications.Persistence
{
    public class ServiceActionSearchCriteria
    {
        public long? ServiceOperationID { get; internal set; }
        public string OperationID { get; set; }
        public ServicеActionTypes? ActionType { get; internal set; }
    }

    public interface IServiceActionRepository : IRepository<ServiceAction, long?, ServiceActionSearchCriteria>
    {
    }

    public class ServiceActionRepository : RepositoryBase<ServiceAction, long?, ServiceActionSearchCriteria, ApplicationProcessDataContext>, IServiceActionRepository
    {
        public ServiceActionRepository() : base(false)
        {
        }

        protected override void CreateInternal(ApplicationProcessDataContext context, ServiceAction item)
        {
            context.ServiceActionCreate(item.OperationID, item.ServiceInstanceID, item.ApplicationID, (int)item.ApplicationStatus, (int)item.ActionTypeID.Value, out long serviceActionID);
            item.ServiceActionID = serviceActionID;
        }

        protected override IEnumerable<ServiceAction> SearchInternal(ApplicationProcessDataContext context, ServiceActionSearchCriteria searchCriteria)
        {
            return base.SearchInternal(context, searchCriteria);
        }
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/Controllers/ApplicationsController.cs
﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using PropertyRegister.REAU.Applications;
using System;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Web.Api.Controllers
{
    public class ApplicationsController : BaseApiController
    {
        private readonly IApplicationAcceptanceService ApplicationService;

        public ApplicationsController(IApplicationAcceptanceService applicationService)
        {
            ApplicationService = applicationService;
        }

        [HttpGet]
        //[RequiredOperationHeader]
        public IActionResult Get([FromServices] ILogger<int> logger)
        {
            //int id = 55;
            //logger.LogError("test", id);
            //logger.LogCritical("some critical event with id: {id}", id);

            return Ok(new { id = 5, oid = RequestOperationID });
        }

        [HttpPost]
        [RequiredOperationHeader]
        public async Task<IActionResult> AcceptApplication(IFormFile file)
        {
            var result = await ApplicationService.AcceptApplicationAsync(RequestOperationID, file.OpenReadStream());

            return Ok(result);
        }
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/PrincipalMiddleware.cs
﻿using CNSys.Security;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Web.Api
{
    public class PrincipalMiddleware
    {
        private readonly RequestDelegate _next;
        private const string TestUserClientID = "1";

        public PrincipalMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            var user = httpContext.User;
            var testPrincipal = new TestPrincipal(user, TestUserClientID);

            httpContext.User = testPrincipal;
            System.Threading.Thread.CurrentPrincipal = testPrincipal;

            await _next(httpContext);
        }
    }

    public static class PrincipalMiddlewareExtensions
    {
        public static IApplicationBuilder UsePrincipalMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<PrincipalMiddleware>();
        }
    }

    public class TestPrincipal : ClaimsPrincipal, IDataSourceUser
    {
        public TestPrincipal(IPrincipal principal, string clientID) : base(principal)
            => ClientID = clientID;        

        public string ClientID { get; }

        public string ProxyUserID => throw new NotImplementedException();
    }
}
<file_sep>/PropertyRegister.REAU/Common/Persistence/CommonDataContext.cs
﻿using Dapper;
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
using PropertyRegister.REAU.Common.Models;
using PropertyRegister.REAU.Persistence;
using System;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;

namespace PropertyRegister.REAU.Common.Persistence
{
    public class CommonDataContext : BaseDataContext
    {
        public CommonDataContext(DbConnection dbConnection) : base(dbConnection)
        {
            SqlMapper.SetTypeMap(typeof(ServiceOperation), DataContextHelper.ColumnMap<ServiceOperation>());
            SqlMapper.SetTypeMap(typeof(DocumentData), DataContextHelper.ColumnMap<DocumentData>());
        }

        public void ServiceOperationCreate(string operationID, int serviceOperationTypeID,
            out long serviceOperationID, out bool isCompleted, out string result)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_OperationID", OracleDbType.Varchar2, operationID);
            parameters.Add("p_OperationTypeID", OracleDbType.Int32, serviceOperationTypeID);
            parameters.Add("p_ServiceOperationID_out", OracleDbType.Int32, null, System.Data.ParameterDirection.Output);
            parameters.Add("p_IsCompleted_out", OracleDbType.Int32, null, System.Data.ParameterDirection.Output);
            parameters.Add("p_Result_out", OracleDbType.Varchar2, null, System.Data.ParameterDirection.Output, 2000);

            DbConnection.SPExecute("pkg_velin.p_Service_Operation_Create", parameters);

            serviceOperationID = parameters.GetLongNumber("p_ServiceOperationID_out");
            isCompleted = parameters.GetBoolean("p_IsCompleted_out");
            result = parameters.GetString("p_Result_out");
        }

        public void ServiceOperationUpdate(long serviceOperationID, string operationID, bool isCompleted, string result)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_ServiceOperationID", OracleDbType.Int64, serviceOperationID);
            parameters.Add("p_OperationID", OracleDbType.Varchar2, operationID);
            parameters.Add("p_IsCompleted", OracleDbType.Int32, isCompleted);
            parameters.Add("p_Result", OracleDbType.Varchar2, result);

            DbConnection.SPExecute("pkg_velin.p_Service_Operation_Update", parameters);
        }

        public CnsysGridReader ServiceOperationSearch(long? serviceOperationID, string operationID, int? operationTypeID)
        {
            var parameters = new OracleDynamicParameters();

            parameters.Add("p_ServiceOperationID", OracleDbType.Int64, serviceOperationID);
            parameters.Add("p_OperationID", OracleDbType.Varchar2, operationID);
            parameters.Add("p_OperationTypeID", OracleDbType.Int32, operationTypeID);
            parameters.Add("c_Data", OracleDbType.RefCursor, ParameterDirection.Output);

            return DbConnection.SPExecuteReader("PKG_VELIN.p_Service_Operation_Search", parameters);
        }

        #region documents

        public void DocumentDataCreate(string docIdentifier, string contentType, string filename, bool isTemporal, OracleBlob content, out long documentId)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Guid", OracleDbType.Varchar2, docIdentifier);
            parameters.Add("p_ContentType", OracleDbType.Varchar2, contentType);
            parameters.Add("p_Filename", OracleDbType.Varchar2, filename);
            parameters.Add("p_IsTemp", OracleDbType.Int32, isTemporal);
            parameters.Add("p_Content", OracleDbType.Blob, content);
            parameters.Add("p_DocumentId_out", OracleDbType.Int32, null, System.Data.ParameterDirection.Output);

            DbConnection.SPExecute("pkg_velin.p_Document_Data_Create", parameters);

            documentId = parameters.GetLongNumber("p_DocumentId_out");
        }

        public void DocumentDataUpdate(string docIdentifier, string contentType, string filename, bool isTemporal)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Guid", OracleDbType.Varchar2, docIdentifier);
            parameters.Add("p_ContentType", OracleDbType.Varchar2, contentType);
            parameters.Add("p_Filename", OracleDbType.Varchar2, filename);
            parameters.Add("p_IsTemp", OracleDbType.Int32, isTemporal);

            DbConnection.SPExecute("pkg_velin.p_Document_Data_Update", parameters);
        }

        public void DocumentDataDelete(Guid key)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Guid", OracleDbType.Varchar2, key);

            DbConnection.SPExecute("pkg_velin.p_Document_Data_Delete", parameters);
        }

        public CnsysGridReader DocumentDataSearch(string documentIDs, string documentIdentifiers, int startIndex, int pageSize, out int? count)
        {
            var parameters = new OracleDynamicParameters();

            parameters.Add("p_Document_Ids", OracleDbType.Varchar2, documentIDs);
            parameters.Add("p_Guid_Ids", OracleDbType.Varchar2, documentIdentifiers);
            parameters.Add("p_StartIndex", OracleDbType.Int32, startIndex);
            parameters.Add("p_PageSize", OracleDbType.Int32, pageSize);
            parameters.Add("p_ResultsCount_out", OracleDbType.Int32, null, System.Data.ParameterDirection.Output);
            parameters.Add("p_Data", OracleDbType.RefCursor, ParameterDirection.Output);

            var reader = DbConnection.SPExecuteReader("PKG_VELIN.p_Document_Data_Search", parameters);
            count = parameters.GetIntNumber("p_ResultsCount_out");

            return reader;
        }

        public IDataReader DocumentContentGet(string docIdentifier)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Guid", OracleDbType.Varchar2, docIdentifier);
            parameters.Add("c_Data", OracleDbType.RefCursor, ParameterDirection.Output);

            return DbConnection.SPExecuteReader("PKG_VELIN.p_Document_Data_GetContent", parameters, CommandBehavior.SequentialAccess);
        }

        #endregion
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Models/ServiceInstance.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Models
{
    /// <summary>
    /// Заявена услуга
    /// </summary>
    public class ServiceInstance
    {
        public long? ServiceInstanceID { get; set; }

        /// <summary>
        /// Идентификатор на служба по вписвания
        /// </summary>
        public int OfficeID { get; set; }

        /// <summary>
        /// идентификатор на потребителския профил на заявителя в ЕПЗЕУ
        /// </summary>
        public string ApplicantCIN { get; set; }


    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/PaymentIntegrationClientMock.cs
﻿using PropertyRegister.REAU.Integration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class PaymentIntegrationClientMock : IPaymentIntegrationClient
    {
        public Task<PaymentResponse> InitiatePaymentAsync(PaymentRequest paymentRequest)
        {
            return Task.FromResult(new PaymentResponse()
            {
                PaymentIdentifier = $"PMT_{Guid.NewGuid()}",
                Successfull = true
            });
        }

        public Task<PaymentResponse> InitiateServiceApplicationAsync(ServiceRequest serviceRequest)
        {
            return Task.FromResult(new PaymentResponse()
            {
                PaymentIdentifier = $"PMT_{Guid.NewGuid()}",
                Successfull = true
            });
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/ApplicationService.cs
﻿using PropertyRegister.REAU.Applications.Models;
using PropertyRegister.REAU.Applications.Persistence;
using PropertyRegister.REAU.Applications.Results;
using PropertyRegister.REAU.Common;
using PropertyRegister.REAU.Integration;
using PropertyRegister.REAU.Nomenclatures;
using PropertyRegister.REAU.Payments;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications
{    
    public interface IApplicationProcessingService
    {
        Task<ApplicationProcessedResult> ProcessApplicationAsync(long applicationID);
    }

    public class ApplicationService : IApplicationProcessingService
    {
        private readonly INomenclaturesProvider NomenclaturesProvider;
        private readonly IIdempotentOperationExecutor IdempotentOperationExecutor;
        private readonly IActionDispatcher ActionDispatcher;
        private readonly IPaymentManager PaymentManager;
        private readonly IPropertyRegisterClient PropertyRegisterClient;
        private readonly IApplicationRepository ApplicationRepository;
        private readonly IServiceActionRepository ServiceActionRepository;

        public ApplicationService(
            IApplicationRepository applicationRepository,
            IServiceActionRepository serviceActionRepository,            
            IPropertyRegisterClient propertyRegisterClient,
            INomenclaturesProvider nomenclaturesProvider,
            IIdempotentOperationExecutor idempotentOperationExecutor,
            IActionDispatcher actionDispatcher,
            IPaymentManager paymentManager)
        {
            ApplicationRepository = applicationRepository;
            ServiceActionRepository = serviceActionRepository;
            PropertyRegisterClient = propertyRegisterClient;
            NomenclaturesProvider = nomenclaturesProvider;
            IdempotentOperationExecutor = idempotentOperationExecutor;            
            ActionDispatcher = actionDispatcher;
            PaymentManager = paymentManager;
        }
        
        public async Task<ApplicationProcessedResult> ProcessApplicationAsync(long applicationID)
        {
            var result = await IdempotentOperationExecutor.ExecuteAsync(applicationID.ToString(), Common.Models.ServiceOperationTypes.ProcessServiceApplication, (oid) =>
            {
                var application = ApplicationRepository.Search(new ApplicationSearchCriteria() { ApplicationIDs = new List<long> { applicationID } }).SingleOrDefault();

                if (application == null && application.Status != ApplicationStatuses.Accepted)
                    throw new InvalidOperationException();

                // decide which api to call

                if (application.IsMainApplication)
                {
                    return RequestApplicationPaymentAsync(oid, application);
                }
                else
                {
                    return SendCorrectionApplicationToPRAsync(oid, application);
                }
            }, async (operationResult) => {

                // when application is free should be prepared to be sent to PR immediatelly
                if (operationResult.ApplicationStatus == ApplicationStatuses.WaitingRegistration ||
                     operationResult.ApplicationStatus == ApplicationStatuses.InProgress)
                {
                    await ActionDispatcher.SendAsync(operationResult);
                }
            });
            
            return result;
        }

        public async Task ProcessServicePaymentChangeAsync(string paymentIdentifier, object paymentData)
        {
            var operationResult = await IdempotentOperationExecutor.ExecuteAsync(paymentIdentifier, Common.Models.ServiceOperationTypes.ProcessServicePayment, (oid) =>
            {
                var application = ApplicationRepository
                    .Search(new ApplicationSearchCriteria() { PaymentIdentifier = paymentIdentifier }).SingleOrDefault();

                if (application == null || application.Status != ApplicationStatuses.WaitingPayment)
                    throw new InvalidOperationException();

                var serviceType = NomenclaturesProvider.GetApplicationServiceTypes().Single(t => t.ApplicationTypeID == application.ApplicationTypeID);

                application.Status = serviceType.IsReport ? ApplicationStatuses.InProgress : ApplicationStatuses.WaitingRegistration;

                ApplicationRepository.Update(application);

                var action = new ServiceAction()
                {
                    OperationID = oid,
                    ServiceInstanceID = application.ServiceInstanceID,
                    ApplicationID = application.ApplicationID.Value,
                    ApplicationStatus = application.Status.Value,
                    ActionTypeID = ServicеActionTypes.PaymentChange,
                };
                ServiceActionRepository.Create(action);

                return Task.FromResult(new Results.PaymentProcessedResult()
                {
                    ApplicationID = application.ApplicationID.Value,
                    ApplicationStatus = application.Status.Value
                });
            });

            await ActionDispatcher.SendAsync(operationResult);
        }
        
        //////
        ////// API CALLS
        //////

        private async Task<ApplicationProcessedResult> SendCorrectionApplicationToPRAsync(long operationID, Models.Application application)
        {
            var prResponse = await PropertyRegisterClient.RegisterServiceApplicationAsync(null);

            // TODO 
            // ANALYZE prResponse

            application.Status = ApplicationStatuses.Completed;
            ApplicationRepository.Update(application);

            var action = new ServiceAction()
            {
                OperationID = operationID,
                ServiceInstanceID = application.ServiceInstanceID,
                ApplicationID = application.ApplicationID.Value,
                ApplicationStatus = application.Status.Value,
                ActionTypeID = ServicеActionTypes.ApplicationRegistrationInPR,
            };
            ServiceActionRepository.Create(action);

            return new Results.ApplicationProcessedResult()
            {
                ApplicationID = application.ApplicationID.Value
            };
        }

        private async Task<ApplicationProcessedResult> RequestApplicationPaymentAsync(long operationID, Models.Application application)
        {
            var servicePayment = await PaymentManager.RequestApplicationPaymentAsync(application, filedAmount: null /*from xml*/);

            if (servicePayment.Status != PaymentStatuses.Ordered)
                throw new Exception(servicePayment.ErrorDescription);

            ApplicationRepository.CreateServicePayment(servicePayment);

            var serviceType = NomenclaturesProvider.GetApplicationServiceTypes().Single(t => t.ApplicationTypeID == application.ApplicationTypeID);

            application.Status = serviceType.IsFree ?
                    (serviceType.IsReport ? ApplicationStatuses.InProgress : ApplicationStatuses.WaitingRegistration) : ApplicationStatuses.WaitingPayment;

            ApplicationRepository.Update(application);

            var action = new ServiceAction()
            {
                OperationID = operationID,
                ServiceInstanceID = application.ServiceInstanceID,
                ApplicationID = application.ApplicationID.Value,
                ApplicationStatus = application.Status.Value,
                ActionTypeID = ServicеActionTypes.RequestedPayment,
            };
            ServiceActionRepository.Create(action);
            
            return new Results.ApplicationProcessedResult
            {
                ApplicationStatus = application.Status,
                PaymentAmount = servicePayment.ObligationAmount,
                PaymentDeadline = servicePayment.PaymentDeadline,
                PaymentIdentifier = servicePayment.PaymentIdentifier
            };
        }        
    }
}
<file_sep>/PropertyRegister.REAU/Common/Models/ServiceOperation.cs
﻿using PropertyRegister.REAU.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Common.Models
{
    public enum ServiceOperationTypes
    {
        AcceptServiceApplication = 1,
        ProcessServiceApplication = 2,
        ProcessServicePayment = 3
    }

    public class ServiceOperation
    {
        [DapperColumn("service_operation_id")]
        public long? ServiceOperationID { get; set; }

        [DapperColumn("operation_id")]
        public string OperationID { get; set; }

        [DapperColumn("operation_type")]
        public ServiceOperationTypes ServiceOperationType { get; set; }

        [DapperColumn("is_completed")]
        public bool IsCompleted { get; set; }

        [DapperColumn("result")]
        public string Result { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/PropertyRegisterClientMock.cs
﻿using PropertyRegister.REAU.Integration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class PropertyRegisterClientMock : IPropertyRegisterClient
    {
        public Task<PropertyRegisterApplicationResult> ExecuteReportApplicationAsync(object request)
        {
            return Task.FromResult(new PropertyRegisterApplicationResult()
            {
                DocumentIdentifier = $"PR_REP_{DateTime.Now.ToString("yyyymmdd")}"
            });
        }

        public Task<ServiceApplicationResult> RegisterServiceApplicationAsync(object request)
        {
            return Task.FromResult(new ServiceApplicationResult()
            {
                RegisterStatusID = 1,
                DocumentIdentifier = $"PR_{DateTime.Now.ToString("yyyymmdd")}_number"
            });
        }
    }
}
<file_sep>/PropertyRegister.REAU/Integration/PaymentIntegrationClient.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Integration
{
    public interface IPaymentIntegrationClient
    {
        Task<PaymentResponse> InitiatePaymentAsync(PaymentRequest paymentRequest);
        Task<PaymentResponse> InitiateServiceApplicationAsync(ServiceRequest serviceRequest);
    }

    internal class PaymentIntegrationClient : IPaymentIntegrationClient
    {
        public Task<PaymentResponse> InitiatePaymentAsync(PaymentRequest paymentRequest)
        {
            return Task.FromResult(new PaymentResponse() {
                PaymentIdentifier = paymentRequest.PaymentFor
            });
        }

        public Task<PaymentResponse> InitiateServiceApplicationAsync(ServiceRequest serviceRequest)
        {
            return Task.FromResult(new PaymentResponse());
        }
    }

    public class PaymentRequest
    {
        public string PaymentFor { get; set; }

        /// <summary>
        /// размера на задължението
        /// </summary>
        public decimal ObligationAmount { get; set; }

        /// <summary>
        /// стойността на услугата
        /// </summary>
        public decimal ServiceFee { get; set; }
    }

    public class ServiceRequest
    {
        /// <summary>
        /// стойността на услугата
        /// </summary>
        public decimal ServiceFee { get; set; }
    }

    public class PaymentResponse
    {
        public string PaymentIdentifier { get; set; }
        public bool Successfull { get; set; }
        public string Errors { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Integration/PropertyRegisterClient.cs
﻿using System.IO;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Integration
{
    public class PropertyRegisterApplicationResult
    {
        public string DocumentIdentifier { get; set; }
        public Stream DocumentContent { get; set; }
    }

    public class ServiceApplicationResult : PropertyRegisterApplicationResult
    {
        public int RegisterStatusID { get; set; }
    }

    public interface IPropertyRegisterClient
    {
        Task<ServiceApplicationResult> RegisterServiceApplicationAsync(object request);

        Task<PropertyRegisterApplicationResult> ExecuteReportApplicationAsync(object request);
    }

    public class PropertyRegisterClient : IPropertyRegisterClient
    {
        public Task<PropertyRegisterApplicationResult> ExecuteReportApplicationAsync(object request)
        {
            return Task.FromResult(new PropertyRegisterApplicationResult());
        }

        public Task<ServiceApplicationResult> RegisterServiceApplicationAsync(object request)
        {
            return Task.FromResult(new ServiceApplicationResult());
        }
    }
}
<file_sep>/PropertyRegister.REAU.Test/ApplicationTests.cs
﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Applications.Persistence;
using PropertyRegister.REAU.Applications.Results;
using PropertyRegister.REAU.Common;
using PropertyRegister.REAU.Common.Persistence;
using PropertyRegister.REAU.Domain;
using PropertyRegister.REAU.Integration;
using PropertyRegister.REAU.Test.Mocks;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;

namespace PropertyRegister.REAU.Test
{
    [TestClass]
    public class ApplicationTests
    {
        private ServiceProvider ServiceProvider;
        private ServiceCollection Services;

        public ApplicationTests()
        {
            var services = new ServiceCollection();
            services.AddTransient<ApplicationService>();
            services.AddTransient<IApplicationRepository, ApplicationEntityMock>();
            services.AddTransient<IDocumentService, DocumentServiceMock>();
            services.AddTransient<IPropertyRegisterClient, PropertyRegisterClientMock>();
            services.AddTransient<IPaymentIntegrationClient, PaymentIntegrationClientMock>();
            services.AddSingleton<DbDataSets>();

            Services = services;
            ServiceProvider = services.BuildServiceProvider();
        }

        [TestMethod]
        public void TestMethod1()
        {
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true;
            doc.Load("sample.xml");

            var obj = (Application)Deserialize(doc, typeof(Application));
        }

        [TestMethod]
        public async Task Test_Application_Save()
        {
            var response = await DoApplicationSave("sample0101.xml");

            Assert.IsNotNull(response);
        }

        [TestMethod]
        public async Task Test_FreeApplication_Save()
        {
            var response = await DoApplicationSave("sample0102.xml");

            Assert.IsTrue(response != null
                && response.PaymentAmount == 0m
                && response.ApplicationStatus == Applications.Models.ApplicationStatuses.Completed
                && !string.IsNullOrEmpty(response.RegisterOutcomeNumber));
        }

        [TestMethod]
        public async Task Test_Correction_Save()
        {
            var response = await DoApplicationSave("correction01.xml");

            Assert.IsNotNull(response);
        }

        [TestMethod]
        public async Task Test_FreeReport_Save()
        {
            var response = await DoApplicationSave("report01.xml");

            Assert.IsTrue(response.ApplicationStatus == Applications.Models.ApplicationStatuses.Completed
                && (response.PaymentAmount == 0m || response.PaymentAmount == null));
        }

        [TestMethod]
        public async Task Test_PaidReport_Save()
        {
            var response = await DoApplicationSave("report02.xml");

            Assert.IsTrue(response.ApplicationStatus == Applications.Models.ApplicationStatuses.WaitingPayment
                && response.PaymentAmount.GetValueOrDefault() > 0m);
        }

        public Task<ApplicationProcessedResult> DoApplicationSave(string filename, string operationID = null, ServiceProvider serviceProvider = null)
        {
            return Task.FromResult(new ApplicationProcessedResult());

            //XmlDocument doc = new XmlDocument();
            //doc.PreserveWhitespace = true;
            //doc.Load(filename);

            //var portalOperationID = operationID ?? Guid.NewGuid().ToString();
            //var applicationService = (serviceProvider ?? ServiceProvider).GetRequiredService<ApplicationService>();

            //ApplicationResponse response = null;

            //using (var ms = new MemoryStream())
            //{
            //    doc.Save(ms);
            //    ms.Position = 0;

            //    response = await applicationService.SaveAsync(portalOperationID, ms);
            //}
            //return response;
        }

        [TestMethod]
        public void Test_DeserializeConcreteType()
        {
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true;
            doc.Load("sample0101.xml");

            var obj = (CertificateForPerson)Deserialize(doc, typeof(CertificateForPerson));
        }

        [TestMethod]
        public void Test_Application_CRUD_Create()
        {
            new ApplicationRepository().Create(new Applications.Models.Application());
        }

        [TestMethod]
        public void Test_ServiceOperation_Search()
        {
            var entity = new ServiceOperationRepository();

            var data = entity.Search(new ServiceOperationSearchCriteria()
            {
                ServiceOperationType = Common.Models.ServiceOperationTypes.AcceptServiceApplication
            }).ToList();

            Assert.IsTrue(data != null);
        }

        [TestMethod]
        public void Test_ServiceOperation_Create()
        {
            var entity = new ServiceOperationRepository();
            
            var operation = new Common.Models.ServiceOperation()
            {
                OperationID = Guid.NewGuid().ToString(),
                ServiceOperationType = Common.Models.ServiceOperationTypes.AcceptServiceApplication
            };
            entity.Create(operation);
            
            Assert.IsTrue(operation.ServiceOperationID.HasValue);
        }



        private object Deserialize(XmlDocument doc, Type type)
        {
            var serializer = new XmlSerializer(type);

            using (StringReader rdr = new StringReader(doc.OuterXml))
            {
                return serializer.Deserialize(rdr);
            }
        }
    }

    [XmlType(AnonymousType = true, Namespace = "reau.pr.bg")]
    [XmlRoot("Application", Namespace = "reau.pr.bg", IsNullable = false)]
    public class Application
    {
        public Header Header { get; set; }

        [System.Xml.Serialization.XmlArrayItemAttribute("AttachedDocument")]
        public AttachedDocument[] AttachedDocuments { get; set; }
    }

    public class Header
    {
        public ServiceInfo ServiceInfo { get; set; }
        public string OfficeID { get; set; }
        public Applicant Applicant { get; set; }
    }

    public class ServiceInfo
    {
        public string Code { get; set; }
        public decimal Obligation { get; set; }
    }

    public class Applicant
    {
        public string CIN { get; set; }
    }

    public class AttachedDocument
    {
        public Guid BackOfficeGUID { get; set; }
    }

    [XmlType(AnonymousType = true, Namespace = "reau.pr.bg/CertificateForPerson")]
    [XmlRoot("CertificateForPerson", Namespace = "reau.pr.bg/CertificateForPerson", IsNullable = false)]
    public class CertificateForPerson : ApplicationBase
    {
        public string Type { get; set; }
    }

    [XmlType(AnonymousType = true, Namespace = "reau.pr.bg")]
    public class ApplicationBase
    {
        public Header Header { get; set; }

        [System.Xml.Serialization.XmlArrayItemAttribute("AttachedDocument")]
        public AttachedDocument[] AttachedDocuments { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Extensions/ServiceCollectionExtensions.cs
﻿using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Applications.Persistence;
using PropertyRegister.REAU.Common;
using PropertyRegister.REAU.Common.Persistence;
using PropertyRegister.REAU.Integration;
using PropertyRegister.REAU.Nomenclatures;
using PropertyRegister.REAU.Payments;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddApplicationsPersistence(this IServiceCollection serviceCollection)
        {
            return serviceCollection
                .AddTransient<IApplicationRepository, ApplicationRepository>()
                .AddTransient<IServiceInstanceRepository, ServiceInstanceRepository>()
                .AddTransient<IApplicationDocumentRepository, ApplicationDocumentRepository>()
                .AddTransient<IServiceActionRepository, ServiceActionRepository>();
        }

        public static IServiceCollection AddApplicationsAcceptance(this IServiceCollection serviceCollection)
        {
            return serviceCollection
                .AddTransient<IApplicationAcceptanceService, ApplicationAcceptanceService>()
                .AddTransient<INomenclaturesProvider, NomenclaturesProvider>()
                .AddTransient<IApplicationInfoResolver, ApplicationInfoResolver>();
        }

        public static IServiceCollection AddApplicationsProcessing(this IServiceCollection serviceCollection)
        {
            return serviceCollection
                .AddTransient<IApplicationProcessingService, ApplicationService>()
                .AddTransient<INomenclaturesProvider, NomenclaturesProvider>()
                .AddTransient<IPaymentManager, PaymentManager>();
        }

        public static IServiceCollection AddDocuments(this IServiceCollection serviceCollection)
        {
            return serviceCollection
                .AddTransient<IDocumentDataRepository, DocumentDataRepository>()
                .AddTransient<IDocumentService, DocumentService>();
        }

        public static IServiceCollection AddREAUInfrastructureServices(this IServiceCollection serviceCollection)
        {
            return serviceCollection
                .AddTransient<IActionDispatcher, DefaultActionDispatcher>()
                .AddTransient<IIdempotentOperationExecutor, IdempotentOperationExecutor>()
                .AddTransient<IServiceOperationRepository, ServiceOperationRepository>();
        }

        public static IServiceCollection AddIntegrationClients(this IServiceCollection serviceCollection)
        {
            return serviceCollection
                .AddTransient<IPropertyRegisterClient, PropertyRegisterClient>()
                .AddTransient<IPaymentIntegrationClient, PaymentIntegrationClient>();
        }
    }
}
<file_sep>/PropertyRegister.REAU/Common/DistributedOperationService.cs
﻿using Newtonsoft.Json;
using PropertyRegister.REAU.Common.Models;
using PropertyRegister.REAU.Common.Persistence;
using PropertyRegister.REAU.Persistence;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Common
{
    public interface IIdempotentOperationExecutor
    {
        Task<T> ExecuteAsync<T>(string operationID, ServiceOperationTypes operationType, Func<long, Task<T>> transactionalOperation, Action<T> nonTransactionOperationOnFirstCall = null);
    }

    public class IdempotentOperationExecutor : IIdempotentOperationExecutor
    {
        private readonly IServiceOperationRepository ServiceOperationRepository;

        public IdempotentOperationExecutor(IServiceOperationRepository serviceOperationRepository)
        {
            ServiceOperationRepository = serviceOperationRepository;
        }

        public async Task<T> ExecuteAsync<T>(string operationID, ServiceOperationTypes operationType, 
                Func<long, Task<T>> transactionalOperation, Action<T> nonTransactionOperationOnFirstCall = null)
        {
            try
            {
                bool firstOperationCall = false;

                var transactResult = await DbContextHelper.TransactionalOperationAsync(async () =>
                {
                    var operation = new ServiceOperation()
                    {
                        OperationID = operationID,
                        ServiceOperationType = operationType
                    };

                    ServiceOperationRepository.Create(operation);

                    if (operation.IsCompleted)
                    {
                        if (string.IsNullOrEmpty(operation.Result))
                            return default(T);

                        return JsonConvert.DeserializeObject<T>(operation.Result);
                    }
                    else
                    {
                        T res = await transactionalOperation(operation.ServiceOperationID.Value);

                        operation.Result = JsonConvert.SerializeObject(res);
                        operation.IsCompleted = true;
                        ServiceOperationRepository.Update(operation);

                        firstOperationCall = true;
                        return res;
                    }
                });

                if (firstOperationCall)
                    nonTransactionOperationOnFirstCall?.Invoke(transactResult);

                return transactResult;
            }
            catch (Exception ex)
            {
                // log ex

                throw;
            }
        }
    }
}
<file_sep>/PropertyRegister.REAU.Test/DocumentsTests.cs
﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PropertyRegister.REAU.Common.Models;
using PropertyRegister.REAU.Common.Persistence;
using PropertyRegister.REAU.Persistence;
using PropertyRegister.REAU.Test.Mocks;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test
{
    [TestClass]
    public class DocumentsTests
    {
        private ServiceProvider ServiceProvider;
        private ServiceCollection Services;

        public DocumentsTests()
        {
            var services = new ServiceCollection();
            services.AddTransient<IDocumentDataRepository, DocumentDataRepository>();

            Services = services;
            ServiceProvider = services.BuildServiceProvider();
        }

        [TestInitialize]
        public void Init()
        {
            Thread.CurrentPrincipal = new TestPrincipal("1");
        }

        [TestMethod]
        public async Task Test_DocumentData()
        {
            await DbContextHelper.TransactionalOperationAsync(() =>
            {
                string filename = @"C:\Users\vachev\Desktop\scripts.sql";
                var entity = ServiceProvider.GetRequiredService<IDocumentDataRepository>();

                using (var fs = File.OpenRead(filename))
                {
                    var document = new DocumentData()
                    {
                        ContentType = "text/xml",
                        Filename = "test.txt",
                        IsTemporal = true,
                        Content = fs
                    };

                    entity.Create(document);

                    Assert.IsTrue(document.DocID.HasValue);

                    document.IsTemporal = false;

                    entity.Update(document);

                    var documentFound = entity.Search(new DocumentDataSearchCriteria() { Identifiers = new List<string>() { document.Identifier } }).SingleOrDefault();

                    Assert.IsTrue(documentFound != null && documentFound.IsTemporal == false);
                }
                return Task.CompletedTask;
            });            
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Persistence/ApplicationRepository.cs
﻿using CNSys.Data;
using PropertyRegister.REAU.Applications.Models;
using System;
using System.Collections.Generic;
using CNSys.Extensions;

namespace PropertyRegister.REAU.Applications.Persistence
{
    public class ApplicationSearchCriteria
    {
        public List<long> ApplicationIDs { get; set; }
        public long? MainApplicationID { get; set; }
        public long? ServiceInstanceID { get; set; }
        public string ApplicationIdentifier { get; set; }
        public string ReportIdentifier { get; set; }
        public ApplicationStatuses? ApplicationStatus { get; set; }
        public int? ApplicationType { get; set; }
        public string PaymentIdentifier { get; set; }
    }

    public interface IApplicationRepository : IRepository<Application, long?, ApplicationSearchCriteria>
    {
        void CreateServicePayment(ServicePayment paymentRequest);
    }
   
    public class ApplicationRepository : RepositoryBase<Application, long?, ApplicationSearchCriteria, ApplicationProcessDataContext>, IApplicationRepository
    {
        public ApplicationRepository() : base(false)
        {
        }

        protected override void CreateInternal(ApplicationProcessDataContext context, Application item)
        {
            context.ApplicationCreate(item.MainApplicationID, item.ServiceInstanceID, item.IsReport,
                (int)item.Status, item.StatusTime, item.ApplicationTypeID.Value, item.RegistrationTime, out long applicationID, out string applIdentifier, out string reportIdentifier);

            item.ApplicationID = applicationID;
            item.ApplicationIdentifier = applIdentifier;
            item.ReportIdentifier = reportIdentifier;
        }

        protected override void UpdateInternal(ApplicationProcessDataContext context, Application item)
        {
            context.ApplicationUpdate(item.ApplicationID.Value, item.MainApplicationID, item.ServiceInstanceID, item.ApplicationIdentifier, item.ReportIdentifier,
                (int)item.Status, item.StatusTime, item.ApplicationTypeID.Value, item.RegistrationTime);
        }

        protected override IEnumerable<Application> SearchInternal(ApplicationProcessDataContext context, ApplicationSearchCriteria searchCriteria)
        {            
            IEnumerable<Application> res;

            string ids = string.Join(",", searchCriteria.ApplicationIDs.ToArray());

            using (var data = context.ApplicationSearch(ids, searchCriteria.MainApplicationID, searchCriteria.ServiceInstanceID, 
                searchCriteria.ApplicationIdentifier, searchCriteria.ReportIdentifier, (int?)searchCriteria.ApplicationStatus, searchCriteria.ApplicationType, 
                1, 20, out int count))
            {
                 res = data.ReadToList<Application>();
            }

            return res;
        }

        public void CreateServicePayment(ServicePayment paymentRequest)
        {
            throw new NotImplementedException();
        }
    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/ApplicationEntityMock.cs
﻿using CNSys.Data;
using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Applications.Models;
using PropertyRegister.REAU.Applications.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class ApplicationEntityMock : IApplicationRepository
    {
        private readonly DbDataSets DataSets;

        public bool IsReadOnly => throw new NotImplementedException();

        public ApplicationEntityMock(DbDataSets dataSets)
        {
            DataSets = dataSets;
        }

        public void AddApplication(Applications.Models.Application application)
        {
            if (application.ServiceInstanceID == default(long) ||
                application.Status == null ||
                application.ApplicationTypeID == null)
                throw new ArgumentException("IApplicationEntity.AddApplication has wrong parameters!");

            application.ApplicationID = 1000;
            application.ApplicationIdentifier = $"{DateTime.Now.ToString("yyyymmdd")}-0001";
        }

        public void SaveApplicationDocument(ApplicationDocument applicationDocument)
        {
            if (applicationDocument.DocumentID == default(long) || applicationDocument.DocumentType == null)
                throw new ArgumentException(nameof(applicationDocument));
        }

        public void SaveServiceDocument(ServiceDocument serviceDocument)
        {
            if (serviceDocument.DocumentID == default(long) || serviceDocument.ServiceDocumentTypeID == null)
                throw new ArgumentException(nameof(serviceDocument));
        }

        public void SavePaymentRequest(ServicePayment paymentRequest)
        {
            paymentRequest.PaymentRequestID = 1000;
        }

        public IEnumerable<Applications.Models.Application> SearchApplications(long? applicationID, string appNumber)
        {
            return DataSets.Applications.Where(a => (applicationID.HasValue && a.ApplicationID == applicationID.Value) ||
                (!string.IsNullOrEmpty(appNumber) && a.ApplicationIdentifier == appNumber));
        }

        public IEnumerable<ServiceInstanceAction> SearchServiceActions(string operationID, ServicеActionTypes actionType)
        {
            return DataSets.ServiceActions.Where(sa => sa.OperationID.ToString() == operationID && sa.ActionTypeID == actionType);
        }

        public void UpdateApplication(Applications.Models.Application application)
        {           
        }

        public void SaveServiceAction(ServiceInstanceAction serviceAction)
        {
            serviceAction.ServiceActionID = 1000;
        }

        public void AddServiceInstance(ServiceInstance serviceInstance)
        {
            serviceInstance.ServiceInstanceID = 1000;
        }

        public void CreateServicePayment(ServicePayment paymentRequest)
        {
            throw new NotImplementedException();
        }

        public void Create(Applications.Models.Application item)
        {
            throw new NotImplementedException();
        }

        public void Delete(long? key)
        {
            throw new NotImplementedException();
        }

        public void Delete(Applications.Models.Application item)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Applications.Models.Application> Search(ApplicationSearchCriteria searchCriteria)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Applications.Models.Application> Search(object state, ApplicationSearchCriteria searchCriteria)
        {
            throw new NotImplementedException();
        }

        public void Update(Applications.Models.Application item)
        {
            throw new NotImplementedException();
        }

        public Applications.Models.Application Read(long? key)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Applications.Models.Application> Search(PagedDataState state, ApplicationSearchCriteria searchCriteria)
        {
            throw new NotImplementedException();
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Models/Application.cs
﻿using PropertyRegister.REAU.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Models
{
    /// <summary>
    /// Статуси на Заявление за услуга
    /// </summary>
    public enum ApplicationStatuses
    {
        /// <summary>
        /// Прието.
        /// </summary>
        Accepted = 1,

        /// <summary>
        /// Чака регистрация.
        /// </summary>
        WaitingRegistration = 2,

        /// <summary>
        ///  В процес на изпълнение.
        /// </summary>
        InProgress = 3,

        /// <summary>
        /// Чака плащане.
        /// </summary>
        WaitingPayment = 4,

        /// <summary>
        /// Прекратено .
        /// </summary>
        Terminated = 5,

        /// <summary>
        /// Изпълнено.
        /// </summary>
        Completed = 6,

        //
        // Service only
        //

        /// <summary>
        /// Регистрирано 
        /// </summary>
        Registered = 11,

        /// <summary>
        /// Без движение – за преразглеждане
        /// </summary>
        WithoutMovementForReview = 12,

        /// <summary>
        /// Без движение
        /// </summary>
        WithoutMovement = 13,

        /// <summary>
        /// Отказано 
        /// </summary>
        Denied = 14,
    }

    /// <summary>
    /// Заявление за услуга
    /// </summary>
    public class Application
    {
        [DapperColumn("application_id")]
        public long? ApplicationID { get; set; }

        /// <summary>
        /// връзка към заявена услуга
        /// </summary>
        [DapperColumn("serviceinstance_id")]
        public long ServiceInstanceID { get; set; }

        /// <summary>
        /// Връзка към основното заявление от ServiceInstanceApplication
        /// </summary>
        [DapperColumn("mainapplication_id")]
        public long? MainApplicationID { get; set; }

        /// <summary>
        /// уникален идентификатор (референтен номер)
        /// </summary>
        [DapperColumn("application_identifier")]
        public string ApplicationIdentifier { get; set; }

        /// <summary>
        /// уникален регистров номер на заявление за справка (различен е от референтния номер)
        /// </summary>
        [DapperColumn("report_identifier")]
        public string ReportIdentifier { get; set; }

        /// <summary>
        /// Актуален Статус (Денормализация?)
        /// </summary>
        [DapperColumn("status")]
        public ApplicationStatuses? Status { get; set; }

        /// <summary>
        /// Дата на статус (Денормализация?).
        /// </summary>
        [DapperColumn("statustime")]
        public DateTime StatusTime { get; set; }

        /// <summary>
        /// Идентификатор на вида на заявлението от номенклатурата на ИС на ИР
        /// </summary>
        [DapperColumn("applicationtype_id")]
        public int? ApplicationTypeID { get; set; }

        /// <summary>
        /// дата на регистрация на заявлението в РЕАУ.
        /// </summary>
        [DapperColumn("registrationtime")]
        public DateTime RegistrationTime { get; set; }

        public bool IsReport { get; set; }

        public bool IsMainApplication => MainApplicationID == null;
    }    
}
<file_sep>/PropertyRegister.REAU/Common/DocumentService.cs
﻿using PropertyRegister.REAU.Common.Models;
using PropertyRegister.REAU.Common.Persistence;
using PropertyRegister.REAU.Persistence;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Common
{
    public class DocumentCreateRequest
    {
        public string Filename { get; set; }
        public string ContentType { get; set; }
        public Stream Content { get; set; }
    }

    public class DocumentCreateResult
    {
        public long DocumentDataID { get; set; }
        public string DocumentIdentifier { get; set; }
    }

    public interface IDocumentService
    {
        Task<DocumentCreateResult> SaveDocumentAsync(DocumentCreateRequest request);
        Task DeleteDocumentAsync(string documentIdentifier);
        Task<IEnumerable<DocumentData>> GetDocumentsAsync(List<string> documentIdentifiers, bool loadContent = false);
    }

    public class DocumentService : IDocumentService
    {
        private readonly IDocumentDataRepository DocumentDataRepository;

        public DocumentService(IDocumentDataRepository documentDataRepository)
        {
            DocumentDataRepository = documentDataRepository;
        }

        public Task<DocumentCreateResult> SaveDocumentAsync(DocumentCreateRequest request)
        {
            return DbContextHelper.TransactionalOperationAsync(() => SaveDocumentAsyncInternal(request));
        }

        public Task DeleteDocumentAsync(string documentIdentifier)
        {
            if (!Guid.TryParse(documentIdentifier, out Guid guid))
                throw new ArgumentException("documentIdentifier not valid GUID!");

            DocumentDataRepository.Delete(guid);

            return Task.CompletedTask;
        }

        public Task<IEnumerable<DocumentData>> GetDocumentsAsync(List<string> documentIdentifiers, bool loadContent = false)
        {
            var docs = DocumentDataRepository.Search(new DocumentDataSearchCriteria()
            {
                Identifiers = documentIdentifiers
            });

            if (loadContent)
            {
                foreach (var doc in docs)
                {
                    doc.Content = DocumentDataRepository.ReadContent(doc.Identifier);
                }
            }

            return Task.FromResult(docs);
        }

        private Task<DocumentCreateResult> SaveDocumentAsyncInternal(DocumentCreateRequest request)
        {
            var documentData = new DocumentData()
            {
                Identifier = Guid.NewGuid().ToString(),
                ContentType = request.ContentType,
                Filename = request.Filename,
                IsTemporal = true,
                Content = request.Content
            };

            DocumentDataRepository.Create(documentData);

            return Task.FromResult(new DocumentCreateResult()
            {
                DocumentDataID = documentData.DocID.Value,
                DocumentIdentifier = documentData.Identifier.ToString()
            });
        }
    }
}
<file_sep>/PropertyRegister.REAU/Persistence/DapperColumnAttribute.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Persistence
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class DapperColumnAttribute : Attribute
    {
        private string _name;

        public DapperColumnAttribute(string name)
        {
            this._name = name;
        }

        public string Name { get { return _name; } }
    }
}
<file_sep>/PropertyRegister.REAU/Common/ActionDispatcher.cs
﻿using Rebus.Bus;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Common
{
    public interface IActionDispatcher
    {
        Task SendAsync(object actionData);
    }

    public class DefaultActionDispatcher : IActionDispatcher
    {
        private readonly IBus Bus;

        public DefaultActionDispatcher(IBus bus)
        {
            Bus = bus;
        }

        public Task SendAsync(object actionData) => Bus.Send(actionData);
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Persistence/ServiceInstanceRepository.cs
﻿using System.Collections.Generic;
using PropertyRegister.REAU.Applications.Models;
using CNSys.Data;

namespace PropertyRegister.REAU.Applications.Persistence
{
    public interface IServiceInstanceRepository : IRepository<ServiceInstance, long?, object>
    {
    }

    public class ServiceInstanceRepository : RepositoryBase<ServiceInstance, long?, object, ApplicationProcessDataContext>, IServiceInstanceRepository
    {
        public ServiceInstanceRepository() : base(false)
        {
        }

        protected override void CreateInternal(ApplicationProcessDataContext context, ServiceInstance item)
        {
            context.ServiceInstanceCreate(item.OfficeID, int.Parse(item.ApplicantCIN), out long serviceInstanceID);
            item.ServiceInstanceID = serviceInstanceID;
        }

        protected override void UpdateInternal(ApplicationProcessDataContext context, ServiceInstance item)
        {
            context.ServiceInstanceUpdate(item.ServiceInstanceID.Value, item.OfficeID, int.Parse(item.ApplicantCIN));
        }

        protected override IEnumerable<ServiceInstance> SearchInternal(ApplicationProcessDataContext context, object searchCriteria)
        {
            IEnumerable<ServiceInstance> res;

            using (var data = context.ServiceInstanceSearch(null, null, null, 1, 20, out int count))
            {
                res = data.ReadToList<ServiceInstance>();
            }

            return res;
        }
    }
}
<file_sep>/PropertyRegister.REAU.MessageProcessor/Program.cs
﻿using Microsoft.Extensions.Hosting;
using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Applications.MessageHandlers;
using PropertyRegister.REAU.Applications.Results;
using Rebus.Activation;
using Rebus.Config;
using Rebus.Handlers;
using Rebus.Retry.Simple;
using Rebus.Routing.TypeBased;
using Rebus.ServiceProvider;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace PropertyRegister.REAU.MessageProcessor
{
    class Program
    {
        static void Main(string[] args)
        {
            IHost host = null;

            var hostBuilder = new HostBuilder()
                .ConfigureServices((context, services) =>
                    {
                        // applications DI
                        services.AddApplicationsPersistence()
                            .AddApplicationsProcessing()
                            .AddIntegrationClients()
                            .AddREAUInfrastructureServices();

                        services.AutoRegisterHandlersFromAssemblyOf<ApplicationAcceptedResultHandler>();

                        services.AddRebus(rconfig => rconfig
                            .Transport(t => t.UseOracle(System.Configuration.ConfigurationManager.ConnectionStrings["defaultRWConnectionString"].ConnectionString, "mbus_messages", "q_appl_processing"))
                            .Routing(r => r.TypeBased().Map<ApplicationAcceptedResult>("q_appl_processing")
                                                       .Map<ApplicationProcessedResult>("q_appl_processing")
                                    ));
                    });
            try
            {
                host = hostBuilder.Build();

                // start rebus
                host.Services.UseRebus();

                host.Start();

                Console.WriteLine("Press ENTER to quit");
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                Console.WriteLine("Quitting...");

                host?.Dispose();
                host = null;
            }
        }       
    }    
}
<file_sep>/PropertyRegister.REAU/Applications/Models/ServiceAction.cs
﻿using PropertyRegister.REAU.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Models
{
    /// <summary>
    /// Класът описва действия/операции по промяна на състояние на Application/ServiceInstance
    /// </summary>
    public class ServiceAction
    {
        [DapperColumn("service_action_id")]
        public long? ServiceActionID { get; set; }

        [DapperColumn("operation_id")]
        public long OperationID { get; set; }

        [DapperColumn("service_inst_id")]
        public long ServiceInstanceID { get; set; }

        [DapperColumn("application_id")]
        public long ApplicationID { get; set; }

        [DapperColumn("application_status")]
        public ApplicationStatuses ApplicationStatus { get; set; }

        [DapperColumn("action_type_id")]
        public ServicеActionTypes? ActionTypeID { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU.Test/ServiceApplicationsTests.cs
﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Applications.Persistence;
using PropertyRegister.REAU.Applications.Results;
using PropertyRegister.REAU.Common;
using PropertyRegister.REAU.Common.Persistence;
using PropertyRegister.REAU.Domain;
using PropertyRegister.REAU.Integration;
using PropertyRegister.REAU.Payments;
using PropertyRegister.REAU.Persistence;
using PropertyRegister.REAU.Test.Mocks;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace PropertyRegister.REAU.Test
{
    [TestClass]
    public class ServiceApplicationsTests
    {
        private ServiceProvider ServiceProvider;
        private ServiceCollection Services;

        public ServiceApplicationsTests()
        {
            var services = new ServiceCollection();
            services.AddTransient<ApplicationService>();
            services.AddTransient<IApplicationRepository, ApplicationRepository>();
            services.AddTransient<IServiceInstanceRepository, ServiceInstanceRepository>();
            services.AddTransient<IServiceOperationRepository, ServiceOperationRepository>();
            services.AddTransient<IApplicationDocumentRepository, ApplicationDocumentRepository>();

            services.AddTransient<IServiceActionRepository, ServiceInstanceActionEntityMock>();
            services.AddTransient<IDocumentService, DocumentServiceMock>();
            services.AddTransient<IPropertyRegisterClient, PropertyRegisterClientMock>();
            services.AddTransient<IPaymentIntegrationClient, PaymentIntegrationClientMock>();
            services.AddTransient<IIdempotentOperationExecutor, IdempotentOperationExecutor>();
            services.AddTransient<IPaymentManager, PaymentManager>();

            services.AddSingleton<IActionDispatcher, DummyActionDispatcherMock>();
            services.AddSingleton<IApplicationInfoResolver, ApplicationInfoResolver>();

            Services = services;
            ServiceProvider = services.BuildServiceProvider();
        }

        [TestInitialize]
        public void Init()
        {
            Thread.CurrentPrincipal = new TestPrincipal("1");
        }

        [TestCleanup]
        public void CleanUp()
        {
        }

        [TestMethod]
        public async Task TestApplicationAcceptance()
        {
            ApplicationAcceptedResult response = null;

            string filename = "sample0102.xml";

            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true;
            doc.Load(filename);

            var applicationService = ServiceProvider.GetRequiredService<IApplicationAcceptanceService>();

            using (var ms = new MemoryStream())
            {
                doc.Save(ms);
                ms.Position = 0;

                response = await DbContextHelper.TransactionalOperationAsync(() =>
                {
                    return applicationService.AcceptApplicationAsync(Guid.NewGuid().ToString(), ms);
                });
            }

            Assert.IsTrue(response.ApplicationStatus == Applications.Models.ApplicationStatuses.Accepted);
        }

        [TestMethod]
        public async Task Test_MessageBus()
        {
            await ServiceProvider.GetRequiredService<IActionDispatcher>()
                .SendAsync((long)100);
        }

        [TestMethod]
        public void Test_ApplicationInfoResolver_AttachedDocuments()
        {
            var resolver = ServiceProvider.GetRequiredService<IApplicationInfoResolver>();
            
            using (var ms = File.OpenRead(@"C:\Users\vachev\Desktop\xsd-xml\xml\CertificateForPersonFromPropertyRegister-test.xml"))
            {
                var info = resolver.GetApplicationInfoFromXml(ms);

                Assert.IsTrue(info.AttachedDocumentIDs != null && info.AttachedDocumentIDs.Any());
            }                
        }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Persistence/ApplicationProcessDataContext.cs
﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
using PropertyRegister.REAU.Applications.Models;
using PropertyRegister.REAU.Persistence;

namespace PropertyRegister.REAU.Applications.Persistence
{
    public class ApplicationProcessDataContext : BaseDataContext
    {
        public ApplicationProcessDataContext(DbConnection dbConnection) : base(dbConnection)
        {
            SqlMapper.SetTypeMap(typeof(Application), DataContextHelper.ColumnMap<Application>());
        }

        public void ApplicationCreate(long? mainApplicationID, long serviceInstanceID, bool isReport,
            int status, DateTime statusTime, int applicationTypeID, DateTime registrationTime, 
            out long applicationID, out string applIdentifier, out string reportIdentifier)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_ServiceInstance_Id", OracleDbType.Int64, serviceInstanceID);
            parameters.Add("p_MainApplication_Id", OracleDbType.Int64, mainApplicationID);
            parameters.Add("p_Is_Report", OracleDbType.Int32, isReport);
            parameters.Add("p_Status", OracleDbType.Int16, status);
            parameters.Add("p_StatusTime", OracleDbType.TimeStamp, statusTime);
            parameters.Add("p_ApplicationType_Id", OracleDbType.Int16, applicationTypeID);
            parameters.Add("p_RegistrationTime", OracleDbType.TimeStamp, registrationTime);
            parameters.Add("p_Application_Id_Out", OracleDbType.Int64, null, System.Data.ParameterDirection.Output);
            parameters.Add("p_Application_Identifier_Out", OracleDbType.Varchar2, null, System.Data.ParameterDirection.Output, 100);
            parameters.Add("p_Report_Identifier_Out", OracleDbType.Varchar2, null, System.Data.ParameterDirection.Output, 100);

            DbConnection.SPExecute("pkg_services.p_Applications_Create", parameters);

            applicationID = parameters.GetLongNumber("p_Application_Id_Out");
            applIdentifier = parameters.GetString("p_Application_Identifier_Out");
            reportIdentifier = parameters.GetString("p_Report_Identifier_Out");
        }

        public void ApplicationUpdate(long applicationID, long? mainApplicationID, long serviceInstanceID, string applIdentifier, string reportIdentifier, 
            int status, DateTime statusTime, int applicationTypeID, DateTime registrationTime)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Application_Id", OracleDbType.Int64, applicationID);
            parameters.Add("p_ServiceInstance_Id", OracleDbType.Int64, serviceInstanceID);
            parameters.Add("p_MainApplication_Id", OracleDbType.Int64, mainApplicationID);
            parameters.Add("p_ApplicationIdentifier", OracleDbType.Varchar2, applIdentifier);
            parameters.Add("p_ReportIdentifier", OracleDbType.Varchar2, reportIdentifier);
            parameters.Add("p_Status", OracleDbType.Int16, status);
            parameters.Add("p_StatusTime", OracleDbType.TimeStamp, statusTime);
            parameters.Add("p_ApplicationType_Id", OracleDbType.Int16, applicationTypeID);
            parameters.Add("p_RegistrationTime", OracleDbType.TimeStamp, registrationTime);

            DbConnection.SPExecute("pkg_services.p_Applications_Update", parameters);
        }

        public CnsysGridReader ApplicationSearch(
            string applicationIDs, long? mainApplicationID, long? serviceInstanceID, string applIdentifier, string reportIdentifier,
            int? status, int? applicationTypeID, int p_StartIndex, int p_PageSize, out int count)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Application_Ids", OracleDbType.Varchar2, applicationIDs);
            parameters.Add("p_ServiceInstance_Id", OracleDbType.Int64, serviceInstanceID);
            parameters.Add("p_MainApplication_Id", OracleDbType.Int64, mainApplicationID);
            parameters.Add("p_ApplicationIdentifier", OracleDbType.Varchar2, applIdentifier);
            parameters.Add("p_ReportIdentifier", OracleDbType.Varchar2, reportIdentifier);
            parameters.Add("p_Status", OracleDbType.Int16, status);
            parameters.Add("p_ApplicationType_Id", OracleDbType.Int16, applicationTypeID);
            parameters.Add("p_StartIndex", OracleDbType.Int32, p_StartIndex);
            parameters.Add("p_PageSize", OracleDbType.Int32, p_PageSize);
            parameters.Add("p_ResultsCount_out", OracleDbType.Int32, null, System.Data.ParameterDirection.Output);
            parameters.Add("cv_1", OracleDbType.RefCursor, ParameterDirection.Output);

            var result = DbConnection.SPExecuteReader("pkg_services.p_Applications_Search", parameters);
            count = parameters.Get<OracleDecimal>("p_ResultsCount_out").ToInt32();

            return result;
        }

        #region Service Instance

        public void ServiceInstanceCreate(int officeID, int applicantCIN, out long serviceInstanceID)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Office_Id", OracleDbType.Int32, officeID);
            parameters.Add("p_Applicant_CIN", OracleDbType.Int32, applicantCIN);
            parameters.Add("p_ServiceInstance_Id_Out", OracleDbType.Int32, null, System.Data.ParameterDirection.Output);

            DbConnection.SPExecute("pkg_services.p_ServiceInstance_Create", parameters);

            serviceInstanceID = parameters.GetLongNumber("p_ServiceInstance_Id_Out");
        }

        public void ServiceInstanceUpdate(long serviceInstanceID, int officeID, int applicantCIN)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("serviceInstanceID", OracleDbType.Int64, serviceInstanceID);
            parameters.Add("p_Office_Id", OracleDbType.Int32, officeID);
            parameters.Add("p_Applicant_CIN", OracleDbType.Int32, applicantCIN);

            DbConnection.SPExecute("pkg_services.p_ServiceInstance_Update", parameters);
        }

        public CnsysGridReader ServiceInstanceSearch(string p_ServiceInstance_Ids, long? p_Office_Id, long? p_Applicant_CIN, int p_StartIndex, int p_PageSize, out int count)
        {
            var parameters = new OracleDynamicParameters();
            
            parameters.Add("p_ServiceInstance_Ids", OracleDbType.Varchar2, p_ServiceInstance_Ids);
            parameters.Add("p_Office_Id", OracleDbType.Int32, p_Office_Id);
            parameters.Add("p_Applicant_CIN", OracleDbType.Int32, p_Applicant_CIN);
            parameters.Add("p_StartIndex", OracleDbType.Int32, p_StartIndex);
            parameters.Add("p_PageSize", OracleDbType.Int32, p_PageSize);
            parameters.Add("p_ResultsCount_out", OracleDbType.Int32, null, System.Data.ParameterDirection.Output);
            parameters.Add("cv_1", OracleDbType.RefCursor, ParameterDirection.Output);

            var reader = DbConnection.SPExecuteReader("PKG_VELIN.p_ServiceInstance_Search", parameters);

            count = parameters.GetIntNumber("p_ResultsCount_out");

            return reader;
        }

        #endregion

        #region Service Actions

        public void ServiceActionCreate(long operationID, long serviceInstanceID, long applicationID, int applicationStatus, int actionTypeID, out long serviceActionID)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_OperationID", OracleDbType.Int64, operationID);
            parameters.Add("p_ServiceInstID", OracleDbType.Int64, serviceInstanceID);
            parameters.Add("p_ApplicationID", OracleDbType.Int64, applicationID);
            parameters.Add("p_ApplicationStatus", OracleDbType.Int32, applicationStatus);
            parameters.Add("p_ActionTypeID", OracleDbType.Int32, actionTypeID);
            parameters.Add("p_ServiceActionID_out", OracleDbType.Int64, null, System.Data.ParameterDirection.Output);

            DbConnection.SPExecute("pkg_velin.p_Service_Action_Create", parameters);

            serviceActionID = parameters.GetLongNumber("p_ServiceActionID_out");
        }

        #endregion

        #region Application Documents 

        public void ApplicationDocumentCreate(long applicationID, int documentTypeID, long documentID, out long appDocumentID)
        {
            var parameters = new OracleDynamicParameters();
            parameters.Add("p_Application_Id", OracleDbType.Int64, applicationID);
            parameters.Add("p_Document_Type", OracleDbType.Int32, documentTypeID);
            parameters.Add("p_Document_Id", OracleDbType.Int64, documentID);
            parameters.Add("p_App_Document_Id_Out", OracleDbType.Int64, null, System.Data.ParameterDirection.Output);

            DbConnection.SPExecute("pkg_services.p_App_Documents_Create", parameters);

            appDocumentID = parameters.GetLongNumber("p_App_Document_Id_Out");
        }

        #endregion
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Results/ApplicationProcessedResult.cs
﻿using PropertyRegister.REAU.Applications.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Results
{
    public class ApplicationProcessedResult
    {
        public long ApplicationID { get; set; }
        public string ApplicationNumber { get; set; }
        public ApplicationStatuses? ApplicationStatus { get; set; }

        public decimal? PaymentAmount { get; set; }
        public DateTime? PaymentDeadline { get; set; }
        public string PaymentIdentifier { get; set; }
        public string RegisterOutcomeNumber { get; set; }

        public ICollection<string> Errors { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU.Test/Mocks/ServiceInstanceActionEntityMock.cs
﻿using CNSys.Data;
using PropertyRegister.REAU.Applications.Models;
using PropertyRegister.REAU.Applications.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Test.Mocks
{
    public class ServiceInstanceActionEntityMock : IServiceActionRepository
    {
        public bool IsReadOnly => throw new NotImplementedException();

        public void Create(ServiceAction item)
        {
            item.ServiceActionID = new Random().Next(1, 100);
        }

        public void Delete(long? key)
        {
            throw new NotImplementedException();
        }

        public void Delete(ServiceAction item)
        {
            throw new NotImplementedException();
        }

        public ServiceAction Read(long? key)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<ServiceAction> Search(ServiceActionSearchCriteria searchCriteria)
        {
            return Enumerable.Empty<ServiceAction>();
        }

        public IEnumerable<ServiceAction> Search(object state, ServiceActionSearchCriteria searchCriteria)
        {
            return Enumerable.Empty<ServiceAction>();
        }

        public IEnumerable<ServiceAction> Search(PagedDataState state, ServiceActionSearchCriteria searchCriteria)
        {
            throw new NotImplementedException();
        }

        public void Update(ServiceAction item)
        {
            throw new NotImplementedException();
        }
    }
}
<file_sep>/PropertyRegister.REAU.Web.Api/Startup.cs
﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PropertyRegister.REAU.Applications;
using PropertyRegister.REAU.Nomenclatures;
using PropertyRegister.REAU.Web.Api.Test;
using Rebus.ServiceProvider;
using Serilog;

namespace PropertyRegister.REAU.Web.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services
                .AddDocuments()
                .AddApplicationsAcceptance()
                .AddApplicationsPersistence()
                .AddREAUInfrastructureServices();

            // dummies ...
            services.AddTransient<INomenclaturesProvider, NomenclaturesProviderDummy>();

            services.AddLogging(loggingBuilder =>
            {
                loggingBuilder.AddSerilog(dispose: true);
            });

            services.AddRebusWithOracleTransport(Configuration);

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            app.UsePrincipalMiddleware();

            app.UseHttpsRedirection();
            app.UseRebus();
            app.UseMvc();
        }
    }
}
<file_sep>/PropertyRegister.REAU/Domain/ApplicationServiceType.cs
﻿using System;

namespace PropertyRegister.REAU.Domain
{
    /// <summary>
    /// Описание на предоставяна услуга от ИС на ПР.
    /// </summary>
    public class ApplicationServiceType
    {
        public int ApplicationTypeID { get; set; }

        public string Title { get; set; }

        public bool IsReport { get; set; }

        public bool IsFree { get; set; }

        public TimeSpan? PaymentDeadline { get; set; }

        public decimal? PaymentAmount { get; set; }

        /// <summary>
        /// дължимата такса се заплаща след регистриране на заявлението в ИС на ИР
        /// </summary>
        public bool PaymentAfterRegistration { get; set; }

        public string XmlNamespace { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Results/PaymentProcessedResult.cs
﻿using PropertyRegister.REAU.Applications.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Results
{
    public class PaymentProcessedResult
    {
        public ApplicationStatuses ApplicationStatus { get; set; }
        public long ApplicationID { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Common/Models/DocumentData.cs
﻿using PropertyRegister.REAU.Persistence;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Common.Models
{
    /// <summary>
    /// Документно съдържание + метаданни.
    /// </summary>
    public class DocumentData
    {
        [DapperColumn("document_id")]
        public long? DocID { get; set; }

        [DapperColumn("guid")]
        public string Identifier { get; set; }

        [DapperColumn("content_type")]
        public string ContentType { get; set; }

        [DapperColumn("filename")]
        public string Filename { get; set; }

        public string ContentHash { get; set; }

        /// <summary>
        /// Дали е наличен. 
        /// В процесът на заявявяне документът може да се изтрие, и само да се остави следа.
        /// </summary>
        public bool IsAvailable { get; set; }

        [DapperColumn("is_temp")]
        public bool IsTemporal { get; set; }

        public Stream Content { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU.Test/DbAccessTests.cs
﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Oracle.ManagedDataAccess.Client;
using PropertyRegister.REAU.Persistence;
using System.Data;
using System.Data.Common;
using System.Linq;

namespace PropertyRegister.REAU.Test
{
    [TestClass]
    public class DbAccessTests
    {
        [TestMethod]
        public void Test_Oracle_Access()
        {
            string connString = "Data Source=comreg;User ID=comreg_dev2;Password=<PASSWORD>;Pooling=true;HA Events=false;Connection Timeout=60";
            string ProviderName = "Oracle.ManagedDataAccess.Client";
            
            using (var con = DbProviderFactories.GetFactory(ProviderName).CreateConnection())
            {
                con.ConnectionString = connString;

                con.Open();

                // 01

                var dyParam = new OracleDynamicParameters();
                dyParam.Add("res", OracleDbType.RefCursor, ParameterDirection.Output);
                var query = "PKG_USERS.GetAllRoles";

                var reader = con.SPExecuteReader(query, dyParam);
                var data = reader.Read<Role>().ToList();

                // 02

                query = "PKG_USERS.getuserrolesbyid";
                dyParam = new OracleDynamicParameters();
                dyParam.Add("puser_id", OracleDbType.Int32, 835);
                dyParam.Add("res", OracleDbType.RefCursor, ParameterDirection.Output);
                reader = con.SPExecuteReader(query, dyParam);

                var userroles = reader.Read<UserRole>().ToList();

                // 03
                query = "pkg_integration_epzeu.p_indexedfields_search";
                dyParam = new OracleDynamicParameters();
                dyParam.Add("p_resultscount_out", OracleDbType.Int32, ParameterDirection.Output);
                dyParam.Add("p_startindex", OracleDbType.Int32, 1);
                dyParam.Add("p_pagesize", OracleDbType.Int32, 20);
                dyParam.Add("p_uic", OracleDbType.NVarchar2, null);
                dyParam.Add("p_name", OracleDbType.NVarchar2, "БЪЛГАРИЯ");
                dyParam.Add("p_typeid", OracleDbType.Int32, null);
                dyParam.Add("res", OracleDbType.RefCursor, ParameterDirection.Output);
                reader = con.SPExecuteReader(query, dyParam);

                var idxFields = reader.Read<IndexedField>().ToList();

                // 04

                dyParam = new OracleDynamicParameters();
                dyParam.Add("res1", OracleDbType.RefCursor, ParameterDirection.Output);
                dyParam.Add("res2", OracleDbType.RefCursor, ParameterDirection.Output);
                query = "pkg_test_2.GetAllRoles2";

                reader = con.SPExecuteReader(query, dyParam);
                var roles1 = reader.Read<Role>().ToList();
                var userroles1 = reader.Read<UserRole>().ToList();
            }

            Assert.IsTrue(true);
        }
    }

    public class Role
    {
        //[DapperColumn("code")]
        public string Role_name { get; set; }
    }

    public class UserRole
    {
        public int Role_ID { get; set; }
    }

    public class IndexedField
    {
        public string Name { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Models/ServiceDocument.cs
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Models
{
    /// <summary>
    /// Документи по услуга.
    /// </summary>
    public class ServiceDocument
    {
        public long? ServiceInstanceDocumentID { get; set; }

        /// <summary>
        /// Заявена услуга
        /// </summary>
        public long ServiceInstanceID { get; set; }

        /// <summary>
        /// електронно издадено удостоверение за лице/имот/период;
        /// сканирана резолюция на съдия по вписвания за открити нередовности в заявление за удостоверение;
        /// електронен незаверен препис на документ;
        /// резултат от електронна справка;
        /// </summary>
        public int? ServiceDocumentTypeID { get; set; }

        /// <summary>
        /// идентификатор/връзка към DocumentData
        /// </summary>
        public long DocumentID { get; set; }
    }
}
<file_sep>/PropertyRegister.REAU/Applications/Models/ApplicationDocument.cs
﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PropertyRegister.REAU.Applications.Models
{
    /// <summary>
    /// Документи към заявление
    /// </summary>
    public class ApplicationDocument
    {
        public long? ApplicationDocumentID { get; set; }

        /// <summary>
        /// идентификатор на заявление
        /// </summary>
        public long ApplicationID { get; set; }

        /// <summary>
        /// 0 - application xml 
        /// 1 - attached document
        /// </summary>
        public int? DocumentType { get; set; }

        /// <summary>
        /// идентификатор/връзка към DocumentData
        /// </summary>
        public long DocumentID { get; set; }        
    }
}
