c # Отображение данных с помощью Oracle и хранимых процедур - PullRequest
0 голосов
/ 11 апреля 2019

У нас сейчас проблема с производительностью. Многие проблемы напрямую связаны с базой данных, но я не уполномочен что-либо менять (из-за существующего программного обеспечения и т. Д.).

Проблемы, с которыми я столкнулся при создании API, заключались в том, что хранимые процедуры, которые содержат ужасные имена полей и часто используют сложные запросы для построения набора результатов.

Из-за этого мне не удалось использовать ORM с базой данных, и мне пришлось создавать собственное решение.

Я придумал этот дизайн:

public abstract class OracleService
{
    protected readonly IOracleUnitOfWork OracleUnitOfWork;
    protected OracleService(IOracleUnitOfWork oracleUnitOfWork) => OracleUnitOfWork = oracleUnitOfWork;

    protected async Task<List<T>> ListAsync<T>(string procedureName, IList<OracleParameter> parameters, int limit, Func<DataRow, T> mapper) => await ListAsync(procedureName, parameters, limit, mapperSync: mapper);
    protected async Task<List<T>> ListAsync<T>(string procedureName, IList<OracleParameter> parameters, int limit, Func<DataRow, Task<T>> mapper) => await ListAsync(procedureName, parameters, limit, mapperAsync: mapper);
    protected async Task<List<T>> ListAsync<T>(string procedureName, IList<OracleParameter> parameters, Func<DataRow, T> mapper) => await ListAsync(procedureName, parameters, 0, mapperSync: mapper);
    protected async Task<List<T>> ListAsync<T>(string procedureName, IList<OracleParameter> parameters, Func<DataRow, Task<T>> mapper) => await ListAsync(procedureName, parameters, 0, mapperAsync: mapper);

    protected T Get<T>(string procedureName, IList<OracleParameter> parameters, Func<DataRow, T> mapper)
    {
        if (mapper == null) throw new ArgumentNullException(nameof(mapper));

        var dataTable = OracleUnitOfWork.FillDataTable(procedureName, parameters);
        var dataRows = dataTable.AsEnumerable();

        if (dataRows.Count() != 1) throw new Exception("The number of rows returned is not 1.");

        return mapper(dataRows.First());
    }

    private async Task<List<T>> ListAsync<T>(string procedureName, IList<OracleParameter> parameters, int limit, Func<DataRow, T> mapperSync = null, Func<DataRow, Task<T>> mapperAsync = null)
    {
        if (mapperSync == null && mapperAsync == null) throw new ArgumentNullException();

        var dataTable = OracleUnitOfWork.FillDataTable(procedureName, parameters);
        var dataRows = limit == 0 ? dataTable.AsEnumerable() : dataTable.AsEnumerable().Take(limit);

        if (mapperSync != null)
            return dataTable.AsEnumerable().Select(mapperSync).ToList();

        var models = new List<T>();

        foreach(var row in dataRows)
        {
            var model = await mapperAsync(row);
            models.Add(model);
        }

        return models;
    }
}

Как видите, мы используем DataTable для извлечения наших данных (мы использовали DataReader, но прирост производительности не был), которые затем передаются в класс отображения.

Класс mapper может быть таким простым:

public static partial class OracleMapper
{
    public static class Complaint
    {
        /// <summary>
        ///     Maps the Returns entity
        /// </summary>
        /// <param name="reader">The data reader</param>
        /// <returns>An Returns entity</returns>
        public static Models.Complaint Map(DataRow reader) => new Models.Complaint
        {
            ComplaintNumber = reader["cidno"].ToString(),
            ComplaintLine = reader["cidlineno"].ToString(),
            AccountNumber = reader["accno"]?.ToString(),
            OrderNumber = reader["ordernumber"]?.ToString(),
            LineNumber = reader["orderline"]?.ToString(),
            Quantity = reader["quantity"].ToDecimal(),
            Reference = reader["complaint_reference"].ToString(),
            Status = reader["status"].ToString(),
            DateCreated = reader["complaintdate"].ToDate(),
            CollectionDate = reader["collectiondate"].ToDate(),
            ShipmentNumber = reader["shipmentNumber"]?.ToString()
        };
    }
}

или так сложно:

