다음을 통해 공유


Windows PowerShell 콘텐츠 제공자 생성

이 주제는 사용자가 데이터 저장소 내 항목의 내용을 조작할 수 있도록 Windows PowerShell 제공자를 만드는 방법을 설명합니다. 따라서 항목의 내용을 조작할 수 있는 제공자를 Windows PowerShell 콘텐츠 제공자라고 부릅니다.

비고

이 제공자의 C# 소스 파일(AccessDBSampleProvider06.cs)은 Windows Vista용 Microsoft Windows 소프트웨어 개발 키트와 .NET Framework 3.0 런타임 컴포넌트를 통해 다운로드할 수 있습니다. 다운로드 방법은 Windows PowerShell 설치 방법과 Windows PowerShell SDK를 다운로드하세요. 다운로드한 소스 파일은 PowerShell Samples> 디렉터리에서< 이용할 수 있습니다. 다른 Windows PowerShell 제공자 구현에 대한 자세한 내용은 'Windows PowerShell Provider를 설계하기'를 참조하세요.

Windows PowerShell 콘텐츠 제공자 클래스를 정의하세요

Windows PowerShell 콘텐츠 제공자는 System.Management.Automation.Provider.IContentCmdletProvider 인터페이스를 지원하는 .NET 클래스를 생성해야 합니다. 다음은 이 절에서 설명한 항목 제공자의 클래스 정의입니다.

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

이 클래스 정의에서 System.Management.Automation.Provider.CmdletProviderAttribute 속성에는 두 가지 매개변수가 포함되어 있습니다. 첫 번째 매개변수는 Windows PowerShell에서 사용하는 제공자의 사용자 친화적인 이름을 지정합니다. 두 번째 매개변수는 명령 처리 중에 제공자가 Windows PowerShell 런타임에 노출하는 Windows PowerShell 고유 기능을 지정합니다. 이 제공자에는 Windows PowerShell 전용 기능이 추가되지 않습니다.

기본 클래스의 기능 정의

Windows PowerShell Provider를 설계하는 데 설명된 대로, System.Management.Automation.Provider.NavigationCmdletProvider 클래스는 서로 다른 제공 기능을 제공한 여러 클래스에서 파생되었습니다. 따라서 Windows PowerShell 콘텐츠 제공자는 일반적으로 해당 클래스들이 제공하는 모든 기능을 정의합니다.

세션별 초기화 정보 추가와 제공자가 사용하는 리소스 해제 기능을 구현하는 방법에 대한 자세한 내용은 ' 기본 Windows PowerShell 제공자 만들기'를 참조하세요. 하지만 여기서 설명한 제공자를 포함해 대부분의 제공자는 Windows PowerShell에서 제공하는 기본 구현을 사용할 수 있습니다.

데이터 저장소에 접근하려면 제공자는 System.Management.Automation.Provider.DriveCmdletProvider 기본 클래스의 메서드를 구현해야 합니다. 이러한 방법들을 구현하는 방법에 대한 자세한 내용은 Windows PowerShell Drive Provider를 참조하세요.

데이터 저장소의 항목을 조작하기 위해, 예를 들어 항목 획득, 설정, 삭제 등은 System.Management.Automation.Provider.ItemCmdletProvider 기본 클래스에서 제공하는 메서드를 구현해야 합니다. 이 방법들을 구현하는 방법에 대한 자세한 내용은 Windows PowerShell 항목 제공자 생성(Creating a Windows PowerShell Item Provider)을 참조하세요.

다중 계층 데이터 저장소에서 작업하려면 제공자는 System.Management.Automation.Provider.ContainerCmdletProvider 기본 클래스에서 제공하는 메서드를 구현해야 합니다. 이러한 방법들을 구현하는 방법에 대한 자세한 내용은 Windows PowerShell 컨테이너 프로바이저 생성 항목을 참조하세요.

