Enable DataRow/DataRowView adapters in PowerShell Core (#4258)

This commit is contained in:
Dongbo Wang
2017-07-17 11:27:01 -07:00
committed by GitHub
parent 06274095ea
commit 069c7a4b6f
5 changed files with 278 additions and 221 deletions
@@ -6,6 +6,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
@@ -5007,6 +5008,221 @@ namespace System.Management.Automation
}
}
/// <summary>
/// Deals with DataRow objects
/// </summary>
internal class DataRowAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">object to get all the property information from</param>
/// <param name="members">collection where the members will be added</param>
protected override void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members)
{
DataRow dataRow = (DataRow)obj;
if (dataRow.Table == null || dataRow.Table.Columns == null)
{
return;
}
foreach (DataColumn property in dataRow.Table.Columns)
{
members.Add(new PSProperty(property.ColumnName, this, obj, property.ColumnName) as T);
}
return;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">object to retrieve the PSProperty from</param>
/// <param name="propertyName">name of the property to be retrieved</param>
/// <returns>The PSProperty corresponding to propertyName from obj</returns>
protected override PSProperty DoGetProperty(object obj, string propertyName)
{
DataRow dataRow = (DataRow)obj;
if (!dataRow.Table.Columns.Contains(propertyName))
{
return null;
}
string columnName = dataRow.Table.Columns[propertyName].ColumnName;
return new PSProperty(columnName, this, obj, columnName);
}
/// <summary>
/// Returns the name of the type corresponding to the property
/// </summary>
/// <param name="property">PSProperty obtained in a previous DoGetProperty</param>
/// <param name="forDisplay">True if the result is for display purposes only</param>
/// <returns>the name of the type corresponding to the property</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
string columnName = (string)property.adapterData;
DataRow dataRow = (DataRow)property.baseObject;
var dataType = dataRow.Table.Columns[columnName].DataType;
return forDisplay ? ToStringCodeMethods.Type(dataType) : dataType.FullName;
}
/// <summary>
/// Returns true if the property is settable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is settable</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
string columnName = (string)property.adapterData;
DataRow dataRow = (DataRow)property.baseObject;
return !dataRow.Table.Columns[columnName].ReadOnly;
}
/// <summary>
/// Returns true if the property is gettable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is gettable</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return true;
}
/// <summary>
/// Returns the value from a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <returns>The value of the property</returns>
protected override object PropertyGet(PSProperty property)
{
DataRow dataRow = (DataRow)property.baseObject;
return dataRow[(string)property.adapterData];
}
/// <summary>
/// Sets the value of a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <param name="setValue">value to set the property with</param>
/// <param name="convertIfPossible">instructs the adapter to convert before setting, if the adapter supports conversion</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
DataRow dataRow = (DataRow)property.baseObject;
dataRow[(string)property.adapterData] = setValue;
return;
}
#endregion virtual
}
/// <summary>
/// Deals with DataRowView objects
/// </summary>
internal class DataRowViewAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">object to get all the property information from</param>
/// <param name="members">collection where the members will be added</param>
protected override void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members)
{
DataRowView dataRowView = (DataRowView)obj;
if (dataRowView.Row == null || dataRowView.Row.Table == null || dataRowView.Row.Table.Columns == null)
{
return;
}
foreach (DataColumn property in dataRowView.Row.Table.Columns)
{
members.Add(new PSProperty(property.ColumnName, this, obj, property.ColumnName) as T);
}
return;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">object to retrieve the PSProperty from</param>
/// <param name="propertyName">name of the property to be retrieved</param>
/// <returns>The PSProperty corresponding to propertyName from obj</returns>
protected override PSProperty DoGetProperty(object obj, string propertyName)
{
DataRowView dataRowView = (DataRowView)obj;
if (!dataRowView.Row.Table.Columns.Contains(propertyName))
{
return null;
}
string columnName = dataRowView.Row.Table.Columns[propertyName].ColumnName;
return new PSProperty(columnName, this, obj, columnName);
}
/// <summary>
/// Returns the name of the type corresponding to the property
/// </summary>
/// <param name="property">PSProperty obtained in a previous DoGetProperty</param>
/// <param name="forDisplay">True if the result is for display purposes only</param>
/// <returns>the name of the type corresponding to the property</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
string columnName = (string)property.adapterData;
DataRowView dataRowView = (DataRowView)property.baseObject;
var dataType = dataRowView.Row.Table.Columns[columnName].DataType;
return forDisplay ? ToStringCodeMethods.Type(dataType) : dataType.FullName;
}
/// <summary>
/// Returns true if the property is settable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is settable</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
string columnName = (string)property.adapterData;
DataRowView dataRowView = (DataRowView)property.baseObject;
return !dataRowView.Row.Table.Columns[columnName].ReadOnly;
}
/// <summary>
/// Returns true if the property is gettable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is gettable</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return true;
}
/// <summary>
/// Returns the value from a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <returns>The value of the property</returns>
protected override object PropertyGet(PSProperty property)
{
DataRowView dataRowView = (DataRowView)property.baseObject;
return dataRowView[(string)property.adapterData];
}
/// <summary>
/// Sets the value of a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <param name="setValue">value to set the property with</param>
/// <param name="convertIfPossible">instructs the adapter to convert before setting, if the adapter supports conversion</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
DataRowView dataRowView = (DataRowView)property.baseObject;
dataRowView[(string)property.adapterData] = setValue;
return;
}
#endregion virtual
}
internal class TypeInference
{
[TraceSource("ETS", "Extended Type System")]
@@ -4,7 +4,6 @@ Copyright (c) Microsoft Corporation. All rights reserved.
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.DirectoryServices;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -15,220 +14,6 @@ using Microsoft.PowerShell;
namespace System.Management.Automation
{
/// <summary>
/// Deals with DataRow objects
/// </summary>
internal class DataRowAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">object to get all the property information from</param>
/// <param name="members">collection where the members will be added</param>
protected override void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members)
{
DataRow dataRow = (DataRow)obj;
if (dataRow.Table == null || dataRow.Table.Columns == null)
{
return;
}
foreach (DataColumn property in dataRow.Table.Columns)
{
members.Add(new PSProperty(property.ColumnName, this, obj, property.ColumnName) as T);
}
return;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">object to retrieve the PSProperty from</param>
/// <param name="propertyName">name of the property to be retrieved</param>
/// <returns>The PSProperty corresponding to propertyName from obj</returns>
protected override PSProperty DoGetProperty(object obj, string propertyName)
{
DataRow dataRow = (DataRow)obj;
if (!dataRow.Table.Columns.Contains(propertyName))
{
return null;
}
string columnName = dataRow.Table.Columns[propertyName].ColumnName;
return new PSProperty(columnName, this, obj, columnName);
}
/// <summary>
/// Returns the name of the type corresponding to the property
/// </summary>
/// <param name="property">PSProperty obtained in a previous DoGetProperty</param>
/// <param name="forDisplay">True if the result is for display purposes only</param>
/// <returns>the name of the type corresponding to the property</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
string columnName = (string)property.adapterData;
DataRow dataRow = (DataRow)property.baseObject;
var dataType = dataRow.Table.Columns[columnName].DataType;
return forDisplay ? ToStringCodeMethods.Type(dataType) : dataType.FullName;
}
/// <summary>
/// Returns true if the property is settable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is settable</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
string columnName = (string)property.adapterData;
DataRow dataRow = (DataRow)property.baseObject;
return !dataRow.Table.Columns[columnName].ReadOnly;
}
/// <summary>
/// Returns true if the property is gettable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is gettable</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return true;
}
/// <summary>
/// Returns the value from a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <returns>The value of the property</returns>
protected override object PropertyGet(PSProperty property)
{
DataRow dataRow = (DataRow)property.baseObject;
return dataRow[(string)property.adapterData];
}
/// <summary>
/// Sets the value of a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <param name="setValue">value to set the property with</param>
/// <param name="convertIfPossible">instructs the adapter to convert before setting, if the adapter supports conversion</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
DataRow dataRow = (DataRow)property.baseObject;
dataRow[(string)property.adapterData] = setValue;
return;
}
#endregion virtual
}
/// <summary>
/// Deals with DataRowView objects
/// </summary>
internal class DataRowViewAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">object to get all the property information from</param>
/// <param name="members">collection where the members will be added</param>
protected override void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members)
{
DataRowView dataRowView = (DataRowView)obj;
if (dataRowView.Row == null || dataRowView.Row.Table == null || dataRowView.Row.Table.Columns == null)
{
return;
}
foreach (DataColumn property in dataRowView.Row.Table.Columns)
{
members.Add(new PSProperty(property.ColumnName, this, obj, property.ColumnName) as T);
}
return;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">object to retrieve the PSProperty from</param>
/// <param name="propertyName">name of the property to be retrieved</param>
/// <returns>The PSProperty corresponding to propertyName from obj</returns>
protected override PSProperty DoGetProperty(object obj, string propertyName)
{
DataRowView dataRowView = (DataRowView)obj;
if (!dataRowView.Row.Table.Columns.Contains(propertyName))
{
return null;
}
string columnName = dataRowView.Row.Table.Columns[propertyName].ColumnName;
return new PSProperty(columnName, this, obj, columnName);
}
/// <summary>
/// Returns the name of the type corresponding to the property
/// </summary>
/// <param name="property">PSProperty obtained in a previous DoGetProperty</param>
/// <param name="forDisplay">True if the result is for display purposes only</param>
/// <returns>the name of the type corresponding to the property</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
string columnName = (string)property.adapterData;
DataRowView dataRowView = (DataRowView)property.baseObject;
var dataType = dataRowView.Row.Table.Columns[columnName].DataType;
return forDisplay ? ToStringCodeMethods.Type(dataType) : dataType.FullName;
}
/// <summary>
/// Returns true if the property is settable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is settable</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
string columnName = (string)property.adapterData;
DataRowView dataRowView = (DataRowView)property.baseObject;
return !dataRowView.Row.Table.Columns[columnName].ReadOnly;
}
/// <summary>
/// Returns true if the property is gettable
/// </summary>
/// <param name="property">property to check</param>
/// <returns>true if the property is gettable</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return true;
}
/// <summary>
/// Returns the value from a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <returns>The value of the property</returns>
protected override object PropertyGet(PSProperty property)
{
DataRowView dataRowView = (DataRowView)property.baseObject;
return dataRowView[(string)property.adapterData];
}
/// <summary>
/// Sets the value of a property coming from a previous call to DoGetProperty
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty</param>
/// <param name="setValue">value to set the property with</param>
/// <param name="convertIfPossible">instructs the adapter to convert before setting, if the adapter supports conversion</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
DataRowView dataRowView = (DataRowView)property.baseObject;
dataRowView[(string)property.adapterData] = setValue;
return;
}
#endregion virtual
}
/// <summary>
/// Deals with DirectoryEntry objects
/// </summary>
@@ -381,13 +381,13 @@ namespace System.Management.Automation
new Microsoft.PowerShell.Cim.CimInstanceAdapter()),
PSObject.dotNetInstanceAdapter);
#if !CORECLR // WMIv1/ADSI/DataRow/DataRowView Adapters Not Supported On CSS
#if !CORECLR // WMIv1/ADSI Adapters Not Supported in PowerShell Core
private static readonly AdapterSet managementObjectAdapter = new AdapterSet(new ManagementObjectAdapter(), dotNetInstanceAdapter);
private static readonly AdapterSet managementClassAdapter = new AdapterSet(new ManagementClassApdapter(), dotNetInstanceAdapter);
private static readonly AdapterSet directoryEntryAdapter = new AdapterSet(new DirectoryEntryAdapter(), dotNetInstanceAdapter);
#endif
private static readonly AdapterSet dataRowViewAdapter = new AdapterSet(new DataRowViewAdapter(), s_baseAdapterForAdaptedObjects);
private static readonly AdapterSet dataRowAdapter = new AdapterSet(new DataRowAdapter(), s_baseAdapterForAdaptedObjects);
#endif
private static readonly AdapterSet s_xmlNodeAdapter = new AdapterSet(new XmlNodeAdapter(), s_baseAdapterForAdaptedObjects);
#region Adapter Mappings
@@ -412,13 +412,13 @@ namespace System.Management.Automation
if (obj is PSObject) { return PSObject.s_mshObjectAdapter; }
if (obj is CimInstance) { return PSObject.s_cimInstanceAdapter; }
#if !CORECLR // WMIv1/ADSI/DataRow/DataRowView Adapters Not Supported On CSS
#if !CORECLR // WMIv1/ADSI Adapters Not Supported in PowerShell Core
if (obj is ManagementClass) { return PSObject.managementClassAdapter; }
if (obj is ManagementBaseObject) { return PSObject.managementObjectAdapter; }
if (obj is DirectoryEntry) { return PSObject.directoryEntryAdapter; }
#endif
if (obj is DataRowView) { return PSObject.dataRowViewAdapter; }
if (obj is DataRow) { return PSObject.dataRowAdapter; }
#endif
if (obj is XmlNode) { return PSObject.s_xmlNodeAdapter; }
return null;
}
@@ -554,7 +554,6 @@ namespace System.Management.Automation.Language
return (errorSuggestion ?? NullResult(target)).WriteToDebugLog(this);
}
#if !CORECLR
// In CORECLR System.Data.DataTable does not have the DataRowCollection IEnumerable, so disabling code.
if (targetValue is DataTable)
{
@@ -582,7 +581,6 @@ namespace System.Management.Automation.Language
target),
GetRestrictions(target))).WriteToDebugLog(this);
}
#endif
if (IsComObject(targetValue))
{
@@ -108,3 +108,61 @@ Describe "Adapter XML Tests" -tags "CI" {
}
}
}
Describe "DataRow and DataRowView Adapter tests" -tags "CI" {
BeforeAll {
## Define the DataTable schema
$dataTable = [System.Data.DataTable]::new("inputs")
$dataTable.Locale = [cultureinfo]::InvariantCulture
$dataTable.Columns.Add("Id", [int]) > $null
$dataTable.Columns.Add("FirstName", [string]) > $null
$dataTable.Columns.Add("LastName", [string]) > $null
$dataTable.Columns.Add("YearsInMS", [int]) > $null
## Add data entries
$dataTable.Rows.Add(@(1, "joseph", "smith", 15)) > $null
$dataTable.Rows.Add(@(2, "paul", "smith", 15)) > $null
$dataTable.Rows.Add(@(3, "mary jo", "soe", 5)) > $null
$dataTable.Rows.Add(@(4, "edmund`todd `n", "bush", 9)) > $null
}
Context "DataRow Adapter tests" {
It "Should be able to access data columns" {
$row = $dataTable.Rows[0]
$row.Id | Should Be 1
$row.FirstName | Should Be "joseph"
$row.LastName | Should Be "smith"
$row.YearsInMS | Should Be 15
}
It "DataTable should be enumerable in PowerShell" {
## Get the third entry in the data table
$row = $dataTable | Select-Object -Skip 2 -First 1
$row.Id | Should Be 3
$row.FirstName | Should Be "mary jo"
$row.LastName | Should Be "soe"
$row.YearsInMS | Should Be 5
}
}
Context "DataRowView Adapter tests" {
It "Should be able to access data columns" {
$rowView = $dataTable.DefaultView[1]
$rowView.Id | Should Be 2
$rowView.FirstName | Should Be "paul"
$rowView.LastName | Should Be "smith"
$rowView.YearsInMS | Should Be 15
}
It "DataView should be enumerable" {
$rowView = $dataTable.DefaultView | Select-Object -Last 1
$rowView.Id | Should Be 4
$rowView.FirstName | Should Be "edmund`todd `n"
$rowView.LastName | Should Be "bush"
$rowView.YearsInMS | Should Be 9
}
}
}