Windows PowerShelles tartalomszolgáltató létrehozása

Ez a témakör azt ismerteti, hogyan hozhat létre Windows PowerShell szolgáltatót, amely lehetővé teszi, hogy a felhasználó módosítsa egy adattár elemeinek tartalmát. Ennek következtében az elemek tartalmát manipuláló szolgáltatót más tartalomszolgáltatónak Windows PowerShell nevezzük.

Megjegyzés

Ehhez a szolgáltatóhoz letöltheti a C#-forrásfájlt (AccessDBSampleProvider06.cs) a Microsoft Windows Software Development Kit for Windows Vista és a .NET-keretrendszer 3.0 Runtime Components használatával. A letöltési utasításokért lásd: How to Install Windows PowerShell and Download the Windows PowerShell SDK.. A letöltött forrásfájlok a könyvtárban <PowerShell Samples> érhetők el. További információ a szolgáltató egyéb Windows PowerShell implementációiról: Designing Your Windows PowerShell Provider(Saját Windows PowerShell tervezése).

A Windows PowerShell tartalomszolgáltató osztályának meghatározása

A Windows PowerShell tartalomszolgáltatónak létre kell hoznia egy .NET-osztályt, amely támogatja a System.Management.Automation.Provider.Icontentcmdletprovider felületet. Itt található az ebben a szakaszban leírt elemszolgáltató osztálydefiníciója.

[CmdletProvider("AccessDB", ProviderCapabilities.None)]
public class AccessDBProvider : NavigationCmdletProvider, IContentCmdletProvider

Vegye figyelembe, hogy ebben az osztálydefinícióban a System.Management.Automation.Provider.Cmdletproviderattribute attribútum két paramétert tartalmaz. Az első paraméter egy felhasználóbarát nevet ad meg a szolgáltatónak, amelyet a Windows PowerShell. A második paraméter határozza meg Windows PowerShell, hogy a szolgáltató mely konkrét képességeket teszi elérhetővé a Windows PowerShell futásidejű futtatás során a parancs feldolgozása során. Ehhez a szolgáltatóhoz nem biztosítanak további Windows PowerShell funkciókat.

Az alaposztály funkcióinak meghatározása

A Design Your Windows PowerShell Provider (Saját Windows PowerShell-szolgáltatótervezése) oldalon leírtak szerint a System.Management.Automation.Provider.Navigationcmdletprovider osztály számos más osztályból származik, amelyek különböző szolgáltatói funkciókat biztosítottak. A Windows PowerShell tartalomszolgáltató, ezért általában az adott osztályok által biztosított összes funkciót meghatározza.

További információ a munkamenet-specifikus inicializálási információk hozzáadására és a szolgáltató által használt erőforrások felszabadítási funkcióinak megvalósítására: Alapszintű Windows PowerShell szolgáltató létrehozása. A legtöbb szolgáltató, beleértve az itt leírt szolgáltatót is, használhatja a jelen funkció alapértelmezett megvalósítását, amelyet a Windows PowerShell.

Az adattár eléréséhez a szolgáltatónak implementálnunk kell a System.Management.Automation.Provider.Drivecmdletprovider alaposztály metódusát. További információ a metódusok megvalósításáról: Új meghajtószolgáltató Windows PowerShell létrehozása.

Egy adattár elemeinek (például lekért, be- és törlés) kezeléséhez a szolgáltatónak implementálnunk kell a System.Management.Automation.Provider.Itemcmdletprovider alaposztály által biztosított metódusokat. További információ a metódusok megvalósításáról: Új Windows PowerShell létrehozása.

A többrétegű adattárakon való munkához a szolgáltatónak implementálnunk kell a System.Management.Automation.Provider.Containercmdletprovider alaposztály által biztosított metódusokat. További információ a metódusok megvalósításáról: Creating a Windows PowerShell Container Provider (Tárolószolgáltató létrehozása).

A rekurzív parancsok, beágyazott tárolók és relatív elérési utak támogatásához a szolgáltatónak implementálnunk kell a System.Management.Automation.Provider.Navigationcmdletprovider alaposztályt. Ez a Windows PowerShell-tartalomszolgáltató emellett csatolhatja a System.Management.Automation.Provider.Icontentcmdletprovider felületet a System.Management.Automation.Provider.Navigationcmdletprovider alaposztályhoz, ezért implementálhatja az osztály által biztosított metódusokat. További információkért lásd a módszerek megvalósításáról szóló cikkeket a Navigation Windows PowerShell Provider Windows PowerShell.

Tartalomolvasó megvalósítása