재귀 명령어, 중첩 컨테이너, 상대 경로를 지원하기 위해 제공자는 System.Management.Automation.Provider.NavigationCmdletProvider 기본 클래스를 구현해야 합니다. 또한, 이 Windows PowerShell 콘텐츠 제공자는 System.Management.Automation.Provider.IContentCmdletProvider 인터페이스를 System.Management.Automation.Provider.NavigationCmdletProvider 기본 클래스에 연결할 수 있으며, 따라서 해당 클래스에서 제공하는 메서드를 구현해야 합니다. 자세한 내용은 해당 메서드 구현을 참고하세요, 'Implement a Navigation Windows PowerShell Provider'를 참조하세요.

콘텐츠 리더 구현

항목에서 콘텐츠를 읽으려면, 제공자는 System.Management.Automation.Provider.IContentReader에서 파생된 콘텐츠 리더 클래스를 구현해야 합니다. 이 제공자의 콘텐츠 리더는 데이터 테이블 내 행의 내용에 접근할 수 있게 해줍니다. 콘텐츠 리더 클래스는 지정된 행에서 데이터를 가져오고 그 데이터를 나타내는 리스트를 반환하는 Read 메서드, 콘텐츠 리더를 이동하는 Seek 메서드, 콘텐츠 리더를 닫는 Close 메서드, 그리고 Dispose 메서드를 정의합니다.

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

콘텐츠 작가 도입

아이템에 대한 콘텐츠를 작성하려면, 제공자는 System.Management.Automation.Provider.IContentWriter에서 파생된 콘텐츠 라이터 클래스를 구현해야 합니다. 콘텐츠 작가 클래스는 지정된 행 콘텐츠를 작성하는 Write 메서드, 콘텐츠 작성자를 이동시키는 Seek 메서드, 콘텐츠 작성자를 닫는 Close 메서드, 그리고 Dispose 메서드를 정의합니다.

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

콘텐츠 리더 가져오기

아이템에서 콘텐츠를 얻으려면, 제공자는 명령어를 지원 Get-Content 하기 위해 System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader*를 구현해야 합니다. 이 메서드는 지정된 경로에 위치한 항목의 콘텐츠 리더를 반환합니다. 그 후 리더 객체를 열어 내용을 읽을 수 있습니다.

이 프로바이저에 대한 System.Management.Automation.Provider.IContentCmdletProvider.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
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

GetContentReader를 구현할 때 기억해야 할 점들

다음 조건들이 System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* 구현에 적용될 수 있습니다:

동적 매개변수를 Get-Content 커들렛에 부착하기

cmdlet은 Get-Content 런타임에 동적으로 지정되는 추가 매개변수를 요구할 수 있습니다. 이러한 동적 매개변수를 제공하기 위해 Windows PowerShell 콘텐츠 제공자는 System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters* 메서드를 구현해야 합니다. 이 메서드는 지정된 경로에 있는 항목에 대한 동적 매개변수를 가져오고, cmdlet 클래스나 System.Management.Automation.RuntimeDefinedParameterDictionary 객체와 유사한 파싱 속성을 가진 속성과 필드를 반환합니다. Windows PowerShell 런타임은 반환된 객체를 사용하여 명령어에 매개변수를 추가합니다.

이 Windows PowerShell 컨테이너 제공자는 이 메서드를 구현하지 않습니다. 하지만 다음 코드는 이 방법의 기본 구현입니다.

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

콘텐츠 작가 검색

항목을 작성하려면, 제공자는 System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* 를 구현하여 와 Add-Content cmdlet을 Set-Content 지원해야 합니다. 이 메서드는 지정된 경로에 위치한 항목의 콘텐츠 작성자를 반환합니다.

이 메서드에 대한 System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* 구현 사례는 다음과 같습니다.

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

GetContentWriter 구현에 대해 기억해야 할 점

System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* 구현에 다음과 같은 조건이 적용될 수 있습니다:

동적 매개변수를 Add-Content 및 Set-Content 커들렛에 부착하기