public static partial class OracleMapper
{
    public static class Account
    {
        /// <summary>
        ///     Maps the Account entity
        /// </summary>
        /// <param name="reader">The data reader</param>
        /// <returns>An Account entity</returns>
        public static Models.Account Map(DataRow reader)
        {
            var account = new Models.Account
            {
                AccountNumber = reader["accno"]?.ToString(),
                BusinessCategory = reader["buscat"]?.ToString(),
                Category = reader["category"]?.ToString(),
                CountryCode = reader["nationcode"]?.ToString(),
                Name = reader["custname"]?.ToString(),
                Disabled = reader["obsolete"]?.ToString(),
                OnStop = reader["stopped"]?.ToString(),
                CatalogueId = reader["prodcat"]?.ToString(),
                Profile = reader["profilestring"]?.ToString(),
                Trade = reader["proforma"].ToString(),
                ShortAccountNumber = reader["customer"]?.ToString(),
                StatementAccountNumber = reader["statementac"]?.ToString(),
                SalesOfficeNotes = reader["salesofficenotes"]?.ToString(),
                TerritoryCode = reader["territory"]?.ToString(),
                Type = reader["accountType"]?.ToString().Trim(),
                UnmannedAddress = reader["unmannedaddress"]?.ToString(),
                ZoneRateCode = reader["zoneratecode"]?.ToString(),
                Address = new Models.Address
                {
                    Name = reader["custname"]?.ToString(),
                    HouseNumber = reader["add1"]?.ToString(),
                    Street = reader["add2"]?.ToString(),
                    Town = reader["add3"]?.ToString(),
                    County = reader["add4"]?.ToString(),
                    PostCode = reader["postcode"]?.ToString()
                }
            };


            double.TryParse(reader["latitude"]?.ToString(), out var latitude);
            double.TryParse(reader["longitude"]?.ToString(), out var longitude);

            account.Address.Latitude = latitude;
            account.Address.Longitude = longitude;

            account.Contact = new Contact
            {
                Position = "Primary Contact",
                Initial = "",
                Title = "",
                Name = reader["primary_contact"]?.ToString(),
                Telephone = reader["phone"]?.ToString(),
                Fax = reader["fax"]?.ToString(),
                Email = reader["email"]?.ToString()
            };

            account.Currency = new Currency
            {
                Code = reader["currency"]?.ToString()
            };

            account.Group = new Group
            {
                Value = reader["grpcode"]?.ToString(),
                Description = reader["grpdesc"]?.ToString()
            };

            account.SalesAnalysisRepresentative = new SalesRepresentative
            {
                Code = reader["sr_code"]?.ToString(),
                FullName = reader["sr_name"]?.ToString()
            };

            account.CommissionAnalysisRepresentative = new SalesRepresentative
            {
                Code = reader["cr_code"]?.ToString(),
                FullName = reader["cr_name"]?.ToString()
            };

            account.ServiceRepresentative = new SalesRepresentative
            {
                Code = reader["repcode"]?.ToString(),
                FullName = reader["repname"]?.ToString(),
                Email = reader["repemail"]?.ToString(),
                Telephone = reader["reptelno"]?.ToString()
            };

            account.Finance = new Finance
            {
                Currency = reader["currency"]?.ToString(),
                CurrenyConversionRate = Convert.ToDecimal(reader["currency_convrate"]?.ToString()),
                Settlement = Convert.ToDecimal(reader["settlement"]?.ToString()),
                CreditLimit = Convert.ToDecimal(reader["creditlimit"]?.ToString()),
                CurrentBalance = Convert.ToDecimal(reader["balance"]?.ToString()),
                OrdersOnHand = Convert.ToDecimal(reader["ordersonhand"]?.ToString()),
                AvailableCredit = Convert.ToDecimal(reader["availablecredit"]?.ToString()),
                TradingPeriod = reader["tradingperiod_nicestring"]?.ToString(),
                VatCode = reader["vatcode"]?.ToString(),
                Visible = reader["financialdatavisible"]?.ToString(),
                CompanyRegistrationNumber = reader["company_registration_number"]?.ToString(),
                CustomerPriceFile = reader["custpricefile"]?.ToString(),
                EdiReference = reader["edi_reference"]?.ToString(),
                ExpectedReceiptDay = Convert.ToInt16(reader["expectedreceiptday"]?.ToString()),
                GroupPriceFile = reader["grouppricefile"]?.ToString(),
                ListPriceFile = reader["listpricefile"]?.ToString(),
                Owner = reader["Owner"]?.ToString(),
                OwnerName = reader["OwnerName"]?.ToString(),
                PaymentTerms = new PaymentTerms
                {
                    Value = reader["paymenttermsval"]?.ToString(),
                    Description = reader["paymentterms"]?.ToString()
                }
            };

            account.AvailableDeliveryDates = new List<DateTime>();
            account.Contacts = StatementContactMapping.MapList(reader) ?? new List<Contact>();

            return account;
        }
    }
}

Наши запросы не быстрые, а в некоторых случаях они невыносимы. Но, как я уже говорил, я не могу изменить базу данных или хранимые процедуры.

Есть ли у кого-нибудь код только предложения, которые могут помочь повысить нашу производительность?

Думаю, стоит упомянуть, что я пробовал Dapper и Entity Framework безрезультатно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...