Share via


ASP.NET extremos

Gráficos con ASP.NET Y LINQ

K. Scott Allen

Descarga de código de la Galería de código de MSDN
Examinar el código en línea

Contenido

Introducción A
Crear gráfico
Bombeo de datos
En gráficos de escritorio digital
Gráficos en el futuro

Microsoft lanzó recientemente un nuevo control gráficos para ASP.NET 3.5 SP1. El control del gráfico es fácil de usar y le permite agregar las visualizaciones de datos interesantes a las aplicaciones Web ASP.NET. El control admite todos los tipos de gráficos estándar, como los gráficos de líneas y los gráficos circulares, así como las visualizaciones avanzadas como los gráficos de embudo y pirámide de. En esta columna, VOY a explorar el control de gráfico y generar algunos datos de uso de consultas escritas para LINQ a objetos. El código fuente completo para esta columna está disponible desde la galería de código de MSDN.

fig01.gif

Figura 1 de Visual Studio Tools

Introducción A

El primer paso es descargar el control de gráfico desde el Centro de descarga de Microsoft. Esta descarga instala los componentes de clave en tiempo de ejecución que necesitará para gráficos, incluidos el selección de ubicación del ensamblado System.Web.DataVisualization en la caché de ensamblados global.

También deseará descargar la Soporte técnico de herramientas de Visual Studio 2008 y la gráficos de sitio Web de ejemplo . La compatibilidad de herramientas le proporcionará integración de cuadro de herramientas y compatibilidad con IntelliSense en tiempo de diseño, mientras que el sitio Web de ejemplo obtendrá cientos de ejemplos que se examinará para inspiración a crear el tipo de gráfico que necesita. Tenga en cuenta que tendrá instalado para Microsoft .NET Framework 3.5 runtime y Visual Studio 2008 el Service Pack 1.

Una vez realizadas todas las instalaciones, debería poder crear un nuevo proyecto de ASP.NET en Visual Studio y busque el control gráfico en la ventana de cuadro de herramientas (consulte la figura 1 ). Puede arrastrar el gráfico en un archivo de ASPX mediante la vista Diseño (que se hacer algunos cambios de configuración necesarios en el archivo web.config) o trabajar con el control directamente en el origen de vista de un archivo ASPX (en cuyo caso deberá manualmente realizar los cambios de configuración describirá en breve).

Al colocar un gráfico en el diseñador de formularios Web, Visual Studio colocará una nueva entrada en la sección <controls> de su archivo web.config, así:

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

Esta entrada permite utilizar el control de gráfico con el prefijo de etiqueta asp familiar que utilizan otros controles ASP.NET integrados. Visual Studio también agrega una nueva entrada de controlador HTTP a la 7.0 de IIS <handlers> sección y una entrada similar en la sección <httphandlers> (para su uso con IIS 6.0 y el servidor de desarrollo Web de 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" />

Este controlador HTTP es responsable de procesar las solicitudes que llegan a para ChartImg.axd, que es el extremo del valor predeterminado que el control de gráfico utilizará para atender los gráficos. Le situará en más detalles sobre el controlador HTTP más adelante. El código en la figura 2 muestra los elementos básicos de un gráfico. Cada gráfico incluye al menos un objeto de series rellena con datos. La propiedad ChartType de cada serie determinará el tipo de gráfico utilizado para trazar la serie (el tipo predeterminado es un gráfico de columnas). Cada gráfico también puede contener uno o varios objetos ChartArea donde se producirá el trazado.

Figura 2 un gráfico básico

<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>

Puede personalizar casi todos los elementos visuales del control de gráfico ASP.NET, incluidos fondos, ejes, títulos, leyendas y rótulos. El control de gráfico es tan altamente personalizable que debe tenga un plan lugar para conservar los gráficos en su aplicación aspecto coherente. En esta columna, VOY a utilizar una estrategia de generador para aplicar coherentes fuentes y colores a todos los gráficos. Esta clase de generador también permitirá el uso del control para la creación de gráficos fuera de los límites de una página ASP.NET.

Crear gráfico

En busca de datos de ejemplo para utilizarlos en esta columna, considera brevemente utilizando los datos históricos del mercado de valores, pero los datos desde el año pasado ha sido deprimente, en lugar de por lo que ello que decidió utilizar datos de los Estados Unidos de transporte estadísticas (bts.gov). La aplicación de ejemplo para esta columna incluye un archivo con información sobre cada vuelo nacional procedentes de mi aeropuerto principal (Baltimore (Washington) internacional o BWI) durante enero de 2008. Los datos incluyen la ciudad de destino, distancia, veces taxiing y retrasos. Estos datos se representan en C# mediante la clase en la figura 3 .

Datos de la caja negra de la figura 3

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; }
}

El primer gráfico creé a partir de estos datos fue un gráfico para mostrar los destinos más populares para vuelos desde el aeropuerto de Baltimore (consulte la figura 4 ). Este gráfico se crea mediante muy poco código en el archivo ASPX o su archivo de código subyacente asociado. El archivo ASPX incluye un control de gráfico en la página, pero sólo establece las propiedades Width y Height, tal como puede ver:

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

