Share via


Dokumentum-összeszerelés (2. rész)

Haladok tovább az első részben megkezdett feladattal: írok egy kis konzolalkalmazást, amely

  • lekérdezi az SQL Servertől a Northwind mintaadatbázis Customers táblájának rekordjait
  • az egyes rekordokat XML formátumúvá alakítja az előző cikkben készített dokumentum igényeinek megfelelően
  • bemásolja az XML-t a mintadokumentum másolatába, és megadott néven és helyre menti az új fájlt

Hogy izgalmasabb legyen, az Open XML mellett a LINQ (Language Integrated Query) technológiát is használom. A megoldáshoz ennélfogva a Visual Studio 2008-as verziójára van szükség.

CreateLetters.exe

Hozzunk létre egy Visual Basic konzolalkalmazást, és kereszteljük CreateLetters névre. A Main() eljárást írjuk át úgy, hogy két paramétert fogadjon (a sablonfájl elérési útja, a kész dokumentumok mappájának elérési útja), vagy írja ki a használat mikéntjét:

Sub Main(ByVal CmdArgs() As String)
If CmdArgs.Length = 2 Then
' Ide jön a levelek létrehozása
Else
Console.WriteLine( _
"Használat: CreateLetters <sablon> <célmappa>" & _
", pl. CreateLetters C:\Sablon.docx C:\Levelek")
End If
End Sub

Az ügyfelek listájának lekérdezése

A LINQ hasonlít a többi Visual Studio-beli adatelérési megoldáshoz. A különbség abban van, ahogy az adatkezelő műveletek a programkódban megjelennek. Az ügyféltábla lekérdezésének lépései:

  1. A Server Explorerben vagy a Data / Add New Data Source... paranccsal adjuk hozzá a Northwind adatbázis Customers tábláját a projekthez.

  2. A Solution Explorerben vagy a Project / Add New Item... paranccsal adjunk a projekthez egy LINQ to SQL Classes nevű elemet, és nevezzük el Customer-nek. A Solution Explorerben megjelenik egy Customer.dbml fájl, és megnyílik az Object Relational Designer nevű tervezőfelület.

  3. A Server Explorerből húzzuk a Customers táblát a tervezőfelület bal oldalára, ahol létrejön egy Customer nevű osztály. A többes számból egyes számba alakítás nem véletlen, ez az osztály egy konkrét ügyfelet testesít meg. (Nem ér megkérdezni, hogy az "Ügyfelek"-ből is "Ügyfél" lesz-e.) Mentsük a projekt fájljait.

  4. Most lekérdezhetjük az ügyfelek listáját. "Vegytiszta" SQL-t használva ezt pl. a következőképpen tehetnénk meg:

    SELECT CustomerID, CompanyName, ContactName, ContactTitle FROM Customers

    A LINQ-kel a fenti lekérdezést az alábbi módon írhatjuk le (a kódot a Main() eljárás If ágába, a megjegyzés helyére szúrjuk be):

    Dim context As New CustomerDataContext
    Dim customers = _
    From c In context.Customers _
    Select _
    c.CustomerID, c.CompanyName, _
    c.ContactName, c.ContactTitle

  5. A kapott customers gyűjtemény tartalmazza valamennyi ügyfél adatait. A levelek előállításához ennek elemein fogunk végigmenni a következő ciklusban:

    For Each customer In customers
    ' Ide jön az egyes levelek összeállítása
    Next

Az XML adatfájl összeállítása

A LINQ segítségével az XML-készítés is leegyszerűsödik, méghozzá egyetlen lépésre. Az alábbi kódot írjuk be a ciklus belsejébe:

Dim customerData As XElement = _
<CustomerData>
<CustomerID><%= customer.CustomerID %></CustomerID>
<CompanyName><%= customer.CompanyName %></CompanyName>
<ContactName><%= customer.ContactName %></ContactName>
<ContactTitle><%= customer.ContactTitle %></ContactTitle>
</CustomerData>

Feltűnő a hasonlóság az ASP.NET-tel...

Új dokumentum létrehozása a sablon alapján

