在Get和Set块中动态检索ScriptProperty的名称

时间:2016-11-08 03:10:05

标签: class powershell powershell-v5.0

目标

我希望利用PowerShell 5的新Class功能创建一个类,我可以在我的项目中使用它来查看数据库的记录,并创建链接和取消链接外键的方法。

下面的类正在使用2个属性,但我需要添加更多但不想复制/粘贴,这就是它目前的工作方式:

# Create a new instance of my Computer class
$computer = [Computer]::new([int]71)

# Change the status of the Server in the DataRow
$computer.Status = "Active"

# Update the Row in the Database
$computer.SaveChanges()

# Link this Server in the Database to a record in the Locations table
$computer.LinkLocation([int]16)

我已经实现了Class部分甚至一些Methods,但我想知道我是否可以通过某种方式引用Name来减少类中所需的代码量ScriptProperty

的属性

class Computer
{
    hidden [System.Data.DataRow]$_dataRow
    hidden [System.Data.SqlClient.SqlDataAdapter]$_dataAdapter

    hidden GetComputerDetails([int]$serverId)
    {
        $connectionString = "Server=HomeTestServer\DB01;Database=TestDB;Integrated Security=SSPI;"

        $sqlQuery = "SELECT ComputerName, Status FROM tbComputers WHERE Id = $serverId"

        $sqlConnection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $connectionString

        $sqlCommand = New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $SqlQuery, $sqlConnection

        $sqlConnection.Open()

        $sqlAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList $sqlCommand

        $sqlData = New-Object -TypeName System.Data.DataSet

        $sqlBuilder = New-Object -TypeName System.Data.SqlClient.SqlCommandBuilder -ArgumentList $sqlAdapter

        $sqlAdapter.Fill($sqlData, "Computer")

        $this._dataRow = $sqlData.Tables[0].Rows[0]
        $this._dataAdapter = $sqlAdapter
    }

    hidden [object] GetValue([string]$propertyName)
    {
        return $this._dataRow.$propertyName
    }

    hidden SetValue([string]$propertyName, $value)
    {
        $this._dataRow.$propertyName = $value
    }

    SaveChanges()
    {
        $this._dataAdapter.Update($this._dataRow)
    }

    LinkLocation([int]$locationId)
    {
        $serverId = $this._dataRow.Id

        [void](Invoke-Sqlcmd -ServerInstance "HomeTestServer\DB01" -Database TestDB -Query "UPDATE tbComputers SET LocationId = $locationId WHERE Id = $serverId" )
    }

    Computer([int]$serverId)
    {
        $this.GetComputerDetails([int]$serverId)

        $this | Add-Member -MemberType ScriptProperty -Name ComputerName -Force -Value `
        {
            # Is there a way to get the ScriptProperty's Name?
            $this.GetValue("ComputerName")
        } `
        {
            param
            (
                $value
            )
            # Is there a way to get the ScriptProperty's Name?
            $this.SetValue("ComputerName", $value) 
        }

        $this | Add-Member -MemberType ScriptProperty -Name Status -Force -Value `
        {
            # Is there a way to get the ScriptProperty's Name?
            $this.GetValue("Status")
        } `
        {
            param
            (
                $value
            )
            # Is there a way to get the ScriptProperty's Name?
            $this.SetValue("Status", $value) 
        }
    }
}

说明

由于DataRow直接在DataAdapter和{{1 }}方法使用ScriptProperty

更新DataRow

问题

对于我要在Savechanges中打包的每个媒体资源,我需要制作DataRow DataAdapterClassScriptProperty有两个属性,看起来不坏但是如果我的SQL表有40个属性怎么办?我真的不想复制和粘贴Getter行40次。

理想情况下,我正在寻找一种循环方式并动态创建每个Setter

尝试

Class

修改

高度简化的例子:

$this | Add-Member...

Image Example

尝试设置动态属性时收到错误(例如`$ computer.number2 = 17)

ScriptProperty

问题

有没有办法可以根据指定的属性名称动态创建foreach($propertyName in $this._dataRow.Table.Columns) { # Looping through doesn't work, my working theory is the Get/Set block don't expand the variable when the Member is being added, only when it's being called $this | Add-Member -MemberType ScriptProperty -Name $propertyName -Force -Value ` { # This is the Get block $this.GetValue($propertyName) } ` { param ( $value ) # This is the Set block $this.SetValue($propertyName, $value) } }

1 个答案:

答案 0 :(得分:0)

这可以通过为setter和getter动态生成脚本块来完成。假设使用两种方法定义类Bar,这两种方法实现了访问字段的一般方法:

class Bar
{
    [object] GetFieldValue ( [string] $FieldName )
    {
        # replace this with code that looks up your value by field name
        Write-Host "Invoked GetFieldValue($FieldName)"
        return "value of $FieldName"
    }
    SetFieldValue ( [string] $FieldName, [object] $Value )
    {
        # replace this with code that sets your field value
        Write-Host "Invoked SetFieldValue($FieldName,$Value)"
    }
}

然后可以实现一个函数,用动态生成的属性装饰Bar对象,该属性具有调用这些方法的getter和setter:

function New-Bar
{
    param( [string[]]$FieldNames )

    # create the new object
    $outputObject = [Bar]::new()

    # add a property for each field
    foreach ( $fieldName in $FieldNames )
    {
        $getter = [scriptblock]::Create(
            "`$this.GetFieldValue('$fieldName')"
        )
        $setter = [scriptblock]::Create(
            "`param(`$p) `$this.SetFieldValue('$fieldName',`$p)"
        )
        $outputObject | 
            Add-Member ScriptProperty $fieldName $getter $setter -Force
    }

    # return the object
    $outputObject
}

然后可以像这样访问字段:

$b = New-Bar 'field1','field2'
$b.field1 = 'my value for field 1'
$b.field1

哪个输出:

Invoked GetFieldValue(field1)
value of field1
Invoked SetFieldValue(field1,my value for field 1)