그리고 Set-Content cmdlet은 Add-Content 런타임에 추가되는 추가 동적 매개변수를 요구할 수 있습니다. 이러한 동적 매개변수를 제공하기 위해 Windows PowerShell 콘텐츠 제공자는 이 매개변수를 처리하는 System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* 메서드를 구현해야 합니다. 이 메서드는 지정된 경로에 있는 항목에 대한 동적 매개변수를 가져오고, cmdlet 클래스나 System.Management.Automation.RuntimeDefinedParameterDictionary 객체와 유사한 파싱 속성을 가진 속성과 필드를 반환합니다. Windows PowerShell 런타임은 반환된 객체를 사용해 cmdlet에 매개변수를 추가합니다.

이 Windows PowerShell 컨테이너 제공자는 이 메서드를 구현하지 않습니다. 하지만 다음 코드는 이 방법의 기본 구현입니다.

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

콘텐츠 정리

콘텐츠 제공자는 명령어를 지원 Clear-Content 하기 위해 System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 메서드를 구현합니다. 이 메서드는 지정된 경로에 있는 항목의 내용을 제거하되, 아이템은 그대로 둡니다.

다음은 이 공급자를 위한 System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 메서드의 구현입니다.

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

ClearContent 구현에 대해 기억해야 할 사항

System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 구현에 다음과 같은 조건이 적용될 수 있습니다:

동적 매개변수를 Clear-Content 커들렛에 부착하기

cmdlet은 Clear-Content 런타임에 추가되는 추가 동적 파라미터를 요구할 수 있습니다. 이러한 동적 매개변수를 제공하기 위해 Windows PowerShell 콘텐츠 제공자는 이 매개변수를 처리하는 System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* 메서드를 구현해야 합니다. 이 방법은 지정된 경로에 있는 항목의 매개변수를 가져옵니다. 이 메서드는 지정된 경로에 있는 항목에 대한 동적 매개변수를 가져오고, cmdlet 클래스나 System.Management.Automation.RuntimeDefinedParameterDictionary 객체와 유사한 파싱 속성을 가진 속성과 필드를 반환합니다. Windows PowerShell 런타임은 반환된 객체를 사용하여 명령어에 매개변수를 추가합니다.

이 Windows PowerShell 컨테이너 제공자는 이 메서드를 구현하지 않습니다. 하지만 다음 코드는 이 방법의 기본 구현입니다.

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

코드 샘플

전체 샘플 코드는 AccessDbProviderSample06 코드 샘플을 참조하세요.

객체 유형 및 서식 정의

제공자를 작성할 때 기존 객체에 멤버를 추가하거나 새로운 객체를 정의해야 할 수도 있습니다. 이 작업을 수행하면, Windows PowerShell이 객체의 구성원을 식별할 수 있는 Types 파일과 객체 표시 방식을 정의하는 Format 파일을 만들어야 합니다. 자세한 내용은 ' 객체 유형 및 서식화 확장'을 참조하세요.

Windows PowerShell 제공자 구축

Cmdlet, Providers, Hosting 애플리케이션 등록 방법을 참조하세요.

Windows PowerShell 제공자 테스트

Windows PowerShell 제공자가 Windows PowerShell에 등록된 후, 명령줄에서 지원되는 cmdlet을 실행해 테스트할 수 있습니다. 예를 들어, 샘플 콘텐츠 제공자를 테스트해 보세요.

cmdlet을 Get-Content 사용해 데이터베이스 테이블에서 매개변수가 Path 지정한 경로에 있는 지정된 항목의 내용을 검색하세요. 매개변수는 ReadCount 정의된 콘텐츠 리더가 읽을 수 있는 항목 수(기본값 1)를 지정합니다. 다음 명령어 항목으로 cmdlet은 테이블에서 두 행(항목)을 불러와 그 내용을 표시합니다. 다음 예제 출력은 가상의 Access 데이터베이스를 사용한다는 점에 유의하세요.

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

또한 참조하십시오

Windows PowerShell 제공자 생성

Windows PowerShell 제공자를 설계하세요

객체 유형 및 서식 확장

Implement a Navigation Windows PowerShell provider

Cmdlet, 제공자 및 호스트 애플리케이션 등록 방법

Windows PowerShell SDK

Windows PowerShell 프로グラ머 가이드