Egy elem tartalmának beolvasásához a szolgáltatónak olyan tartalomolvasó osztályt kell megvalósítania, amely a System.Management.Automation.Provider.Icontentreader osztályból származik. A szolgáltató tartalomolvasója hozzáférést biztosít egy sor tartalmához egy adattáblában. A tartalomolvasó osztály meghatároz egy Read metódust, amely lekéri az adatokat a jelzett sorból, és visszaadja az adatokat képviselő listát, a tartalomolvasót áthelyező Seek metódust, a tartalomolvasót lezáró Close metódust és egy Dispose metódust.

public class AccessDBContentReader : IContentReader
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentReader(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Read the specified number of rows from the source.
    /// </summary>
    /// <param name="readCount">The number of items to 
    /// return.</param>
    /// <returns>An array of elements read.</returns>
    public IList Read(long readCount)
    {
        // Read the number of rows specified by readCount and increment
        // offset
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        Collection<DatabaseRowInfo> rows =
            provider.GetRows(tableName);
        Collection<DataRow> results = new Collection<DataRow>();

        if (currentOffset < 0 || currentOffset >= rows.Count)
        {
            return null;
        }

        int rowsRead = 0;

        while (rowsRead < readCount && currentOffset < rows.Count)
        {
            results.Add(rows[(int)currentOffset].Data);
            rowsRead++;
            currentOffset++;
        }

        return results;
    } // Read

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified must represent a table or a row :" + path);
        }

        if (type == PathType.Table)
        {
            Collection<DatabaseRowInfo> rows = provider.GetRows(tableName);

            int numRows = rows.Count;

            if (offset > rows.Count)
            {
                throw new
                       ArgumentException(
                           "Offset cannot be greater than the number of rows available"
                                        );
            }

            if (origin == System.IO.SeekOrigin.Begin)
            {
                // starting from Beginning with an index 0, the current offset
                // has to be advanced to offset - 1
                currentOffset = offset - 1;
            }
            else if (origin == System.IO.SeekOrigin.End)
            {
                // starting from the end which is numRows - 1, the current
                // offset is so much less than numRows - 1
                currentOffset = numRows - 1 - offset;
            }
            else
            {
                // calculate from the previous value of current offset
                // advancing forward always
                currentOffset += offset;
            }
        } // if (type...
        else
        {
            // for row, the offset will always be set to 0
            currentOffset = 0;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);
        
        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentReader

Tartalomíró megvalósítása

Ahhoz, hogy tartalmat írjon egy elembe, a szolgáltatónak implementálnunk kell egy, a System.Management.Automation.Provider.Icontentwriterosztályból származtatott tartalomíró osztályt. A tartalomíró osztály meghatároz egy Write metódust, amely a megadott sortartalmat írja, egy Seek metódust, amely áthelyezi a tartalomírót, egy Close metódust, amely bezárja a tartalomírót, és egy Dispose metódust.

public class AccessDBContentWriter : IContentWriter
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentWriter(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Write the specified row contents in the source
    /// </summary>
    /// <param name="content"> The contents to be written to the source.
    /// </param>
    /// <returns>An array of elements which were successfully written to 
    /// the source</returns>
    /// 
    public IList Write(IList content)
    {
        if (content == null)
        {
            return null;
        }

        // Get the total number of rows currently available it will 
        // determine how much to overwrite and how much to append at
        // the end
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Table)
        {
            OdbcDataAdapter da = provider.GetAdapterForTable(tableName);
            if (da == null)
            {
                return null;
            }

            DataSet ds = provider.GetDataSetForTable(da, tableName);
            DataTable table = provider.GetDataTable(ds, tableName);

            string[] colValues = (content[0] as string).Split(',');

            // set the specified row
            DataRow row = table.NewRow();

            for (int i = 0; i < colValues.Length; i++)
            {
                if (!String.IsNullOrEmpty(colValues[i]))
                {
                    row[i] = colValues[i];
                }
            }

            //table.Rows.InsertAt(row, rowNumber);
            // Update the table
            table.Rows.Add(row);
            da.Update(ds, tableName);
            
        }
        else 
        {
            throw new InvalidOperationException("Operation not supported. Content can be added only for tables");
        }

        return null;
    } // Write

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified should represent either a table or a row : " + path);
        }

        Collection<DatabaseRowInfo> rows =
               provider.GetRows(tableName);

        int numRows = rows.Count;

        if (offset > rows.Count)
        {
            throw new
                   ArgumentException(
                       "Offset cannot be greater than the number of rows available"
                                           );
        }

        if (origin == System.IO.SeekOrigin.Begin)
        {
            // starting from Beginning with an index 0, the current offset
            // has to be advanced to offset - 1
            currentOffset = offset - 1;
        }
        else if (origin == System.IO.SeekOrigin.End)
        {
            // starting from the end which is numRows - 1, the current
            // offset is so much less than numRows - 1
            currentOffset = numRows - 1 - offset;
        }
        else
        {
            // calculate from the previous value of current offset
            // advancing forward always
            currentOffset += offset;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);

        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentWriter

A tartalomolvasó beolvasása

Egy elem tartalmának leolvasásához a szolgáltatónak implementálja a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreader* elemet a Get-Content parancsmag támogatásához. Ez a metódus visszaadja a tartalomolvasót a megadott elérési úton található elemhez. Az olvasó objektum ezután megnyitható a tartalom olvasásához.

Itt látható a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreader* implementációja ehhez a metódushoz ehhez a szolgáltatóhoz.

public IContentReader GetContentReader(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be obtained only for tables");
    }

    return new AccessDBContentReader(path, this);
} // GetContentReader
public IContentReader GetContentReader(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be obtained only for tables");
    }

    return new AccessDBContentReader(path, this);
} // GetContentReader

A GetContentReader megvalósításával kapcsolatos dolgok

A következő feltételek vonatkozhatnak a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreader* implementációjára:

Dinamikus paraméterek csatolása a Get-Content parancsmaghoz

A parancsmag további paramétereket is megkövetelhet, amelyek dinamikusan Get-Content vannak megadva futásidőben. A dinamikus paraméterek megvalósításához a Windows PowerShell tartalomszolgáltatónak implementálja a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreaderdynamicparameters* metódust. Ez a metódus lekéri a jelzett elérési úton található elem dinamikus paramétereit, és egy olyan objektumot ad vissza, amely a parancsmagosztályhoz vagy a System.Management.Automation.Runtimedefinedparameterdictionary objektumhoz hasonló elemzési attribútumokkal és tulajdonságokkal rendelkezik. A Windows PowerShell a visszaadott objektummal adja hozzá a paramétereket a parancsmaghoz.

Ez Windows PowerShell tárolószolgáltató nem implementálja ezt a módszert. A metódus alapértelmezett implementációja azonban a következő kód.

public object GetContentReaderDynamicParameters(string path)
{
    return null;
}
public object GetContentReaderDynamicParameters(string path)
{
    return null;
}

A tartalomíró leolvasása

Ahhoz, hogy tartalmat írjon egy elembe, a szolgáltatónak implementálja a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriter* parancsmagot a és Set-Content Add-Content parancsmagok támogatásához. Ez a metódus visszaadja a tartalomírót a megadott elérési úton található elemhez.

Ebben a módszerben a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriter* implementációja látható.

public IContentWriter GetContentWriter(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be added only to tables");
    }

    return new AccessDBContentWriter(path, this);
}
public IContentWriter GetContentWriter(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be added only to tables");
    }

    return new AccessDBContentWriter(path, this);
}

A GetContentWriter megvalósításával kapcsolatos dolgok

Az alábbi feltételek vonatkozhatnak a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriter* implementációjára:

Dinamikus paraméterek csatolása a Add-Content és Set-Content parancsmaghoz

A és a parancsmaghoz további dinamikus paraméterekre lehet Add-Content Set-Content szükség, amelyek hozzáadnak egy futásidőt. A dinamikus paraméterek megvalósításához a Windows PowerShell tartalomszolgáltatónak implementálja a System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriterdynamicparameters* metódust a paraméterek kezeléséhez. Ez a metódus lekéri a jelzett elérési úton található elem dinamikus paramétereit, és egy olyan objektumot ad vissza, amely a parancsmagosztályhoz vagy a System.Management.Automation.Runtimedefinedparameterdictionary objektumhoz hasonló elemzési attribútumokkal és tulajdonságokkal rendelkezik. A Windows PowerShell futtatás a visszaadott objektummal adja hozzá a paramétereket a parancsmagokhoz.

Ez Windows PowerShell tárolószolgáltató nem implementálja ezt a módszert. A metódus alapértelmezett implementációja azonban a következő kód.

public object GetContentWriterDynamicParameters(string path)
{
    return null;
}

Tartalom törlése

A tartalomszolgáltató implementálja a System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontent* metódust a Clear-Content parancsmag támogatásához. Ez a metódus eltávolítja az elem tartalmát a megadott elérési úton, de az elemet érintetlenül hagyja.

Itt látható a System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontent* metódus implementációja ehhez a szolgáltatóhoz.

