EP Plus - диапазон ошибок таблицы сталкивается с таблицей - PullRequest
5 голосов
/ 08 марта 2019

Я создаю экспорт в Excel, используя приложения EP plus и c #. Я сейчас получаю ошибку.

'Диапазон таблицы сталкивается с таблицей tblAllocations29'

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

Я перебираю все ключи и снова перебираю каждую коллекцию, принадлежащую этому ключу.

Мне нужно распечатать табличную информацию для каждой коллекции вместе с ее итогами.

В текущем сценарии я получаю сообщение об ошибке при попытке печати три массива Первый массив имеет 17 записей Второй массив имеет 29 записей Третий массив имеет 6 записей

Я принял к сведению диапазоны, которые он создает при отладке

Диапазоны

A1  G18
A20 G50
A51 G58

контроллер

[HttpGet]
[SkipTokenAuthorization]
public HttpResponseMessage DownloadFundAllocationDetails(int id, DateTime date)
{
    var ms = GetStrategy(id);

    DateTime d = new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
    if (ms.FIRM_ID != null)
    {
        var firm = GetService<FIRM>().Get(ms.FIRM_ID.Value);
        IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationsGroup = null;
        var allocationsGrouped = GetAllocationsGrouped(EntityType.Firm, firm.ID, d);


         string fileName = string.Format("{0} as of {1}.xlsx", "test", date.ToString("MMM, yyyy"));
         byte[] fileContents;
         var newFile = new FileInfo(fileName);
         using (var package = new OfficeOpenXml.ExcelPackage(newFile))
         {
            FundAllocationsPrinter.Print(package, allocationsGrouped);
            fileContents = package.GetAsByteArray();
         }

         var result = new HttpResponseMessage(HttpStatusCode.OK)
         {
             Content = new ByteArrayContent(fileContents)
         };

         result.Content.Headers.ContentDisposition =
            new ContentDispositionHeaderValue("attachment")
            {
                 FileName = fileName
            };

         result.Content.Headers.ContentType =
            new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

         return result;
    }

    return null;

    #endregion
}

Я написал следующую утилиту, которая будет пытаться экспортировать. Иногда он работает, когда есть две коллекции массивов, и сбой при обработке трех. Может кто-нибудь сказать мне, в чем проблемы

FundsAllocationsPrinter.cs

public class FundAllocationsPrinter
{
    public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
    {
        ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
        wsSheet1.Protection.IsProtected = false;
        int count = 0;
        int previouscount = 0;
        var position = 2;
        int startposition = 1;
        IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;

        foreach (var ag in allocation)
        {
            allocationGroup = ag.Select(a => a);
            var allocationList = allocationGroup.ToList();
            count = allocationList.Count();

            using (ExcelRange Rng = wsSheet1.Cells["A" + startposition + ":G" + (count + previouscount + 1)])
            {
                ExcelTableCollection tblcollection = wsSheet1.Tables;
                ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);

                //Set Columns position & name  
                table.Columns[0].Name = "Manager Strategy";
                table.Columns[1].Name = "Fund";
                table.Columns[2].Name = "Portfolio";
                table.Columns[3].Name = "As Of";
                table.Columns[4].Name = "EMV (USD)";
                table.Columns[5].Name = "Percent";
                table.Columns[6].Name = "Allocations";

                wsSheet1.Column(1).Width = 45;
                wsSheet1.Column(2).Width = 45;
                wsSheet1.Column(3).Width = 55;
                wsSheet1.Column(4).Width = 15;
                wsSheet1.Column(5).Width = 25;
                wsSheet1.Column(6).Width = 20;
                wsSheet1.Column(7).Width = 20;

                // table.ShowHeader = true;
                table.ShowFilter = true;
                table.ShowTotal = true;
                //Add TotalsRowFormula into Excel table Columns  
                table.Columns[0].TotalsRowLabel = "Total Rows";
                table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
                table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
                table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";

                table.TableStyle = TableStyles.Dark10;
            }

            foreach (var ac in allocationGroup)
            {
                wsSheet1.Cells["A" + position].Value = ac.MANAGER_STRATEGY_NAME;
                wsSheet1.Cells["B" + position].Value = ac.MANAGER_FUND_NAME;
                wsSheet1.Cells["C" + position].Value = ac.PRODUCT_NAME;
                wsSheet1.Cells["D" + position].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
                wsSheet1.Cells["E" + position].Value = ac.UsdEmv;
                wsSheet1.Cells["F" + position].Value = Math.Round(ac.GroupPercent,2);
                wsSheet1.Cells["G" + position].Value = Math.Round(ac.WEIGHT_WITH_EQ,2);
                position++;
            }
            position++;
            previouscount = position;
            // position = position + 1;
            startposition = position;
            position++;
        }
    }
}

