From bbd9be74b3ce1efe59442fc80b7b63475abb0c50 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 14 Jul 2017 13:49:03 -0700 Subject: [PATCH] Enable DataRow/DataRowView adapters in PowerShell Core --- .../engine/CoreAdapter.cs | 216 ++++++++++++++++++ .../engine/ExtraAdapter.cs | 215 ----------------- .../engine/MshObject.cs | 8 +- .../engine/runtime/Binding/Binders.cs | 2 - test/powershell/engine/ETS/Adapter.Tests.ps1 | 58 +++++ 5 files changed, 278 insertions(+), 221 deletions(-) diff --git a/src/System.Management.Automation/engine/CoreAdapter.cs b/src/System.Management.Automation/engine/CoreAdapter.cs index 70e25a3a1f5..4fbf9d629e7 100644 --- a/src/System.Management.Automation/engine/CoreAdapter.cs +++ b/src/System.Management.Automation/engine/CoreAdapter.cs @@ -6,6 +6,7 @@ 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 @@ private static XmlNode[] FindNodes(object obj, string propertyName, StringCompar } } + /// + /// Deals with DataRow objects + /// + internal class DataRowAdapter : PropertyOnlyAdapter + { + #region virtual + /// + /// Retrieves all the properties available in the object. + /// + /// object to get all the property information from + /// collection where the members will be added + protected override void DoAddAllProperties(object obj, PSMemberInfoInternalCollection 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; + } + /// + /// 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. + /// + /// object to retrieve the PSProperty from + /// name of the property to be retrieved + /// The PSProperty corresponding to propertyName from obj + 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); + } + + /// + /// Returns the name of the type corresponding to the property + /// + /// PSProperty obtained in a previous DoGetProperty + /// True if the result is for display purposes only + /// the name of the type corresponding to the property + 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; + } + + /// + /// Returns true if the property is settable + /// + /// property to check + /// true if the property is settable + protected override bool PropertyIsSettable(PSProperty property) + { + string columnName = (string)property.adapterData; + DataRow dataRow = (DataRow)property.baseObject; + return !dataRow.Table.Columns[columnName].ReadOnly; + } + + /// + /// Returns true if the property is gettable + /// + /// property to check + /// true if the property is gettable + protected override bool PropertyIsGettable(PSProperty property) + { + return true; + } + + + /// + /// Returns the value from a property coming from a previous call to DoGetProperty + /// + /// PSProperty coming from a previous call to DoGetProperty + /// The value of the property + protected override object PropertyGet(PSProperty property) + { + DataRow dataRow = (DataRow)property.baseObject; + return dataRow[(string)property.adapterData]; + } + /// + /// Sets the value of a property coming from a previous call to DoGetProperty + /// + /// PSProperty coming from a previous call to DoGetProperty + /// value to set the property with + /// instructs the adapter to convert before setting, if the adapter supports conversion + protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible) + { + DataRow dataRow = (DataRow)property.baseObject; + dataRow[(string)property.adapterData] = setValue; + return; + } + #endregion virtual + } + /// + /// Deals with DataRowView objects + /// + internal class DataRowViewAdapter : PropertyOnlyAdapter + { + #region virtual + /// + /// Retrieves all the properties available in the object. + /// + /// object to get all the property information from + /// collection where the members will be added + protected override void DoAddAllProperties(object obj, PSMemberInfoInternalCollection 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; + } + /// + /// 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. + /// + /// object to retrieve the PSProperty from + /// name of the property to be retrieved + /// The PSProperty corresponding to propertyName from obj + 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); + } + + /// + /// Returns the name of the type corresponding to the property + /// + /// PSProperty obtained in a previous DoGetProperty + /// True if the result is for display purposes only + /// the name of the type corresponding to the property + 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; + } + + /// + /// Returns true if the property is settable + /// + /// property to check + /// true if the property is settable + protected override bool PropertyIsSettable(PSProperty property) + { + string columnName = (string)property.adapterData; + DataRowView dataRowView = (DataRowView)property.baseObject; + return !dataRowView.Row.Table.Columns[columnName].ReadOnly; + } + + /// + /// Returns true if the property is gettable + /// + /// property to check + /// true if the property is gettable + protected override bool PropertyIsGettable(PSProperty property) + { + return true; + } + + /// + /// Returns the value from a property coming from a previous call to DoGetProperty + /// + /// PSProperty coming from a previous call to DoGetProperty + /// The value of the property + protected override object PropertyGet(PSProperty property) + { + DataRowView dataRowView = (DataRowView)property.baseObject; + return dataRowView[(string)property.adapterData]; + } + /// + /// Sets the value of a property coming from a previous call to DoGetProperty + /// + /// PSProperty coming from a previous call to DoGetProperty + /// value to set the property with + /// instructs the adapter to convert before setting, if the adapter supports conversion + 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")] diff --git a/src/System.Management.Automation/engine/ExtraAdapter.cs b/src/System.Management.Automation/engine/ExtraAdapter.cs index 7ef258808d6..c2ca2317e59 100644 --- a/src/System.Management.Automation/engine/ExtraAdapter.cs +++ b/src/System.Management.Automation/engine/ExtraAdapter.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Collections.Generic; -using System.Data; using System.DirectoryServices; using System.Reflection; using System.Runtime.InteropServices; @@ -15,220 +14,6 @@ namespace System.Management.Automation { - /// - /// Deals with DataRow objects - /// - internal class DataRowAdapter : PropertyOnlyAdapter - { - #region virtual - /// - /// Retrieves all the properties available in the object. - /// - /// object to get all the property information from - /// collection where the members will be added - protected override void DoAddAllProperties(object obj, PSMemberInfoInternalCollection 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; - } - /// - /// 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. - /// - /// object to retrieve the PSProperty from - /// name of the property to be retrieved - /// The PSProperty corresponding to propertyName from obj - 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); - } - - /// - /// Returns the name of the type corresponding to the property - /// - /// PSProperty obtained in a previous DoGetProperty - /// True if the result is for display purposes only - /// the name of the type corresponding to the property - 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; - } - - /// - /// Returns true if the property is settable - /// - /// property to check - /// true if the property is settable - protected override bool PropertyIsSettable(PSProperty property) - { - string columnName = (string)property.adapterData; - DataRow dataRow = (DataRow)property.baseObject; - return !dataRow.Table.Columns[columnName].ReadOnly; - } - - /// - /// Returns true if the property is gettable - /// - /// property to check - /// true if the property is gettable - protected override bool PropertyIsGettable(PSProperty property) - { - return true; - } - - - /// - /// Returns the value from a property coming from a previous call to DoGetProperty - /// - /// PSProperty coming from a previous call to DoGetProperty - /// The value of the property - protected override object PropertyGet(PSProperty property) - { - DataRow dataRow = (DataRow)property.baseObject; - return dataRow[(string)property.adapterData]; - } - /// - /// Sets the value of a property coming from a previous call to DoGetProperty - /// - /// PSProperty coming from a previous call to DoGetProperty - /// value to set the property with - /// instructs the adapter to convert before setting, if the adapter supports conversion - protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible) - { - DataRow dataRow = (DataRow)property.baseObject; - dataRow[(string)property.adapterData] = setValue; - return; - } - #endregion virtual - } - /// - /// Deals with DataRowView objects - /// - internal class DataRowViewAdapter : PropertyOnlyAdapter - { - #region virtual - /// - /// Retrieves all the properties available in the object. - /// - /// object to get all the property information from - /// collection where the members will be added - protected override void DoAddAllProperties(object obj, PSMemberInfoInternalCollection 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; - } - /// - /// 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. - /// - /// object to retrieve the PSProperty from - /// name of the property to be retrieved - /// The PSProperty corresponding to propertyName from obj - 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); - } - - /// - /// Returns the name of the type corresponding to the property - /// - /// PSProperty obtained in a previous DoGetProperty - /// True if the result is for display purposes only - /// the name of the type corresponding to the property - 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; - } - - /// - /// Returns true if the property is settable - /// - /// property to check - /// true if the property is settable - protected override bool PropertyIsSettable(PSProperty property) - { - string columnName = (string)property.adapterData; - DataRowView dataRowView = (DataRowView)property.baseObject; - return !dataRowView.Row.Table.Columns[columnName].ReadOnly; - } - - /// - /// Returns true if the property is gettable - /// - /// property to check - /// true if the property is gettable - protected override bool PropertyIsGettable(PSProperty property) - { - return true; - } - - /// - /// Returns the value from a property coming from a previous call to DoGetProperty - /// - /// PSProperty coming from a previous call to DoGetProperty - /// The value of the property - protected override object PropertyGet(PSProperty property) - { - DataRowView dataRowView = (DataRowView)property.baseObject; - return dataRowView[(string)property.adapterData]; - } - /// - /// Sets the value of a property coming from a previous call to DoGetProperty - /// - /// PSProperty coming from a previous call to DoGetProperty - /// value to set the property with - /// instructs the adapter to convert before setting, if the adapter supports conversion - protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible) - { - DataRowView dataRowView = (DataRowView)property.baseObject; - dataRowView[(string)property.adapterData] = setValue; - return; - } - #endregion virtual - } /// /// Deals with DirectoryEntry objects /// diff --git a/src/System.Management.Automation/engine/MshObject.cs b/src/System.Management.Automation/engine/MshObject.cs index 197bdd4c7b4..6dd16ab05bd 100644 --- a/src/System.Management.Automation/engine/MshObject.cs +++ b/src/System.Management.Automation/engine/MshObject.cs @@ -381,13 +381,13 @@ private void CommonInitialization(object obj) 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 @@ private static AdapterSet MappedInternalAdapterSet(object obj) 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; } diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 90fad60525f..2dad4d8ef02 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -554,7 +554,6 @@ public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, Dyna 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 @@ public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, Dyna target), GetRestrictions(target))).WriteToDebugLog(this); } -#endif if (IsComObject(targetValue)) { diff --git a/test/powershell/engine/ETS/Adapter.Tests.ps1 b/test/powershell/engine/ETS/Adapter.Tests.ps1 index 0762b58d746..8ab621038f0 100644 --- a/test/powershell/engine/ETS/Adapter.Tests.ps1 +++ b/test/powershell/engine/ETS/Adapter.Tests.ps1 @@ -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 + } + } +}