public void ClearContent(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type != PathType.Table)
    {
        WriteError(new ErrorRecord(
            new InvalidOperationException("Operation not supported. Content can be cleared only for table"),
                "NotValidRow", ErrorCategory.InvalidArgument,
                    path));
        return;
    }

    OdbcDataAdapter da = GetAdapterForTable(tableName);

    if (da == null)
    {
        return;
    }

    DataSet ds = GetDataSetForTable(da, tableName);
    DataTable table = GetDataTable(ds, tableName);

    // Clear contents at the specified location
    for (int i = 0; i < table.Rows.Count; i++)
    {
        table.Rows[i].Delete();
    }

    if (ShouldProcess(path, "ClearContent"))
    {
        da.Update(ds, tableName);
    }

} // ClearContent

A ClearContent megvalósításával kapcsolatos dolgok

A következő feltételek vonatkozhatnak a System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontent* implementációjára:

Dinamikus paraméterek csatolása a Clear-Content parancsmaghoz

A Clear-Content parancsmag további dinamikus paramétereket is megkövetelhet, amelyek futásidőben vannak hozzáadva. A dinamikus paraméterek megvalósításához a Windows PowerShell tartalomszolgáltatónak implementálja a System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontentdynamicparameters* metódust a paraméterek kezeléséhez. Ez a metódus lekéri az elem paramétereit a jelzett elérési úton. Ez a metódus lekéri a jelzett elérési úton található elem dinamikus paramétereit, és egy olyan objektumot ad vissza, amely a parancsmagosztályhoz vagy a System.Management.Automation.Runtimedefinedparameterdictionary objektumhoz hasonló elemzési attribútumokkal és tulajdonságokkal rendelkezik. A Windows PowerShell a visszaadott objektummal adja hozzá a paramétereket a parancsmaghoz.

Ez Windows PowerShell tárolószolgáltató nem implementálja ezt a módszert. A metódus alapértelmezett implementációja azonban a következő kód.

public object ClearContentDynamicParameters(string path)
{
    return null;
}
public object ClearContentDynamicParameters(string path)
{
    return null;
}

Kódminta

A teljes mintakódért lásd: AccessDbProviderSample06 – Kódminta.

Objektumtípusok definiálása és formázása

Szolgáltató írásakor szükség lehet tagok hozzáadására a meglévő objektumokhoz, vagy új objektumok definiálása. Ha ez megtörtént, létre kell hoznia egy Types (Típusok) fájlt, Windows PowerShell segítségével azonosíthatja az objektum tagjait, és egy Format (Formátum) fájlt, amely meghatározza az objektum megjelenítési mikéntját. További információ: Extending Object Types and Formatting (Objektumtípusok kiterjesztése és formázása).

A Windows PowerShell kiépítése

Lásd: Parancsmagok, szolgáltatók és gazdaalkalmazások regisztrálása.

A Windows PowerShell tesztelése

Ha a Windows PowerShell-szolgáltató regisztrálva van a Windows PowerShell, tesztelheti a támogatott parancsmagok parancssoron való futtatásával. Tesztelje például a mintatartalom-szolgáltatót.

A parancsmag használatával lekéri az adatbázistábla megadott elemének tartalmát a paraméter által Get-Content megadott elérési Path úton. A paraméter határozza meg a meghatározott tartalomolvasó által olvasni szükséges ReadCount elemek számát (alapértelmezés: 1). A következő parancsbejegyzéssel a parancsmag két sort (elemet) ad vissza a táblából, és megjeleníti azok tartalmát. Vegye figyelembe, hogy az alábbi példakimenet egy fiktív Access-adatbázist használ.

Get-Content -Path mydb:\Customers -ReadCount 2
ID        : 1
FirstName : Eric
LastName  : Gruber
Email     : ericgruber@fabrikam.com
Title     : President
Company   : Fabrikam
WorkPhone : (425) 555-0100
Address   : 4567 Main Street
City      : Buffalo
State     : NY
Zip       : 98052
Country   : USA
ID        : 2
FirstName : Eva
LastName  : Corets
Email     : evacorets@cohowinery.com
Title     : Sales Representative
Company   : Coho Winery
WorkPhone : (360) 555-0100
Address   : 8910 Main Street
City      : Cabmerlot
State     : WA
Zip       : 98089
Country   : USA

Lásd még:

Adatszolgáltatók Windows PowerShell létrehozása

Saját Windows PowerShell megtervezése

Objektumtípusok kiterjesztése és formázása

Navigációszolgáltató Windows PowerShell megvalósítása

Parancsmagok, szolgáltatók és gazdaalkalmazások regisztrálása

Windows PowerShell SDK

Windows PowerShell – programozói útmutató