Так выглядят данные при успешном их отображении

enter image description here

1 Ответ

4 голосов
/ 15 марта 2019

Ваша проблема полностью в вашем Print методе. Вы были укушены созданием слегка усложненного механизма отслеживания строк и сочетания его с магическими числами. Это заставляет вас размещать каждую таблицу после первой строки выше, чем она должна быть. Заголовок и промежуточные итоги не являются частью таблицы, поэтому у вас есть пара возможностей для ошибки. Как вы видели, таблицы не могут перекрываться, поэтому EPPlus начинает лаять на вас после того, как вы исчерпали свою свободу действий.

Все, что вам нужно сделать, это отслеживать текущую строку, в которую вы пишете, и учитывать пространство, занимаемое заголовком и нижним колонтитулом таблицы (промежуточными итогами), если вы их используете.

Вы заявляете следующее:

int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;

Но для записи в правильный ряд все, что вам нужно, это:

var rowNumber = 1;

Это правильно начнет запись ваших данных в первой строке листа Excel. Когда вы пишете строки таблицы, вы будете отслеживать и увеличивать только rowNumber. Но как насчет верхнего и нижнего колонтитула каждой таблицы? Если вы начнете писать в первой строке таблицы, вы перезапишете верхний колонтитул, а если вы не учтете ни верхний, ни нижний колонтитулы, у вас начнутся коллизии, как вы видели. Итак, давайте сделаем это:

var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);

Это довольно очевидно, вы будете использовать rowAdders для переключения верхнего или нижнего колонтитула, когда это необходимо. rowNumber всегда будет вашей текущей строкой для создания таблицы и записи ваших данных. Вы используете count при определении таблицы, но мы сделали ее неактуальной для чего-либо еще, поэтому мы переместили ее:

var allocationList = allocationGroup.ToList();

//Moved here
var count = allocationList.Count();

Ваше заявление об использовании становится:

using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])

Далее, это не упоминается в вашем посте, но вы столкнетесь с проблемой следующего:

ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);

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

var rowNumber = 1;
var tableIndex = 0;

//...
foreach (var ag in allocation)
{
    tableIndex += 1;
    //...
}

И используйте его для обеспечения уникальных имен таблиц:

ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);

Мы используем переменные управления форматом:

// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;

//Changes to
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;

У вас есть небольшая опечатка здесь:

table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";

//Should be:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,[Allocations])";

После того, как вы закончили определение таблицы, вы начинаете записывать свои данные с циклом foreach. Чтобы предотвратить перезапись заголовка таблицы, если он существует, нам нужно продвинуться на одну строку. Мы также должны продвинуть одну строку для каждого FIRMWIDE_MANAGER_ALLOCATION. Если вы используете промежуточные итоги, нам нужно продвинуться на одну строку после завершения цикла, чтобы правильно расположить следующую таблицу:

rowNumber += rowAdderForHeader; 
foreach (var ac in allocationGroup)
{
    //...
    rowNumber += 1;
}
rowNumber += rowAdderForFooter;

И это все. Теперь мы должным образом отслеживаем нашу позицию, используя только одну переменную, и при необходимости изменяем позицию, если на вашем столе есть верхний или нижний колонтитул.

Ниже приведен полный рабочий пример, который можно запустить в LinqPad, если вы добавите пакет EPPlus через Nuget. Он создает случайное количество групп распределений, каждое из которых имеет случайное количество распределений, а затем экспортирует их. Измените путь к выходному файлу, чтобы он работал для вас:

void Main()
{
    var dataGenerator = new DataGenerator();
    var allocations = dataGenerator.Generate();
    var xlFile = new FileInfo(@"d:\so-test.xlsx");

    if (xlFile.Exists)
    {
        xlFile.Delete();
    }

    using(var xl = new ExcelPackage(xlFile))
    {
        FundAllocationsPrinter.Print(xl, allocations);
        xl.Save();
    }
}

// Define other methods and classes here

