Este artigo foi traduzido por máquina.

ASP.NET extremo

Gráfico com ASP.NET E LINQ

K. Scott Allen

Download do código disponível na Galeria de código do MSDN
Procure o código on-line

Conteúdo

Guia de Introdução
Criando gráfico
Colocando dados
De gráficos para painéis
Representando o futuro

Recentemente a Microsoft lançou um novo controle gráfico para o SP1 do ASP.NET 3.5. O controle do gráfico é fácil de usar e permite que você adicione visualizações de dados atraente a seus aplicativos da Web do ASP.NET. O controle suporta todos os tipos, como gráficos de linhas e os gráficos de pizza, bem como visualizações avançadas como gráficos funil e pirâmide gráfico padrão. Nesta coluna, VOU explorar o controle do gráfico e gerar alguns dados usando consultas escritas para LINQ para objetos. O código-fonte completo desta coluna está disponível da Galeria de código do MSDN.

fig01.gif

Figura 1 Visual Studio Tools

Guia de Introdução

A primeira etapa é parabaixar o controle de gráficopartir do Centro de Download da Microsoft. Este download instala os componentes de runtime chave que você precisará para gráficos, incluindo o posicionamento do assembly System.Web.DataVisualization no cache global de assemblies.

Você também precisará baixar oO Visual Studio 2008 ferramenta de suportee osite de exemplo de gráfico. O suporte de ferramenta fornecerá integração de caixa de ferramentas e suporte do IntelliSense em tempo de design, enquanto o site de exemplo fornecerá centenas de exemplos para examinar de inspiração para criar o tipo de gráfico desejado. Observe que será necessário Service Pack 1 instalado para o tempo de execução do Microsoft .NET Framework 3.5 e Visual Studio 2008.

Após a conclusão todos as instalações, você deve consiga criar um novo projeto do ASP.NET no Visual Studio e localizar o controle do gráfico na janela de caixa de ferramentas (veja a Figura 1 ). Você pode arrastar o organograma em um arquivo ASPX usando o modo de exibição design (que o tornará algumas alterações de configuração necessárias para o seu arquivo web.config) ou trabalhar com o controle diretamente na fonte de exibição de um arquivo ASPX (nesse caso você precisará alterar manualmente a configuração descreverei em breve).

Quando você coloca um gráfico para o designer de formulários da Web, o Visual Studio irá colocar uma nova entrada para a seção <controls> do seu arquivo web.config, assim:

<add tagPrefix="asp" 
     namespace="System.Web.UI.DataVisualization.Charting"
     assembly="System.Web.DataVisualization, 
               Version=3.5.0.0, Culture=neutral, 
               PublicKeyToken=31bf3856ad364e35" />

Essa entrada permite que você usar o controle do gráfico com o prefixo de marca asp familiares que usam outros controles ASP.NET internos. O Visual Studio também adiciona uma nova entrada de manipulador HTTP para o IIS 7.0 <handlers> seção e uma entrada semelhante para a seção <httphandlers> (para uso com o IIS 6.0 e o servidor de desenvolvimento da Web do Visual Studio):

<add name="ChartImageHandler" 
     preCondition="integratedMode" 
     verb="GET,HEAD"
     path="ChartImg.axd"
     type="System.Web.UI.DataVisualization.Charting.Chart­           HttpHandler, 
           System.Web.DataVisualization, 
           Version=3.5.0.0, Culture=neutral,
           PublicKeyToken=31bf3856ad364e35" />

Esse manipulador HTTP é responsável pelo processamento de solicitações que chegam ChartImg.axd, que é o ponto de extremidade padrão que irá usar o controle de gráfico para servir de gráficos. Irá fornecer mais detalhes sobre o manipulador HTTP mais tarde. O código na Figura 2 mostra os elementos básicos de um gráfico. Cada gráfico inclui pelo menos um objeto de série preenchido com dados. A propriedade ChartType de cada série será determinar o tipo de gráfico usado para plotagem da série (o tipo padrão é um gráfico de colunas). Cada gráfico também pode conter um ou mais objetos ChartArea onde plotagem ocorrerá.

