Daily Archives: 2007.06.24. 19:10

Excel export ASP.NET-ből

Gyakori feladat, hogy a webalkalmazásaink adatbázisából valamilyen használható formátumban kell adatokat exportálnunk. Egy átlagos felhasználó számára a relációs adatok használható formátuma az Excel. Íme néhány módszer Excel fájl előállítására ASP.NET-ből:

1. OLE DB

Ennél a módszernél kihasználjuk, hogy az Excel OLE DB provideren keresztül elérhető, a connection string valami ilyesmi lesz:

    private string _connectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=MyFile.xls;Extended Properties=""Excel 8.0;""";

Miután rányitottunk OleDbConnectionnel, az OleDbCommand osztály segítségével szórhatjuk a SELECT-eket, INSERT-eket, DELETE-eket és UPDATE-eket. Minden egyes sheet táblaként érhető el.

Ennek a megközelítésnek az előnye, hogy előre elkészíthetünk és beformázhatunk egy Excel fájlt, a beleírt adatok nem rontják el a formázást. Persze ne teljes formázásra tessék gondolni, tipikusan felesleges sheetek kiírtása, magyarázó szövegek mellékelése, fejléc értelmes formában történő megadása, néhány formázás stb.

A nagy hátránya, hogy ha egy általános DataSet -> Excel export osztályt akarunk készíteni, akkor meg fog gyűlni a bajunk az SQL utasítások összefűzésével, mert persze tárolt eljárásokról szó se lehet. Gyakorlatilag INSERT utasításokat kell majd összeállítanunk a DataSet minden egyes sorához, ahol leginkább a speciális karakterek escape-elésére és az egyes mezőtípusokra kell figyelni. Sajnos az ilyen általános osztályoknál tapasztalataink szerint a dátum például sztringként szokott megérkezni az Excelbe, aposztróffal prefixelve. Puff neki, csak nyomtatni lehet, formázni nem.

2. Excel objektum modell

Van az Excelnek egy gyönyörű objektum modellje, ami elérhető, ha referenciát adunk a Microsoft Excel 11.0 Object Library COM komponensre, majd bejúzingoljuk a Microsoft.Office.Interop.Excel névteret. Tessék vele nyugodtan játszani, egy darabig még akár élmény is lehet az Excel.Workbook és az Excel.Worksheet osztályok használata és teljesen jól is fog működni a fejlesztői gépünkön.

Ezzel a megoldással két igen nagy probléma van, ha ASP.NET-ből akarjuk használni:

  • A szerverre Excelt kell telepíteni, amihez licenc kell, telepíteni kell, frissíteni kell stb.
  • Az Excelt nem szerver oldali környezetbe tervezték, ezért nem is működik jól a szerveren. Tipikusan nem szereti, ha egyszerre több szálon matatják az objektumait és bizony könnyen ki lehet akasztani, arról nem is beszélve, hogy zabálja a memóriát.

Ergó ezt a megközelítést tessék elfelejteni ASP.NET környezetben. Pont.

3. XLSX fájl előállítása

Ha megengedhetjük magunknak, hogy Excel 2007-re optimalizálunk és megkövetelhetjük a szerveren a .NET Framework 3.0-t, akkor gyárthatunk XLSX fájlokat közvetlenül kódból. Ehhez tömöríteni kell tudni és nyers XML-t írni. A tömörítéshez még használhatjuk a WindowsBase.dll-ben lévő Packaging API-t (System.IO.Packaging névtér), de az Excel specifikus XML gyártáshoz nem marad más, mint a System.Xml névtér. Ez majdnem semmi, meg kell ismernünk a teljes Excel sémát.

Ez a megoldás jól működik szerver oldalon és Excel sem kell hozzá. Bár a Microsoft ezt nagyon favorizálja, nem fehér embernek való feladat, ráadásul tényleg csak Excel 2007-tel működik (bár van Compatibility Pack a 2003-hoz).

4. HTML előállítása

Ha nincs szükségünk minden Excel funkcióra, akkor trükközhetünk egy kicsit: kihasználhatjuk, hogy az Excel meg tud nyitni HTML fájlokat és felismeri bennük a táblázatokat. Gyárthatunk tehát szerver oldalon egyszerű HTML táblázatot, mert ha azt kliens oldalon Excellel nyitjuk meg, az eredmény majdnem ugyanaz, mintha közvetlenül Excel fájlt gyártottunk volna. Az alábbi kód pont erre szolgál:

    StringWriter sw = new StringWriter();
    HtmlTextWriter wr = new HtmlTextWriter( sw );

    wr.RenderBeginTag( HtmlTextWriterTag.H1 );
    wr.Write( "Ez itt a cím" );
    wr.RenderEndTag();

    GridView grid = new GridView();
    grid.DataSource = this.GetData();
    grid.DataBind();
    grid.HeaderStyle.Font.Bold = true;
    grid.RenderControl( wr );

    this.Response.Clear();
    this.Response.ContentType = "application/vnd.ms-excel";
    this.Response.Write( sw.ToString() );
    this.Response.End();

A megoldás előnye, hogy aránylag egyszerű, nem kell Excel a szerverre, sőt még a típusok is jól mennek át. A hátrány, hogy ez nyilván egy trükközés, nem tudunk minden Excel funkciót elérni, és az Excelben megnyitott HTML oldalban nem látszanak a cellarácsok.

Íme tehát négy megoldás, mindegyiknek vannak előnyei és hátrányai, lehet választani!

 

Technorati tags: ,

Helyi? Biztonságos?

A nagyobb osztálykönyvtárak hátránya, hogy nincs élő ember, aki elejétől a végéig képes lenne átlátni. Ezért aztán születnek olyan kódrészletek, melyek lényegesen egyszerűbben is megírhatóak lennének.

Az egyik gyakori eset annak lekérdezése, hogy SSL-en keresztül nézik-e az oldalt. A brute force megoldás valami ilyesmi:

    if( this.Request.Url.ToString().StartsWith( "https" ) )...

Badarság a System.Uri-t Stringgé alakítani, mikor az helyből szét tudja szedni az URL-t, tehát akár ezt is írhatjuk:

    if( this.Request.Url.Scheme == Uri.UriSchemeHttps )...

A legegyszerűbb azonban talán mégis ez:

    if( this.Request.IsSecureConnection )...

Meg lehet nézni Reflectorban, a FormsAuthentication osztály SetAuthCookie metódusa is ezt használja:

    if( !current.Request.IsSecureConnection && RequireSSL )
    {
        throw new HttpException( SR.GetString( "Connection_not_secure_creating_secure_cookie" ) );
    }

 

Amennyiben csak a HTTPS a kérdés, még alig van különbség a fenti sztring buheráláshoz szükséges karakterek száma és a legolvashatóbb megoldás karakterszáma között. Ámde a helyzet sokkal zűrösebb, ha azt kell vizsgálnunk, hogy helyi kapcsolaton keresztül nézik az oldalt, vagy távolról. A balga módszer nekiesni az URL-nek és megkeresni benne a localhostot. Ez még String.IndexOf hívással is szörnyű, de láttam már olyat, aki biztosra akart menni és ezért előbb a ":" mentén, aztán a "/" mentén felsplittelte az URL sztringet, majd a keletkező tömbök közül a megfelelőre nézett sztring egyezést. Téptem a hajam….

A kérdés eldöntéséhez elméletben meg kell vizsgálni, hogy az alábbi esetek közül igaz-e valamelyik:

  • A cél domain localhost.
  • A cél IP cím 127.0.0.1.
  • A cél IP cím a gép valamelyik IP címe.

Mivel a localhost címe 127.0.0.1, ezért ha IP cím alapján nézzük, akkor csak az a kérdés, hogy a kérés a 127.0.0.1-re, vagy a gép valamelyik címére érkezett-e.

Természetesen erre is van rövid megoldás a HttpRequest osztályban:

    if( this.Request.IsLocal )...

Elfogadhatóan rövid, nem? Nézzük meg Reflectorral, hogyan működik:

    public bool IsLocal
    {
        get
        {
            string userHostAddress = this.UserHostAddress;
            if( string.IsNullOrEmpty( userHostAddress ) )
            {
                return false;
            }
            return ( ( ( userHostAddress == "127.0.0.1" ) || 
( userHostAddress == "::1" ) ) ||
( userHostAddress == this.LocalAddress ) ); } }

Ez valóban jól működik az összes fenti esetre, de persze csak akkor, ha az ember sejti, hogy van ilyen tulajdonság, amit csak le kell kérdezni. Nincs mese, a legtöbb hasonló probléma esetén úgy kell hozzáállni a .NET Frameworkhöz, hogy van itt x ezer darab osztály, jó sokan írták és jó sok helyen használják, tutira megoldották már azt, amivel én csak most szembesültem. Ez nálam általában beválik, ha a megoldás nem az MSDN-ből, akkor a Google-ből kiderül.

 

Technorati tags:

Linkblog: SQL Server 2005 cikkek

Meg kell hagyni, SQL Server 2005 témakörben vannak nagyon jó cikkek nem csak blogokban, hanem az MSDN oldalain is, ami többek között annak köszönhető, hogy olyanok írják őket, mint Kimberly "SQL Goddess" Tripp. Aki esetleg nem ismerné, ezen a címen találhat sok whitepapert, továbbá a teljesség kedvéért idemásolom az SQL Server 2005-tel foglalkozó microsoft.com/sql oldal, az MSDN és a TechNet cikkeket összegyűjtő oldalak címeit is.

 

Technorati tags: ,