fig04.gif

La figura 4 destinos de la parte superior (haga clic en la imagen de una vista más grande)

El código subyacente delega todos su evento Page_Load tratamiento de trabajo para una clase TopDestinationsChartBuilder, como puede ver aquí:

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

El TopDestinationsChartBuilder hereda de una clase de ChartBuilder. Esta clase de base de ChartBuilder utiliza un modelo de diseño del método de plantilla para ensamblar las piezas de un gráfico. El método de la plantilla especifica el algoritmo básico necesaria para generar un gráfico aesthetically coherente y funcional, pero proporciona enlaces de una subclase personalizar los componentes del ensamblado. El método de plantilla se denomina BuildChart, y se muestra en la figura 5 .

La 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 generación de la clase ChartBuilder tiene un método de personalizar asociado. Una vez que el método de generación ha construido su parte de gráfico, llama al método personalizar. Una clase derivada puede reemplazar el método de personalización para aplicar la configuración específica de gráfico. Un ejemplo es el método de BuildChartTitle, que se muestra en la figura 6 . La clase TopDestinationsChartBuilder reemplaza la CustomizeChartTitle para aplicar el texto concreto para un título:

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

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) { }

La mayor parte del trabajo de la TopDestinationsChartBuilder está dedicada a la personalización de la serie del gráfico, que incluye agregar todos los puntos de datos para mostrar. Por fortuna, la buscar destino cinco ciudades de una colección de objetos de la caja negra es ridiculously sencillo mediante LINQ to Objects, como puede ver en la figura 7 . El código en primer lugar aplica operador de GroupBy de LINQ para agrupar los vuelos por su ciudad de destino. El operador OrderByDescending ordena a continuación, la secuencia de agrupaciones por el número de vuelos en cada grupo. Por último, el código utiliza el operador realizar para identificar los destinos de cinco superiores de la secuencia.

Figura 7 obtener superior cinco Cities

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());
    }
}

La consulta LINQ produce una secuencia de agrupaciones. Puede recorrer estos grupos para agregar información al gráfico. La propiedad de clave de cada agrupación representa el valor de la clave seleccionada en el operador GroupBy (en este caso, el valor de la ciudad de destino). El código utiliza el destino como el valor de X para cada punto de datos. Cada grupo también contiene una secuencia enumerable de los objetos de caja negra que agrupa bajo este destino. Sólo tiene que utilizar el operador de cuenta para obtener el número total de vuelos a cada destino y agregar el número como el valor de Y para cada punto de datos.

Bombeo de datos

En lugar de agregar datos a un punto de una serie de gráfico a la vez, puede utilizar un método DataBindXY en DataPointCollection del gráfico para insertar una secuencia de puntos de datos sin utilizar un bucle. Puede ver esto suceda en DelaysByDayChartBuilder, que calcula el total de todos los retrasos (en minutos) para cada día del mes. Este generador también genera una segunda serie de datos que muestra los retrasos relacionados con el tiempo totales. El generador de comienza con una consulta LINQ que agrupa los vuelos por el día del mes. La propiedad Key para cada agrupación ahora representa el día del mes (de 1 a 31 de enero):

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

El código en la figura 8 utiliza el método DataBindXY. En primer lugar, todos los valores de X se recopilan en una lista utilizando el operador de selección para tomar el valor de clave de cada grupo. A continuación, más procesamiento se aplica a la consulta de grupo inicial para sumar los valores ArrivalDelay y WeatherDelay dentro de cada grupo.

Figura 8 insertar valores sin bucle

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 puede ver, los operadores LINQ estándar facilitan slice y manipular los datos para informes y gráficos. Otro ejemplo excelente es en la TaxiTimeChartBuilder. Esta clase ensambla un gráfico radial para mostrar la hora de total "taxi fuera" para cada día de la semana. El tiempo de "taxi fuera" es la cantidad de tiempo que dedica un avión entre dejando la puerta de entrada y levantamiento de fuera de la runway aeropuerto. En aeropuertos congestionados el tiempo de "taxi fuera" puede soar como planos cola hacia arriba y espere a la runway borrar. El TaxiTimeChartBuilder resalta los puntos de datos que el tiempo "taxi fuera" excede algún valor de umbral. Este trabajo se realiza muy fácil mediante una consulta LINQ en los puntos de datos en una serie:

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

Aquí el color de cada punto de datos que excede el umbral especificado cambia a rojo. Este comportamiento podría hacer que piense un informe de panel aeropuerto, por lo que vamos a vistazo a los paneles a continuación.

En gráficos de escritorio digital

En los círculos de inteligencia empresarial, los paneles se utilizan para mostrar información de clave de rendimiento de una empresa de forma gráfica. Esta información de rendimiento puede proceder de varios orígenes y se puede visualizar mediante una serie de gráficos y dispositivos. Un usuario debe poder para glance en una pantalla de escritorio e identificar rápidamente las anomalías que puedan afectar a la empresa.