A Figura 2 A básica gráfico

<asp:Chart ID="Chart1" runat="server" Height="300" Width="400">
    <Series>
        <asp:Series BorderColor="180, 26, 59, 105">
            <Points>
                <asp:DataPoint YValues="45" />
                <asp:DataPoint YValues="34" />
                <asp:DataPoint YValues="67" />
            </Points>
        </asp:Series>
    </Series>
    <ChartAreas>
        <asp:ChartArea />
    </ChartAreas>
</asp:Chart>

Você pode personalizar quase cada elemento visual do controle de gráfico ASP.NET, incluindo planos de fundo, eixos, títulos, legendas e rótulos. O controle do gráfico é tão altamente personalizável que você deve ter um plano local para manter os gráficos no seu aplicativo procurando consistente. Nesta coluna, VOU usar uma estratégia de construtor para aplicar fontes consistentes e cores para todos os gráficos. Essa classe de construtor irá permitir o uso do controle gráfico fora limites de uma página ASP.NET.

Criando gráfico

Procurando por dados de exemplo para usar esta coluna, eu rapidamente considerado usando os dados históricos de mercado de ações, mas os dados do ano passado tem sido deprimente, em vez disso portanto, que decidi usar dados dos Estados Unidos Bureau de transporte estatísticas (bts.gov). O aplicativo de exemplo para esta coluna inclui um arquivo com as informações sobre cada vôo doméstica originadas de minha casa aeroporto (Baltimore Washington internacionais ou BWI) durante janeiro de 2008. Os dados incluem a cidade de destino, distância, taxiing horários e atrasos. Este dados são representados em C# usando a classe na Figura 3 .

A Figura 3 Flight dados

public class Flight {
    public DateTime Date { get; set; }
    public string Airline { get; set; }
    public string Origin { get; set; }
    public string Destination { get; set; }
    public int TaxiOut { get; set; }
    public int TaxiIn { get; set; }
    public bool Cancelled { get; set; }
    public int ArrivalDelay { get; set; }
    public int AirTime { get; set; }
    public int Distance { get; set; }
    public int CarrierDelay { get; set; }
    public int WeatherDelay { get; set; }
}

O primeiro gráfico criei esses dados era um gráfico para mostrar os destinos mais comuns para vôos do aeroporto de Baltimore (consulte a Figura 4 ). Este gráfico criado usando muito pouco código no arquivo ASPX ou seu arquivo code-behind associado. O arquivo ASPX inclui um controle de gráfico na página, mas apenas define as propriedades de largura e altura, como você pode ver:

  <form id="form1" runat="server">
  <div>
    <asp:Chart runat="server" Width="800" Height="600" ID="_chart">
    </asp:Chart>
  </div>
</form>

fig04.gif

A Figura 4 destinos de início (clique na imagem para aumentar a exibição)

O code-behind delega todos o evento Page_Load tratamento de trabalho para uma classe TopDestinationsChartBuilder, como visto aqui:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        var builder = new TopDestinationsChartBuilder(_chart);
        builder.BuildChart();
    }
}

O TopDestinationsChartBuilder herda de uma classe ChartBuilder. Esta classe de base ChartBuilder usa um padrão de design de método de modelo para montar os pedaços de um gráfico. O método modelo especifica o algoritmo básico necessário para produzir um gráfico aesthetically consistente e funcional, mas fornece conexões para uma subclasse personalizar as partes montadas. O método de modelo é denominado BuildChart, e é mostrada na Figura 5 .

A Figura 5 BuildChart

public void BuildChart() {
    _chart.ChartAreas.Add(BuildChartArea());
    _chart.Titles.Add(BuildChartTitle());
    if (_numberOfSeries > 1) {
        _chart.Legends.Add(BuildChartLegend());
    }
    foreach (var series in BuildChartSeries()) {
        _chart.Series.Add(series);
    }
}