public static class FundAllocationsPrinter
{
    public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
    {
        ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
        wsSheet1.Protection.IsProtected = false;

        IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;

        var rowNumber = 1;
        int tableIndex = 0;

        var showFilter = true;
        var showHeader = true;
        var showTotals = true;
        var rowAdderForHeader = Convert.ToInt32(showHeader);
        var rowAdderForFooter = Convert.ToInt32(showTotals);

        foreach (var ag in allocation)
        {
            tableIndex += 1;
            Console.WriteLine(tableIndex);

            allocationGroup = ag.Select(a => a);
            var allocationList = allocationGroup.ToList();
            var count = allocationList.Count();

            using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
            {
                ExcelTableCollection tblcollection = wsSheet1.Tables;
                ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);

                //Set Columns position & name  
                table.Columns[0].Name = "Manager Strategy";
                table.Columns[1].Name = "Fund";
                table.Columns[2].Name = "Portfolio";
                table.Columns[3].Name = "As Of";
                table.Columns[4].Name = "EMV (USD)";
                table.Columns[5].Name = "Percent";
                table.Columns[6].Name = "Allocations";

                wsSheet1.Column(1).Width = 45;
                wsSheet1.Column(2).Width = 45;
                wsSheet1.Column(3).Width = 55;
                wsSheet1.Column(4).Width = 15;
                wsSheet1.Column(5).Width = 25;
                wsSheet1.Column(6).Width = 20;
                wsSheet1.Column(7).Width = 20;

                table.ShowHeader = showHeader;
                table.ShowFilter = showFilter;
                table.ShowTotal = showTotals;
                //Add TotalsRowFormula into Excel table Columns  
                table.Columns[0].TotalsRowLabel = "Total Rows";
                table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
                table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
                table.Columns[6].TotalsRowFormula = "SUBTOTAL(109, [Allocations])";

                table.TableStyle = TableStyles.Dark10;
            }

            //Account for the table header
            rowNumber += rowAdderForHeader; 

            foreach (var ac in allocationGroup)
            {
                wsSheet1.Cells["A" + rowNumber].Value = ac.MANAGER_STRATEGY_NAME;
                wsSheet1.Cells["B" + rowNumber].Value = ac.MANAGER_FUND_NAME;
                wsSheet1.Cells["C" + rowNumber].Value = ac.PRODUCT_NAME;
                wsSheet1.Cells["D" + rowNumber].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
                wsSheet1.Cells["E" + rowNumber].Value = ac.UsdEmv;
                wsSheet1.Cells["F" + rowNumber].Value = Math.Round(ac.GroupPercent, 2);
                wsSheet1.Cells["G" + rowNumber].Value = Math.Round(ac.WEIGHT_WITH_EQ, 2);
                rowNumber++;
            }
            //Account for the table footer
            rowNumber += rowAdderForFooter;
        }
    }
}

public class FIRMWIDE_MANAGER_ALLOCATION
{
    public FIRMWIDE_MANAGER_ALLOCATION(string name, Random rnd)
    {
        Name = name;
        MANAGER_STRATEGY_NAME = "strategy name";
        MANAGER_FUND_NAME = "fund name";
        PRODUCT_NAME = "product name";
        EVAL_DATE = DateTime.Now;
        UsdEmv = (decimal)rnd.NextDouble() * 100000000;
        GroupPercent = (decimal)rnd.NextDouble() * 100;
        WEIGHT_WITH_EQ = 0;
    }

    public string Name { get; set; }
    public string MANAGER_STRATEGY_NAME { get; set; }
    public string MANAGER_FUND_NAME { get; set; }
    public string PRODUCT_NAME { get; set; }
    public DateTime EVAL_DATE { get; set; }
    public decimal UsdEmv { get; set; }
    public decimal GroupPercent { get; set; }
    public decimal WEIGHT_WITH_EQ { get; set; }
}

public class DataGenerator
{
    public static Random rnd = new Random();

    public ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> Generate()
    {
        var data = new List<FIRMWIDE_MANAGER_ALLOCATION>();
        var itemCount = rnd.Next(1, 100);

        for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
        {
            var name = Path.GetRandomFileName();
            data.AddRange(GenerateItems(name));
        }
        return data.ToLookup(d => d.Name, d => d); 
    }

    private IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> GenerateItems(string name)
    {
        var itemCount = rnd.Next(1,100);
        var items = new List<FIRMWIDE_MANAGER_ALLOCATION>();

        for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
        {
            items.Add(new FIRMWIDE_MANAGER_ALLOCATION(name, rnd));
        }
        return items;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...