A parancssorban kapott első paraméter a sablonfájl elérési útja, a második a mappáé, ahova a leveleket gyűjtjük. A kövtkező lépésben lemásoljuk a sablont, az új fájl neve az ügyfélazonosítóból származik (példa: ALFKI.docx). Az alábbi kód is a ciklus belsejébe megy:

My.Computer.FileSystem.CopyFile( _
CmdArgs(0), _
CmdArgs(1) & "\" & customer.CustomerID & ".docx", True)

A dokumentumban lévő XML adatfájl felülírása

Most jött el az ideje, hogy az Open XML csomaggal foglalkozzunk. A teendők (az eleje egy korábbi bejegyzésemből már ismerős lehet):

  1. Töltsük le az Open XML SDK-t erről a címről, és telepítsük.

  2. Adjunk a projekthez egy új referenciát: a .NET fülön a Microsoft.Office.DocumentFormat.OpenXML bejegyzést keressük.

  3. A modul tetején hivatkozzuk meg ezt és a System.IO-t:

    Imports _
    Microsoft.Office.DocumentFormat.OpenXml.Packaging
    Imports System.IO

  4. A ciklus belsejében (mostantól minden kód ide kerül) nyissuk meg az újonnan lemásolt dokumentumot mint csomagot:

    Dim document As WordprocessingDocument = _
    WordprocessingDocument.Open( _
    CmdArgs(1) & "\" & customer.CustomerID _
    & ".docx", True)

  5. Keressük meg a fő dokumentumrészt és az egyedi adatokat (az XML-t) tartalmazó részt:

    Dim mainPart As MainDocumentPart = _
    document.MainDocumentPart
    Dim dataPart As CustomXmlPart = _
    mainPart.CustomXmlParts(0)

  6. A következők kicsit bonyolultnak tűnhetnek, de ez a memóriában tárolt fájlok kezelésének jellegzetessége. Egy adatfolyam-objektumot (stream) nyitunk - ez gyakorlatilag az XML fájl. A folyam módosításához egy író objektumot (writer) használunk. Amikor ezt kiürítjük (flush), az új XML fájl bekerül a DOCX dokumentumba; mentésre nincs szükség:

    Dim stream As Stream = dataPart.GetStream( _
    FileMode.Create, FileAccess.ReadWrite)
    Dim writer As StreamWriter = New StreamWriter(stream)
    writer.Write(customerData.ToString)
    writer.Flush()

Kész a fejlesztés: mentsük a projektet, majd építsük meg az alkalmazást.

Próba, szerencse

Itt az ideje, hogy megírjunk néhány levelet! Másoljuk az első részben készített dokumentumot a kívánt helyre (a C:\ is tökéletesen megfelel), és hozzunk létre egy mappát a termésnek (legyen pl. C:\Levelek). Most futtassuk az alkalmazást a következő paranccsal:

CreateLetters C:\Ügyfélértesítő.docx C:\Levelek

Ha semmit sem rontottunk el (ez fontos, hiszen a rövidség kedvéért kihagytam a hibakezelést :-)), a Levelek mappában néhány másodperc múlva 91 db Word 2007-dokumentum jelenik meg, mindegyikben a Customers tábla egy-egy rekordjának megfelelő adatok.

De a legjobb rész most jön: minden további nélkül átalakíthatjuk a sablonként szolgáló dokumentumot, a megoldás helyesen fog működni. Igazából semmit nem ronthatunk el, a tartalomvezérlők törlését ugyanis korábban megtiltottuk. Íme egy áttervezett dokumentum, ahol a mezők sorrendje és az oldal grafikai megvalósítása egyaránt új:

clip_image001

Vége (vagy mégsem?)

Az Open XML nemcsak a dokumentumok szövegének és grafikai elemeinek bővítését és cseréjét teszi lehetővé, hanem külső adatforrások alapján, dinamikusan történő előállításukat is. És mindezt minimális programozási igény mellett. Érdemes az új fájlformátummal és a hozzá tartozó SDK-val kísérletezni; lehet, hogy én is írok még néhány további lehetőségről itt a blogban.