Cada método de criação na classe ChartBuilder tem um método de personalizar associado. Depois que o método de compilação foi construído sua parte do gráfico, ele chama o método Customize. Uma classe derivada pode substituir o método de personalização para aplicar configurações específicas do gráfico. Um exemplo é o método de BuildChartTitle, mostrado na Figura 6 . A classe TopDestinationsChartBuilder substitui o CustomizeChartTitle para aplicar o texto específico para um título:

protected override void  CustomizeChartTitle(Title title)
{
    title.Text = "Top Destinations";     
}

A Figura 6 BuildChartTitle

private Title BuildChartTitle() {
    Title title = new Title() {
        Docking = Docking.Top,
        Font = new Font("Trebuchet MS", 18.0f, FontStyle.Bold),
    };
    CustomizeChartTitle(title);
    return title;
}

protected virtual void CustomizeChartTitle(Title title) { }

A maioria do trabalho para o TopDestinationsChartBuilder é dedicada à personalização a série de gráficos, que inclui a adição de todos os pontos de dados para exibição. Felizmente, localizar as cidades de destino cinco superior de uma coleção de objetos de Flight é absurdamente fácil usando LINQ to Objects, como você pode ver na Figura 7 . O código primeiro se aplica a operador de GroupBy do LINQ para agrupar os vôos por sua cidade de destino. O operador OrderByDescending, em seguida, classifica a seqüência de agrupamentos pelo número de vôos em cada grupo. Finalmente, o código usa o operador de serem para identificar os destinos de cinco principais da seqüência.

Figura 7 Get primeiros cinco cidades

protected override void CustomizeChartSeries(IList<Series> seriesList) {
    var repository = FlightRepositoryFactory.CreateRepository();
    var query = repository.Flights
                          .GroupBy(flight => flight.Destination)
                          .OrderByDescending(group => group.Count())
                          .Take(5);
    Series cities = seriesList.Single();
    cities.Name = "Cities"; 
    foreach (var record in query) {
        cities.Points.AddXY(record.Key, record.Count());
    }
}

A consulta LINQ produz uma seqüência de agrupamentos. Você pode fazer um loop através desses agrupamentos para adicionar informações ao gráfico. A propriedade Key de cada agrupamento representa o valor da chave selecionada no operador GroupBy (no caso, o valor de cidade destino). O código usa o destino como o valor de X para cada ponto de dados. Cada agrupamento também contém uma seqüência enumeráveis dos objetos Flight-agrupado sob este destino. Você só precisará usar o operador de contagem para obter o número total de vôos para cada destino e adicionar a contagem de como o valor de Y para cada ponto de dados.

Colocando dados

Em vez de adicionar dados a um ponto de uma série de gráfico uma vez, você pode usar um método DataBindXY em DataPointCollection do gráfico para inserir uma seqüência de pontos de dados sem usar um loop. Você pode ver isso acontecer em DelaysByDayChartBuilder, que calcula o total de todos os atrasos (em minutos) para cada dia do mês. Esse construtor também produz uma segunda série de dados mostrando os atrasos de relacionados ao tempo totais. O construtor começa com uma consulta LINQ que agrupa os vôos até o dia do mês. A propriedade de chave para cada agrupamento agora representa o dia do mês (de 1 a 31 de janeiro):

var query = repository.Flights
                              .GroupBy(flight => flight.Date.Day)
                              .OrderBy(group => group.Key)
                              .ToList();

O código na Figura 8 usa o método DataBindXY. Primeiro, todos os valores X são coletados em uma lista usando o operador Select para capturar o valor de chave de cada grupo. Em seguida, adicional processamento é aplicado à consulta de agrupamento inicial para somar os valores ArrivalDelay e WeatherDelay dentro de cada grupo.

A Figura 8 inserir valores sem loop

var xValues = query.Select(group => group.Key).ToList();

totalDelaySeries.Points.DataBindXY( 
        xValues,
        query.Select(
            group => group.Sum(
                flight => flight.ArrivalDelay)).ToList());

weatherDelaySeries.Points.DataBindXY(
        xValues,
        query.Select(
            group => group.Sum(
                flight => flight.WeatherDelay)).ToList());

