From 455fb2f43585348a6081436baeca78c6a44fb8b2 Mon Sep 17 00:00:00 2001 From: Carsten Schlote Date: Sun, 22 Aug 2010 02:31:54 +0200 Subject: [PATCH 1/5] [System.Configuration] Added check for SettingProviderAttribute on settings class There is no checkfor the SettingProviderAttribute attached to a Settings class. .NET uses this to replace the default LocalSettingsProvider with your own SettingsProvider. The patch adds this feature. Signed-off-by: Carsten Schlote --- .../ApplicationSettingsBase.cs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/mcs/class/System/System.Configuration/ApplicationSettingsBase.cs b/mcs/class/System/System.Configuration/ApplicationSettingsBase.cs index 9e96e4929941..6061a55cb421 100644 --- a/mcs/class/System/System.Configuration/ApplicationSettingsBase.cs +++ b/mcs/class/System/System.Configuration/ApplicationSettingsBase.cs @@ -272,11 +272,24 @@ public override SettingsPropertyCollection Properties { try { if (properties == null) { - LocalFileSettingsProvider local_provider = null; + SettingsProvider local_provider = null; properties = new SettingsPropertyCollection (); - foreach (PropertyInfo prop in GetType ().GetProperties ()) { // only public properties + Type this_type = GetType(); + SettingsProviderAttribute[] provider_attrs = (SettingsProviderAttribute[])this_type.GetCustomAttributes (typeof (SettingsProviderAttribute), false);; + if (provider_attrs != null && provider_attrs.Length != 0) { + Type provider_type = Type.GetType (provider_attrs[0].ProviderTypeName); + SettingsProvider provider = (SettingsProvider) Activator.CreateInstance (provider_type); + provider.Initialize (null, null); + if (provider != null && Providers [provider.Name] == null) { + Providers.Add (provider); + local_provider = provider; + } + } + + PropertyInfo[] type_props = this_type.GetProperties (); + foreach (PropertyInfo prop in type_props) { // only public properties SettingAttribute[] setting_attrs = (SettingAttribute[])prop.GetCustomAttributes (typeof (SettingAttribute), false); if (setting_attrs == null || setting_attrs.Length == 0) continue; @@ -292,7 +305,7 @@ public override SettingsPropertyCollection Properties { } } - void CreateSettingsProperty (PropertyInfo prop, SettingsPropertyCollection properties, ref LocalFileSettingsProvider local_provider) + void CreateSettingsProperty (PropertyInfo prop, SettingsPropertyCollection properties, ref SettingsProvider local_provider) { SettingsAttributeDictionary dict = new SettingsAttributeDictionary (); SettingsProvider provider = null; @@ -346,7 +359,7 @@ void CreateSettingsProperty (PropertyInfo prop, SettingsPropertyCollection prope if (provider == null) { if (local_provider == null) { - local_provider = new LocalFileSettingsProvider (); + local_provider = new LocalFileSettingsProvider () as SettingsProvider; local_provider.Initialize (null, null); } setting.Provider = local_provider; From 8b838effddaffa6c7889107e4256c87d38df0332 Mon Sep 17 00:00:00 2001 From: Carsten Schlote Date: Sun, 22 Aug 2010 15:50:52 +0200 Subject: [PATCH 2/5] [System.Configuration] Fixed GetEvidenceHash() function to produce valid filenames The previous implementation creates eventually invalid filenames in a random fashion on linux, mac, windows by having '/' or '\' chars in the base64 output. As a result you can't save or retrieve your settings due to filesystem errors. This patch changes the output to plain hex values. This is a bit longer then a base64 string, but also 100% filesystem safe. Might Signed-off-by: Carsten Schlote --- .../CustomizableFileSettingsProvider.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs b/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs index 3b5715d1926f..04d15311f2fa 100644 --- a/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs +++ b/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs @@ -299,8 +299,10 @@ private static string GetProductName () #endif } - // FIXME: it seems that something else is used - // here, to convert hash bytes to string. + // Note: Changed from base64() to hex output to avoid unexpected chars like '\' or '/' with filesystem meaning. + // Otherwise eventually filenames, which are invalid on linux or windows, might be created. + // Signed-off-by: Carsten Schlote + // TODO: Compare with .NET. It might be also, that their way isn't suitable for Unix OS derivates (slahes in output) private static string GetEvidenceHash () { Assembly assembly = Assembly.GetEntryAssembly (); @@ -308,9 +310,11 @@ private static string GetEvidenceHash () assembly = Assembly.GetCallingAssembly (); byte [] pkt = assembly.GetName ().GetPublicKeyToken (); - byte [] hash = SHA1.Create ().ComputeHash (pkt != null ? pkt : Encoding.UTF8.GetBytes (assembly.EscapedCodeBase)); - - return Convert.ToBase64String (hash); + byte [] hash = SHA1.Create ().ComputeHash (pkt != null && pkt.Length >0 ? pkt : Encoding.UTF8.GetBytes (assembly.EscapedCodeBase)); + System.Text.StringBuilder evidence_string = new System.Text.StringBuilder(); + foreach (byte b in hash) + evidence_string.AppendFormat("{0:x2}",b); + return evidence_string.ToString (); } private static string GetProductVersion () From 36e21cce244678d97f1eb369b311432bb0439fe7 Mon Sep 17 00:00:00 2001 From: Carsten Schlote Date: Sun, 22 Aug 2010 15:54:44 +0200 Subject: [PATCH 3/5] [System.Configuration] Fixed GetProductName() function Added missing check for empty strongname. This case is now handled the same way as a missing strongname. GetProductName() now correctly uses _Url_ and assembly.EscapedCodeBase for its output. Signed-off-by: Carsten Schlote --- .../System.Configuration/CustomizableFileSettingsProvider.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs b/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs index 04d15311f2fa..ddf742a1c16b 100644 --- a/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs +++ b/mcs/class/System/System.Configuration/CustomizableFileSettingsProvider.cs @@ -282,13 +282,11 @@ private static string GetProductName () assembly = Assembly.GetCallingAssembly (); #if !TARGET_JVM - byte [] pkt = assembly.GetName ().GetPublicKeyToken (); return String.Format ("{0}_{1}_{2}", AppDomain.CurrentDomain.FriendlyName, - pkt != null ? "StrongName" : "Url", + pkt != null && pkt.Length > 0 ? "StrongName" : "Url", GetEvidenceHash()); - #else // AssemblyProductAttribute-based code AssemblyProductAttribute [] attrs = (AssemblyProductAttribute[]) assembly.GetCustomAttributes (typeof (AssemblyProductAttribute), true); From 87f28a334be722968e3728ab1dee7035eb1c8a62 Mon Sep 17 00:00:00 2001 From: Carsten Schlote Date: Thu, 11 Nov 2010 17:35:41 +0100 Subject: [PATCH 4/5] [System.Data] Fixes for broken RO check - When calling GetChanges() the ReadOnly check for DataCalumns must be turned off, as otherwise data of readonly columns can't be cloned into new rows for the new created DataSet/dataTable Signed-off-by: Carsten Schlote --- mcs/class/System.Data/System.Data/DataRow.cs | 13 +++++++++---- mcs/class/System.Data/System.Data/DataSet.cs | 3 ++- mcs/class/System.Data/System.Data/DataTable.cs | 3 ++- mcs/class/System.Data/System.Data/MergeManager.cs | 3 ++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/mcs/class/System.Data/System.Data/DataRow.cs b/mcs/class/System.Data/System.Data/DataRow.cs index 941bc1f9097e..bef0d3054668 100644 --- a/mcs/class/System.Data/System.Data/DataRow.cs +++ b/mcs/class/System.Data/System.Data/DataRow.cs @@ -487,7 +487,7 @@ internal void ImportRecord (int record) CheckValue (this [col], col, false); } - void CheckValue (object v, DataColumn col) + private void CheckValue (object v, DataColumn col) { CheckValue (v, col, true); } @@ -1399,6 +1399,11 @@ public void SetParentRow (DataRow parentRow, DataRelation relation) //Copy all values of this DataRow to the row parameter. internal void CopyValuesToRow (DataRow row) + { + CopyValuesToRow(row, true); + } + + internal void CopyValuesToRow (DataRow row, bool doROCheck) { if (row == null) throw new ArgumentNullException("row"); @@ -1454,19 +1459,19 @@ internal void CopyValuesToRow (DataRow row) if (targetColumn != null) { if (HasVersion (DataRowVersion.Original)) { object val = column[Original]; - row.CheckValue (val, targetColumn); + row.CheckValue (val, targetColumn, doROCheck); targetColumn [row.Original] = val; } if (HasVersion (DataRowVersion.Current) && Current != Original) { object val = column[Current]; - row.CheckValue (val, targetColumn); + row.CheckValue (val, targetColumn, doROCheck); targetColumn [row.Current] = val; } if (HasVersion (DataRowVersion.Proposed)) { object val = column[row.Proposed]; - row.CheckValue (val, targetColumn); + row.CheckValue (val, targetColumn, doROCheck); targetColumn [row.Proposed] = val; } } diff --git a/mcs/class/System.Data/System.Data/DataSet.cs b/mcs/class/System.Data/System.Data/DataSet.cs index b62aff1e4d78..3b0f0c4e9adb 100644 --- a/mcs/class/System.Data/System.Data/DataSet.cs +++ b/mcs/class/System.Data/System.Data/DataSet.cs @@ -568,7 +568,8 @@ private void AddChangedRow (Hashtable addedRows, DataTable copyTable, DataRow ro // add the current row DataRow newRow = copyTable.NewNotInitializedRow (); copyTable.Rows.AddInternal (newRow); - row.CopyValuesToRow (newRow); + // Don't check for ReadOnly, when cloning data to new uninitialized row. + row.CopyValuesToRow (newRow, false); newRow.XmlRowID = row.XmlRowID; addedRows.Add (row, row); } diff --git a/mcs/class/System.Data/System.Data/DataTable.cs b/mcs/class/System.Data/System.Data/DataTable.cs index 21f54308f397..f622744cf390 100644 --- a/mcs/class/System.Data/System.Data/DataTable.cs +++ b/mcs/class/System.Data/System.Data/DataTable.cs @@ -996,7 +996,8 @@ public DataTable GetChanges (DataRowState rowStates) if (copyTable == null) copyTable = Clone (); DataRow newRow = copyTable.NewNotInitializedRow (); - row.CopyValuesToRow (newRow); + // Don't check for ReadOnly, when cloning data to new uninitialized row. + row.CopyValuesToRow (newRow, false); #if NET_2_0 newRow.XmlRowID = row.XmlRowID; #endif diff --git a/mcs/class/System.Data/System.Data/MergeManager.cs b/mcs/class/System.Data/System.Data/MergeManager.cs index 09b8f58ff458..911ca37f36fc 100644 --- a/mcs/class/System.Data/System.Data/MergeManager.cs +++ b/mcs/class/System.Data/System.Data/MergeManager.cs @@ -148,7 +148,8 @@ private static void MergeRow(DataTable targetTable, DataRow row, bool preserveCh if (targetRow == null) { DataRow newRow = targetTable.NewNotInitializedRow(); - row.CopyValuesToRow(newRow); + // Don't check for ReadOnly, when cloning data to new uninitialized row. + row.CopyValuesToRow(newRow, false); targetTable.Rows.AddInternal (newRow); } // row exists in target table, and presere changes is false - From cf131b812205f4589079e055cb4ceefd876ea28f Mon Sep 17 00:00:00 2001 From: Carsten Schlote Date: Fri, 12 Nov 2010 16:56:46 +0100 Subject: [PATCH 5/5] [System.Data] Extended CloneCopy() test with ReadOnly field - It is recommended to set auto-increment fields to ReadOnly. When calling Copy() the check for ReadOnly must be omitted, otherwise you get an exception. The RadOnly attribute is set for the parent "Id" field to test proper handling of read-only fields. - Source reindention and cleanups. Signed-off-by: Carsten Schlote --- .../Test/System.Data/DataSetTest.cs | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/mcs/class/System.Data/Test/System.Data/DataSetTest.cs b/mcs/class/System.Data/Test/System.Data/DataSetTest.cs index b6a49eaa6dd9..3ea16fd7b378 100644 --- a/mcs/class/System.Data/Test/System.Data/DataSetTest.cs +++ b/mcs/class/System.Data/Test/System.Data/DataSetTest.cs @@ -1211,46 +1211,54 @@ public void WriteDiffReadAutoWriteSchema () [Test] public void CloneCopy () { - DataTable table = new DataTable ("pTable"); DataTable table1 = new DataTable ("cTable"); DataSet set = new DataSet (); - + DataTable table = new DataTable ("pTable"); + DataTable table1 = new DataTable ("cTable"); + DataSet set = new DataSet (); + set.Tables.Add (table); - set.Tables.Add (table1); DataColumn col = new DataColumn (); + set.Tables.Add (table1); + + DataColumn col = new DataColumn (); col.ColumnName = "Id"; col.DataType = Type.GetType ("System.Int32"); table.Columns.Add (col); UniqueConstraint uc = new UniqueConstraint ("UK1", table.Columns[0] ); table.Constraints.Add (uc); - + col = new DataColumn (); col.ColumnName = "Name"; col.DataType = Type.GetType ("System.String"); table.Columns.Add (col); - + col = new DataColumn (); col.ColumnName = "Id"; col.DataType = Type.GetType ("System.Int32"); table1.Columns.Add (col); - + col = new DataColumn (); col.ColumnName = "Name"; col.DataType = Type.GetType ("System.String"); table1.Columns.Add (col); ForeignKeyConstraint fc = new ForeignKeyConstraint ("FK1", table.Columns[0], table1.Columns[0] ); table1.Constraints.Add (fc); - - + + DataRow row = table.NewRow (); - + row ["Id"] = 147; row ["name"] = "Row1"; row.RowError = "Error#1"; table.Rows.Add (row); - + + // Set column to RO as commonly used by auto-increment fields. + // ds.Copy() has to omit the RO check when cloning DataRows + table.Columns["Id"].ReadOnly = true; + row = table1.NewRow (); row ["Id"] = 147; row ["Name"] = "Row1"; table1.Rows.Add (row); - + //Setting properties of DataSet set.CaseSensitive = true; set.DataSetName = "My DataSet"; @@ -1262,7 +1270,7 @@ public void CloneCopy () set.ExtendedProperties.Add ("TimeStamp", DateTime.Now); CultureInfo cultureInfo = new CultureInfo( "ar-SA" ); set.Locale = cultureInfo; - + //Testing Copy () DataSet copySet = set.Copy (); Assert.AreEqual (set.CaseSensitive, copySet.CaseSensitive, "#A01");