Uno de los desafíos de producir una visualización de escritorio es el rendimiento. Unir toda la información de un panel puede producir una multitud de consultas en bases de datos relacionales y multidimensionales. Incluso cuando se almacenan en caché los datos sin formato de un panel, el número total de gráficos y dispositivos en un escritorio digital puede burden un servidor.

la figura 9 muestra un panel simple de un aeropuerto mostrar los destinos superiores, el tiempo taxi por día de semana, vuelos totales para cada día de la semana y el tiempo de demora total para cada día del mes. Un enfoque para crear este panel sería colocar todos los controles de cuatro gráficos en una única página Web y utilizar las clases de generador de gráfico se se ha generar para ensamblar los gráficos. Sin embargo, imagine si cada gráfico requiere dos segundos generar. Normalmente, dos segundos no es un largo tiempo de espera para una consulta compleja, pero dado que tiene un total de cuatro gráficos en la página (que es un número pequeño de un panel), el usuario se se espera al menos ocho segundos para el primer gráfico para que aparezca.

fig09.gif

Figura 9 aeropuerto actividad de paneles

Un enfoque diferente para crear esta página de panel es definir los marcadores de posición de cada gráfico en la página y crear las imágenes de gráfico asincrónicamente. Un método asincrónico podría permitir al usuario empezar a ver el primer resultado tan pronto como está disponible. Esta solución podría aprovechar servidores proxy de Windows Communication Foundation (WCF) en JavaScript para mostrar gráficos cuando estén disponibles.

Afortunadamente, las clases de generador que ha definido para los gráficos facilitan mover la lógica de generación gráfico detrás de servicios Web de WCF. El código en la figura 10 muestra un método de servicio WCF que primero se crea un gráfico vacío y, a continuación, se construye una de ChartBuilder-derivada clases implementadas en el proyecto. El generador se especifica como un parámetro del método, y el código comprueba este parámetro con una asignación de elegir generadores conocidos para buscar el tipo real del generador para crear una instancia (con Activator.CreateInstance).

Figura 10 crear instancias de un generador

[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);
}

Utilizando una asignación de tipos conocidos generador significa no tenemos que pasa al método un nombre de tipo real, y también proporciona una capa de aislamientos con datos malintencionados de a través de Internet. Sólo se creará instancias de tipos que se conoce el servicio.

La imagen del gráfico en el servicio Web de producción es complicada. Como mencionamos anteriormente, el gráfico funciona con un controlador HTTP para procesar gráficos en el cliente. Específicamente, el control Chart proporciona la clase ChartHttpHandler los bytes que representa imagen terminado el gráfico. El ChartHttpHandler responde con un identificador único. Cuando el control de gráfico representa, genera un HTML normal <img> etiqueta con el atributo src hace referencia a ChartImg.axd e incluir el identificador único en la cadena de consulta. Cuando esta solicitud de la imagen alcanza el controlador HTTP, el controlador puede buscar el gráfico adecuado para su presentación. Puede leer más detalles acerca de este proceso, incluidas todas las opciones de configuración, en Blog de delian Tchoparino .

Por desgracia, las API para el controlador HTTP no son públicas y, por tanto, no están disponibles en un servicio Web. En su lugar, el método SaveChart, que invoca el código de servicio en la figura 10 , utiliza el método de SaveImage del control de gráfico para escribir la imagen del gráfico en el sistema de archivos en formato PNG. El servicio, a continuación, devuelve el nombre del archivo al cliente. Generando archivos físicos también se puede presentar una estrategia de almacenamiento en caché y evite la consultas y generación de imágenes durante los períodos de carga elevada.

El código en la figura 11 utiliza el servicio WCF desde JavaScript para establecer el atributo src del marcador de imagen de cada gráfico. (Consulte la columna Fritz Onion" Llamadas A de servicios de cliente de web con AJAX Extensions " para entender cómo generar el proxy de JavaScript y invocar servicios Web con JavaScript). Documento objeto modelo (DOM) identificador y el generador para cada gráfico se define la clase de una matriz de JavaScript que se parte de un objeto "contexto". En este contexto se túnel mediante sucesivas invocaciones de servicio Web en el parámetro userState de los métodos de proxy de servicios Web. El JavaScript usa el objeto de contexto para realizar un seguimiento su progreso en los gráficos de escritorio de actualización. Para páginas de panel dinámico, el servidor podría generar dinámicamente la matriz.

Figura 11 actualizar el 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");
}

Gráficos en el futuro

HE tratado bastante un poco de tecnología, incluidos el patrón de diseño de método de plantilla, los operadores LINQ y servicios Web de JavaScript de compatibles con WCF. Puesto que esta columna muestra sólo una pequeña fracción de las características disponibles en el control de gráfico, debe asegúrese de comprobar el sitio de Web de ejemplo para ver la amplitud sorprendente de características disponibles. Combinar esta herramienta de gran visualización con la flexibilidad y expressiveness de LINQ significa que las futuras aplicaciones gráficos se ser más flexible, eficaz y útil.

k. Scott Allen es un miembro del personal técnico de Pluralsight y fundador de OdeToCode. Pueden llegar a Scott a Scott@OdeToCode.com o leer su blog en odetocode.com/blogs/Scott.