Como você pode ver, os operadores LINQ padrão tornam mais fácil separar e manipular os dados para relatórios e gráficos. Outro ótimo exemplo está na TaxiTimeChartBuilder. Essa classe cria um gráfico de radar para exibir o tempo total "Taxi check-out" para cada dia da semana. O tempo de "Taxi check-out" é o período de tempo que avião gasta entre deixar a porta e levantar fora da pista de pouso a aeroporto. Em aeroportos congestionados o tempo de "Taxi check-out" pode soar como planes fila backup e aguarde até a pista de pouso limpar. O TaxiTimeChartBuilder realça os pontos de dados onde o tempo de "táxi check-out" exceder um valor de limite. Esse trabalho é feito muito fácil usando uma consulta LINQ contra os pontos de dados em uma série:

var overThresholdPoints = 
    taxiOutSeries.Points
                 .Where(p => p.YValues.First() > _taxiThreshold);
foreach (var point in overThresholdPoints)
{
    point.Color = Color.Red;
}

Aqui a cor de cada ponto de dados que excedem o limite especificado é alterada para vermelho. Esse comportamento pode fazer você pensar um relatório de Painel do aeroporto, portanto, vamos examinar os painéis em seguida.

De gráficos para painéis

Em círculos de inteligência comercial, os painéis são usados para exibir informações de chave de desempenho sobre uma empresa de forma gráfica. Essas informações de desempenho podem ser provenientes de várias fontes e podem ser visualizadas usando uma série de gráficos e widgets. Um usuário deve ser capaz de Observe uma exibição de painel e identificar rapidamente qualquer anomalias que podem afetar os negócios.

Um dos desafios na produção de uma exibição de painel é desempenho. Montar todas as informações para um painel podem resultar em uma infinidade de consultas a bancos de dados relacionais e multidimensionais. Mesmo quando os dados brutos para um painel está em cache, o grande número de gráficos e widgets em um painel pode burden um servidor.

a Figura 9 mostra um painel simples para um aeroporto, exibindo a destinos superior, taxi hora por dia da semana, vôos totais para cada dia da semana e o tempo de atraso total para cada dia do mês. Uma abordagem para criar este painel seria colocar todos os controles de gráfico quatro em uma única página da Web e usar as classes de construtor gráfico nós tiver sido criando para montar os gráficos. No entanto, imagine se cada gráfico necessário dois segundos criar. Normalmente, dois segundos não é muito tempo para aguardar uma consulta complicada, mas como os têm um total de quatro gráficos na página (que é um número pequeno de um painel), o usuário irá esperar pelo menos oito segundos para o primeiro gráfico seja exibido.

fig09.gif

A Figura 9 aeroporto atividade Dashboard

Uma abordagem diferente para criar esta página de painel é definir espaços reservados para cada gráfico na página e criar as imagens do gráfico de forma assíncrona. Uma abordagem assíncrona deve permitir que o usuário começar vendo o primeiro resultado assim que ele estiver disponível. Essa solução pode aproveitar proxies do WCF (Windows Communication Foundation) em JavaScript para exibir gráficos medida que forem disponibilizadas.

Felizmente, as classes de construtor que você definiu para os gráficos tornam mais fácil mover a lógica de geração de gráfico por trás de WCF Web services. O código na Figura 10 mostra um método de serviço WCF que primeiro cria um gráfico vazio e, em seguida, constrói uma das classes derivadas ChartBuilder implementadas no projeto. O construtor é especificado como um parâmetro para o método, e o código verifica esse parâmetro em um mapa dos desenvolvedores conhecidos para localizar o tipo real de construtor para instanciar (usando Activator.CreateInstance).

A Figura 10 um construtor de instância

[OperationContract]
public string GenerateChart(string builderName) {
    Chart chart = new Chart() {
        Width = 500, Height = 300
    };

    ChartBuilder builder = 
        Activator.CreateInstance(_typeMap[builderName], chart)
            as ChartBuilder;
    builder.BuildChart();

    return SaveChart(builderName, chart);
}

Usando um mapa dos tipos de construtor conhecidos significa não temos que passar o método um nome de tipo real e ele também fornece uma camada das contra entradas mal-intencionadas de através da Internet. Nós só será instanciar tipos que o serviço sabe sobre.

A imagem do gráfico no serviço da Web de produção é complicado. Como mencionamos anteriormente, o gráfico funciona com um manipulador HTTP para processar os gráficos no cliente. Especificamente, o controle de gráfico oferece a classe ChartHttpHandler os bytes que representa a imagem concluído do gráfico. O ChartHttpHandler responde com um identificador exclusivo. Quando o controle de gráfico processa, ela produz uma regular HTML <img> marca com o atributo src referência ChartImg.axd e incluindo o identificador exclusivo na seqüência de caracteres de consulta. Quando esta solicitação de imagem atinge o manipulador HTTP, o manipulador pode pesquisar o gráfico adequado para exibição. Você pode ler mais detalhes sobre esse processo, incluindo todas as opções de configuração, no Blog do delian Tchoparino.

Infelizmente, as APIs para o manipulador de HTTP não são públicas e, portanto, não estão disponíveis em um serviço da Web. Em vez disso, o método SaveChart, que chama o código de serviço na Figura 10 , usa o método SaveImage do controle de gráfico para gravar a imagem do gráfico para o sistema de arquivos no formato PNG. O serviço, em seguida, retorna o nome do arquivo para o cliente. Gerando arquivos físicos também pode apresentar uma estratégia de cache e evitar as consultas e geração de imagem durante os períodos de carga pesada.

O código na Figura 11 usa o serviço WCF do JavaScript para definir o atributo src do espaço reservado de imagem do gráfico. (Consulte a coluna de Fritz Onion" Chamadas de serviço da Web do cliente com extensões AJAX" para compreender como gerar proxies JavaScript e chamar serviços da Web com JavaScript.) O documento objeto modelo (DOM Document Object Model) identificador e o construtor de classe para cada gráfico é definida em uma matriz de JavaScript que é parte de um objeto de "contexto". Neste contexto é encapsulado por meio de sucessivas chamadas de serviço da Web no parâmetro userState dos métodos de proxy do serviço da Web. O JavaScript usa o objeto de contexto para acompanhar seu progresso em atualizar os gráficos de painel. Para páginas de painel dinâmico, o servidor dinamicamente foi possível gerar a matriz.

A Figura 11 atualizando o gráfico

/// <reference name="MicrosoftAjax.js" />
function pageLoad() {
    var context = {
        index: 0,
        client: new ChartingService(),
        charts: 
        [
            { id: "topDestinations", builder: "TopDestinations" },
            { id: "taxiTime", builder: "TaxiTime" },
            { id: "dayOfWeek", builder: "DayOfWeek" },
            { id: "delaysByDay", builder: "DelaysByDay" }
        ]
    };

    context.client.GenerateChart(
        context.charts[context.index].builder,
        updateChart,
        displayError,
        context);
}

function updateChart(result, context) {
    var img = $get(context.charts[context.index].id);
    img.src = result;
    context.index++;
    if (context.index < context.charts.length) {
        context.client.GenerateChart(
            context.charts[context.index].builder,
            updateChart, displayError, context);
    }
}

function displayError() {
    alert("There was an error creating the dashboard charts");
}

Representando o futuro

Abordei um pouco da tecnologia, incluindo o modelo padrão de design de método, operadores LINQ e compatível com JavaScript WCF Web services. Como esta coluna demonstrado apenas uma pequena fração dos recursos disponíveis no controle do gráfico, você deve estar se esqueça de verificar o site de exemplo para ver a variedade surpreendente das recursos disponíveis. Combinar essa ferramenta de visualização grande com a flexibilidade e a expressividade do LINQ significa que os aplicativos gráficos futuros será mais flexível, eficiente e útil.

k. Scott Allen é um membro da equipe técnica Pluralsight e fundador da OdeToCode. Você pode acessar Scott em Scott@OdeToCode.com ou ler seu blog em odetocode.com/blogs/Scott.