From 3921f88e309824a467342c0cb0fdc0f431e6181f Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 9 Feb 2016 00:47:22 +0100
Subject: [PATCH 01/48] implemented functionality for changing the key for
dictionary items
---
.../settings/EditDictionaryItem.aspx.cs | 39 ++++++++++++++++---
src/umbraco.cms/businesslogic/Dictionary.cs | 6 +++
2 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
index deb77f4bad..ca07004f44 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
@@ -25,6 +25,7 @@ namespace umbraco.settings
protected uicontrols.TabView tbv = new uicontrols.TabView();
private System.Collections.ArrayList languageFields = new System.Collections.ArrayList();
private cms.businesslogic.Dictionary.DictionaryItem currentItem;
+ protected TextBox keyNameBox;
protected void Page_Load(object sender, System.EventArgs e)
{
@@ -33,16 +34,16 @@ namespace umbraco.settings
// Put user code to initialize the page here
Panel1.hasMenu = true;
Panel1.Text = ui.Text("editdictionary") + ": " + currentItem.key;
-
- uicontrols.Pane p = new uicontrols.Pane();
- var save = Panel1.Menu.NewButton();
+ var save = Panel1.Menu.NewButton();
save.Text = ui.Text("save");
save.Click += save_Click;
save.ToolTip = ui.Text("save");
save.ID = "save";
save.ButtonType = uicontrols.MenuButtonType.Primary;
+ uicontrols.Pane p = new uicontrols.Pane();
+
Literal txt = new Literal();
txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, base.getUser()) + "
";
p.addProperty(txt);
@@ -63,7 +64,23 @@ namespace umbraco.settings
}
- if (!IsPostBack)
+ keyNameBox = new TextBox
+ {
+ ID = "editname-" + currentItem.id,
+ CssClass = "umbEditorTextField",
+ Text = currentItem.key
+ };
+
+ var txtChangeKey = new Literal
+ {
+ Text = "
" +
+ "Change the key of the dictionary item. Carefull :)
"
+ };
+
+ p.addProperty(txtChangeKey);
+ p.addProperty(keyNameBox);
+
+ if (!IsPostBack)
{
var path = BuildPath(currentItem);
ClientTools
@@ -71,7 +88,6 @@ namespace umbraco.settings
.SyncTree(path, false);
}
-
Panel1.Controls.Add(p);
}
@@ -92,8 +108,21 @@ namespace umbraco.settings
currentItem.setValue(int.Parse(t.ID),t.Text);
}
}
+
+ var newKey = keyNameBox.Text;
+ if (string.IsNullOrWhiteSpace(newKey) == false && newKey != currentItem.key)
+ {
+ currentItem.setKey(newKey);
+
+ Panel1.title.InnerHtml = ui.Text("editdictionary") + ": " + currentItem.key;
+
+ var path = BuildPath(currentItem);
+ ClientTools.SyncTree(path, true);
+ }
+
ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), "");
}
+
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
diff --git a/src/umbraco.cms/businesslogic/Dictionary.cs b/src/umbraco.cms/businesslogic/Dictionary.cs
index 8e76122175..206965168e 100644
--- a/src/umbraco.cms/businesslogic/Dictionary.cs
+++ b/src/umbraco.cms/businesslogic/Dictionary.cs
@@ -169,6 +169,12 @@ namespace umbraco.cms.businesslogic
}
}
+ public void setKey(string value)
+ {
+ key = value;
+ Save();
+ }
+
public string Value(int languageId)
{
if (languageId == 0)
From eec651c9d3d466d9f68afdc6b0757a0515a6fdfd Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 9 Feb 2016 00:47:22 +0100
Subject: [PATCH 02/48] implemented functionality for changing the key for
dictionary items
---
.../settings/EditDictionaryItem.aspx.cs | 39 ++++++++++++++++---
src/umbraco.cms/businesslogic/Dictionary.cs | 6 +++
2 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
index deb77f4bad..ca07004f44 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
@@ -25,6 +25,7 @@ namespace umbraco.settings
protected uicontrols.TabView tbv = new uicontrols.TabView();
private System.Collections.ArrayList languageFields = new System.Collections.ArrayList();
private cms.businesslogic.Dictionary.DictionaryItem currentItem;
+ protected TextBox keyNameBox;
protected void Page_Load(object sender, System.EventArgs e)
{
@@ -33,16 +34,16 @@ namespace umbraco.settings
// Put user code to initialize the page here
Panel1.hasMenu = true;
Panel1.Text = ui.Text("editdictionary") + ": " + currentItem.key;
-
- uicontrols.Pane p = new uicontrols.Pane();
- var save = Panel1.Menu.NewButton();
+ var save = Panel1.Menu.NewButton();
save.Text = ui.Text("save");
save.Click += save_Click;
save.ToolTip = ui.Text("save");
save.ID = "save";
save.ButtonType = uicontrols.MenuButtonType.Primary;
+ uicontrols.Pane p = new uicontrols.Pane();
+
Literal txt = new Literal();
txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, base.getUser()) + "
";
p.addProperty(txt);
@@ -63,7 +64,23 @@ namespace umbraco.settings
}
- if (!IsPostBack)
+ keyNameBox = new TextBox
+ {
+ ID = "editname-" + currentItem.id,
+ CssClass = "umbEditorTextField",
+ Text = currentItem.key
+ };
+
+ var txtChangeKey = new Literal
+ {
+ Text = "
" +
+ "Change the key of the dictionary item. Carefull :)
"
+ };
+
+ p.addProperty(txtChangeKey);
+ p.addProperty(keyNameBox);
+
+ if (!IsPostBack)
{
var path = BuildPath(currentItem);
ClientTools
@@ -71,7 +88,6 @@ namespace umbraco.settings
.SyncTree(path, false);
}
-
Panel1.Controls.Add(p);
}
@@ -92,8 +108,21 @@ namespace umbraco.settings
currentItem.setValue(int.Parse(t.ID),t.Text);
}
}
+
+ var newKey = keyNameBox.Text;
+ if (string.IsNullOrWhiteSpace(newKey) == false && newKey != currentItem.key)
+ {
+ currentItem.setKey(newKey);
+
+ Panel1.title.InnerHtml = ui.Text("editdictionary") + ": " + currentItem.key;
+
+ var path = BuildPath(currentItem);
+ ClientTools.SyncTree(path, true);
+ }
+
ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), "");
}
+
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
diff --git a/src/umbraco.cms/businesslogic/Dictionary.cs b/src/umbraco.cms/businesslogic/Dictionary.cs
index 8e76122175..206965168e 100644
--- a/src/umbraco.cms/businesslogic/Dictionary.cs
+++ b/src/umbraco.cms/businesslogic/Dictionary.cs
@@ -169,6 +169,12 @@ namespace umbraco.cms.businesslogic
}
}
+ public void setKey(string value)
+ {
+ key = value;
+ Save();
+ }
+
public string Value(int languageId)
{
if (languageId == 0)
From 9427979b1c5b4a7ce0525779afe0cba22c623b54 Mon Sep 17 00:00:00 2001
From: Mads Krohn
Date: Tue, 16 Feb 2016 00:06:56 +0100
Subject: [PATCH 03/48] Check if the new key already exists. Added error
message. Enhanced error handling logic.
---
.../settings/EditDictionaryItem.aspx.cs | 46 +++++++++++++------
1 file changed, 33 insertions(+), 13 deletions(-)
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
index ca07004f44..b0dd94c869 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
@@ -8,6 +8,7 @@ using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
+using umbraco.cms.businesslogic;
using umbraco.cms.presentation.Trees;
using Umbraco.Core;
using Umbraco.Core.IO;
@@ -25,7 +26,8 @@ namespace umbraco.settings
protected uicontrols.TabView tbv = new uicontrols.TabView();
private System.Collections.ArrayList languageFields = new System.Collections.ArrayList();
private cms.businesslogic.Dictionary.DictionaryItem currentItem;
- protected TextBox keyNameBox;
+ protected TextBox boxChangeKey;
+ protected Label labelChangeKey;
protected void Page_Load(object sender, System.EventArgs e)
{
@@ -64,21 +66,26 @@ namespace umbraco.settings
}
- keyNameBox = new TextBox
+ boxChangeKey = new TextBox
{
- ID = "editname-" + currentItem.id,
+ ID = "changeKey-" + currentItem.id,
CssClass = "umbEditorTextField",
Text = currentItem.key
};
- var txtChangeKey = new Literal
+ labelChangeKey = new Label
+ {
+ ID = "changeKeyLabel",
+ CssClass = "text-error"
+ };
+
+ p.addProperty(new Literal
{
Text = "
" +
"Change the key of the dictionary item. Carefull :)
"
- };
-
- p.addProperty(txtChangeKey);
- p.addProperty(keyNameBox);
+ });
+ p.addProperty(boxChangeKey);
+ p.addProperty(labelChangeKey);
if (!IsPostBack)
{
@@ -109,15 +116,28 @@ namespace umbraco.settings
}
}
- var newKey = keyNameBox.Text;
+ labelChangeKey.Text = "";
+ var newKey = boxChangeKey.Text;
if (string.IsNullOrWhiteSpace(newKey) == false && newKey != currentItem.key)
{
- currentItem.setKey(newKey);
+ // key already exists, save but inform
+ if (Dictionary.DictionaryItem.hasKey(newKey) == true)
+ {
+ labelChangeKey.Text = "The key '" + newKey + "' already exists, sorry..";
+ boxChangeKey.Text = currentItem.key; // reset the key
+ }
+ else
+ {
+ // set the new key
+ currentItem.setKey(newKey);
- Panel1.title.InnerHtml = ui.Text("editdictionary") + ": " + currentItem.key;
+ // update the title with the new key
+ Panel1.title.InnerHtml = ui.Text("editdictionary") + ": " + newKey;
- var path = BuildPath(currentItem);
- ClientTools.SyncTree(path, true);
+ // sync the content tree
+ var path = BuildPath(currentItem);
+ ClientTools.SyncTree(path, true);
+ }
}
ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), "");
From 56492cd1b20360490ab4749f18bc31b8b7214b8a Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 9 Feb 2016 00:47:22 +0100
Subject: [PATCH 04/48] implemented functionality for changing the key for
dictionary items
---
.../settings/EditDictionaryItem.aspx.cs | 39 ++++++++++++++++---
src/umbraco.cms/businesslogic/Dictionary.cs | 6 +++
2 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
index deb77f4bad..ca07004f44 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
@@ -25,6 +25,7 @@ namespace umbraco.settings
protected uicontrols.TabView tbv = new uicontrols.TabView();
private System.Collections.ArrayList languageFields = new System.Collections.ArrayList();
private cms.businesslogic.Dictionary.DictionaryItem currentItem;
+ protected TextBox keyNameBox;
protected void Page_Load(object sender, System.EventArgs e)
{
@@ -33,16 +34,16 @@ namespace umbraco.settings
// Put user code to initialize the page here
Panel1.hasMenu = true;
Panel1.Text = ui.Text("editdictionary") + ": " + currentItem.key;
-
- uicontrols.Pane p = new uicontrols.Pane();
- var save = Panel1.Menu.NewButton();
+ var save = Panel1.Menu.NewButton();
save.Text = ui.Text("save");
save.Click += save_Click;
save.ToolTip = ui.Text("save");
save.ID = "save";
save.ButtonType = uicontrols.MenuButtonType.Primary;
+ uicontrols.Pane p = new uicontrols.Pane();
+
Literal txt = new Literal();
txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, base.getUser()) + "
";
p.addProperty(txt);
@@ -63,7 +64,23 @@ namespace umbraco.settings
}
- if (!IsPostBack)
+ keyNameBox = new TextBox
+ {
+ ID = "editname-" + currentItem.id,
+ CssClass = "umbEditorTextField",
+ Text = currentItem.key
+ };
+
+ var txtChangeKey = new Literal
+ {
+ Text = "
" +
+ "Change the key of the dictionary item. Carefull :)
"
+ };
+
+ p.addProperty(txtChangeKey);
+ p.addProperty(keyNameBox);
+
+ if (!IsPostBack)
{
var path = BuildPath(currentItem);
ClientTools
@@ -71,7 +88,6 @@ namespace umbraco.settings
.SyncTree(path, false);
}
-
Panel1.Controls.Add(p);
}
@@ -92,8 +108,21 @@ namespace umbraco.settings
currentItem.setValue(int.Parse(t.ID),t.Text);
}
}
+
+ var newKey = keyNameBox.Text;
+ if (string.IsNullOrWhiteSpace(newKey) == false && newKey != currentItem.key)
+ {
+ currentItem.setKey(newKey);
+
+ Panel1.title.InnerHtml = ui.Text("editdictionary") + ": " + currentItem.key;
+
+ var path = BuildPath(currentItem);
+ ClientTools.SyncTree(path, true);
+ }
+
ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), "");
}
+
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
diff --git a/src/umbraco.cms/businesslogic/Dictionary.cs b/src/umbraco.cms/businesslogic/Dictionary.cs
index 8e76122175..206965168e 100644
--- a/src/umbraco.cms/businesslogic/Dictionary.cs
+++ b/src/umbraco.cms/businesslogic/Dictionary.cs
@@ -169,6 +169,12 @@ namespace umbraco.cms.businesslogic
}
}
+ public void setKey(string value)
+ {
+ key = value;
+ Save();
+ }
+
public string Value(int languageId)
{
if (languageId == 0)
From 330cd7a4092b1b81325bb550ec7372c16999a1c5 Mon Sep 17 00:00:00 2001
From: Mads Krohn
Date: Tue, 16 Feb 2016 00:06:56 +0100
Subject: [PATCH 05/48] Check if the new key already exists. Added error
message. Enhanced error handling logic.
---
.../settings/EditDictionaryItem.aspx.cs | 46 +++++++++++++------
1 file changed, 33 insertions(+), 13 deletions(-)
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
index ca07004f44..b0dd94c869 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
@@ -8,6 +8,7 @@ using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
+using umbraco.cms.businesslogic;
using umbraco.cms.presentation.Trees;
using Umbraco.Core;
using Umbraco.Core.IO;
@@ -25,7 +26,8 @@ namespace umbraco.settings
protected uicontrols.TabView tbv = new uicontrols.TabView();
private System.Collections.ArrayList languageFields = new System.Collections.ArrayList();
private cms.businesslogic.Dictionary.DictionaryItem currentItem;
- protected TextBox keyNameBox;
+ protected TextBox boxChangeKey;
+ protected Label labelChangeKey;
protected void Page_Load(object sender, System.EventArgs e)
{
@@ -64,21 +66,26 @@ namespace umbraco.settings
}
- keyNameBox = new TextBox
+ boxChangeKey = new TextBox
{
- ID = "editname-" + currentItem.id,
+ ID = "changeKey-" + currentItem.id,
CssClass = "umbEditorTextField",
Text = currentItem.key
};
- var txtChangeKey = new Literal
+ labelChangeKey = new Label
+ {
+ ID = "changeKeyLabel",
+ CssClass = "text-error"
+ };
+
+ p.addProperty(new Literal
{
Text = "
" +
"Change the key of the dictionary item. Carefull :)
"
- };
-
- p.addProperty(txtChangeKey);
- p.addProperty(keyNameBox);
+ });
+ p.addProperty(boxChangeKey);
+ p.addProperty(labelChangeKey);
if (!IsPostBack)
{
@@ -109,15 +116,28 @@ namespace umbraco.settings
}
}
- var newKey = keyNameBox.Text;
+ labelChangeKey.Text = "";
+ var newKey = boxChangeKey.Text;
if (string.IsNullOrWhiteSpace(newKey) == false && newKey != currentItem.key)
{
- currentItem.setKey(newKey);
+ // key already exists, save but inform
+ if (Dictionary.DictionaryItem.hasKey(newKey) == true)
+ {
+ labelChangeKey.Text = "The key '" + newKey + "' already exists, sorry..";
+ boxChangeKey.Text = currentItem.key; // reset the key
+ }
+ else
+ {
+ // set the new key
+ currentItem.setKey(newKey);
- Panel1.title.InnerHtml = ui.Text("editdictionary") + ": " + currentItem.key;
+ // update the title with the new key
+ Panel1.title.InnerHtml = ui.Text("editdictionary") + ": " + newKey;
- var path = BuildPath(currentItem);
- ClientTools.SyncTree(path, true);
+ // sync the content tree
+ var path = BuildPath(currentItem);
+ ClientTools.SyncTree(path, true);
+ }
}
ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), "");
From ce897509bae64ddc120fb6c720c1ed77cf077c1b Mon Sep 17 00:00:00 2001
From: Mads Krohn
Date: Tue, 16 Feb 2016 10:44:43 +0100
Subject: [PATCH 06/48] added localization
---
src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 6 ++++++
src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 6 ++++++
src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 6 ++++++
.../umbraco/settings/EditDictionaryItem.aspx.cs | 14 ++++++++------
4 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
index 446796dd88..f90d3a5ec6 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
@@ -236,6 +236,12 @@
Rediger de forskellige sprogversioner for ordbogselementet '%0%' herunder. Du tilføjer flere sprog under 'sprog' i menuen til venstre
Kulturnavn
+ Her kan du ændre nøglen på ordbogselementet.
+
+
+
Indtast dit brugernavn
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
index b14b101af7..c953e899cc 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
@@ -284,6 +284,12 @@
Edit the different language versions for the dictionary item '%0% ' below You can add additional languages under the 'languages' in the menu on the left
]]>
Culture Name
+ Here you can change the key of the dictionary item.
+
+
+
Enter your username
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
index 1cc40abd1d..012d7074e7 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
@@ -285,6 +285,12 @@
Edit the different language versions for the dictionary item '%0% ' below You can add additional languages under the 'languages' in the menu on the left
]]>
Culture Name
+ Here you can change the key of the dictionary item.
+
+
+
Enter your username
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
index b0dd94c869..c66a6ca37f 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
@@ -8,6 +8,7 @@ using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
+using umbraco.BusinessLogic;
using umbraco.cms.businesslogic;
using umbraco.cms.presentation.Trees;
using Umbraco.Core;
@@ -28,10 +29,12 @@ namespace umbraco.settings
private cms.businesslogic.Dictionary.DictionaryItem currentItem;
protected TextBox boxChangeKey;
protected Label labelChangeKey;
+ protected User currentUser;
protected void Page_Load(object sender, System.EventArgs e)
{
currentItem = new cms.businesslogic.Dictionary.DictionaryItem(int.Parse(Request.QueryString["id"]));
+ currentUser = getUser();
// Put user code to initialize the page here
Panel1.hasMenu = true;
@@ -47,7 +50,7 @@ namespace umbraco.settings
uicontrols.Pane p = new uicontrols.Pane();
Literal txt = new Literal();
- txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, base.getUser()) + "
";
+ txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, currentUser) + "
";
p.addProperty(txt);
foreach (cms.businesslogic.language.Language l in cms.businesslogic.language.Language.getAll)
@@ -81,8 +84,7 @@ namespace umbraco.settings
p.addProperty(new Literal
{
- Text = "
" +
- "Change the key of the dictionary item. Carefull :)
"
+ Text = "
" + ui.Text("dictionaryItem", "changeKey") + "
"
});
p.addProperty(boxChangeKey);
p.addProperty(labelChangeKey);
@@ -116,15 +118,15 @@ namespace umbraco.settings
}
}
- labelChangeKey.Text = "";
+ labelChangeKey.Text = ""; // reset error text
var newKey = boxChangeKey.Text;
if (string.IsNullOrWhiteSpace(newKey) == false && newKey != currentItem.key)
{
// key already exists, save but inform
if (Dictionary.DictionaryItem.hasKey(newKey) == true)
{
- labelChangeKey.Text = "The key '" + newKey + "' already exists, sorry..";
- boxChangeKey.Text = currentItem.key; // reset the key
+ labelChangeKey.Text = ui.Text("dictionaryItem", "changeKeyError", newKey, currentUser);
+ boxChangeKey.Text = currentItem.key; // reset key
}
else
{
From d8eaab2aa4f4e9207c1b3c569a8f3943ef7c3218 Mon Sep 17 00:00:00 2001
From: Mads Krohn
Date: Tue, 16 Feb 2016 11:02:29 +0100
Subject: [PATCH 07/48] added a missing to the danish translation. added
current user to the changeKey text.
---
src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 4 +++-
.../umbraco/settings/EditDictionaryItem.aspx.cs | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
index f90d3a5ec6..9e24a6e167 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
@@ -234,7 +234,9 @@
Se Cache Item
- Rediger de forskellige sprogversioner for ordbogselementet '%0%' herunder. Du tilføjer flere sprog under 'sprog' i menuen til venstre
+ Du tilføjer flere sprog under 'sprog' i menuen til venstre
+ ]]>
Kulturnavn
Her kan du ændre nøglen på ordbogselementet.
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
index c66a6ca37f..dcece3d955 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/EditDictionaryItem.aspx.cs
@@ -84,7 +84,7 @@ namespace umbraco.settings
p.addProperty(new Literal
{
- Text = "
" + ui.Text("dictionaryItem", "changeKey") + "
"
+ Text = "
" + ui.Text("dictionaryItem", "changeKey", currentUser) + "
"
});
p.addProperty(boxChangeKey);
p.addProperty(labelChangeKey);
From d74a30e4e405dca3ad970682d695c1c65b1f8d1e Mon Sep 17 00:00:00 2001
From: michael
Date: Fri, 9 Sep 2016 13:17:53 +0200
Subject: [PATCH 08/48] Review and add translation labels to language file
fr.xml
---
src/Umbraco.Web.UI/umbraco/config/lang/fr.xml | 985 ++++++++++++------
1 file changed, 662 insertions(+), 323 deletions(-)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
index 59021956a5..2e5ee51bac 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
@@ -26,7 +26,8 @@
Publier
Dépublier
Rafraîchir
- Republier le site entier
+ Republier le site tout entier
+ Récupérer
Permissions
Version antérieure
Envoyer pour publication
@@ -39,13 +40,13 @@
Permission refusée.
- Ajouter nouveau domaine
+ Ajouter un nouveau domaine
Supprimer
Noeud invalide.
Domaine invalide.
Domaine déjà assigné.
Domaine
- Langage
+ Langue
Nouveau domaine '%0%' créé
Domaine '%0%' supprimé
Domaine '%0%' déjà assigné
@@ -53,24 +54,25 @@
"https://www.example.com/". Les domaines contenant un chemin d'un niveau sont autorisés, ex : "example.com/en". Pour autant, cela
devrait être évité. Utilisez plutôt la gestion des noms d'hôte.]]>
Domaine '%0%' mis à jour
- Editer les domaines
+ Editer les domaines actuels
Hériter
Culture
- ou hériter la culture des noeuds parent. S'appliquera aussi'
- au noeud courant, à moins qu'un domaine soit appliqué aussi.]]>
+ ou hériter de la culture des noeuds parents. S'appliquera aussi
+ au noeud courant, à moins qu'un domaine ci-dessous soit aussi d'application.]]>
Domaines
- Voir pour
+ Aperçu pour
+ Vider la sélection
Choisir
Choisir le répertoire courant
Faire autre chose
Gras
Annuler l'indentation de paragraphe
- Insérer champ de formulaire
- Insérer une entête graphique
+ Insérer un champ de formulaire
+ Insérer un entête graphique
Editer le HTML
Indenter le paragraphe
Italique
@@ -78,42 +80,47 @@
Justifier à gauche
Justifier à droite
Insérer un lien
- Insérer une ancre
+ Insérer un lien local (ancre)
Liste à puces
Liste numérique
Insérer une macro
Insérer une image
+ Retourner à la liste
Editer les relations
Sauver
Sauver et publier
Sauver et envoyer pour approbation
+ Sauver la mise en page de la liste
Prévisualiser
La prévisualisation est désactivée car aucun modèle n'a été assigné.
Choisir un style
Afficher les styles
Insérer un tableau
+ Générer les modèles
+ Sauver et générer les modèles
- Pour changer le type de document du contenu séléctionné, choisissez un type valide pour cet emplacement, qui soit conforme à la structure des types de documents.
- Puis modifiez si nécessaire le mappage des propriétés du type actuel vers le nouveau, et cliquez sur Sauver
+ Pour changer le type de document du contenu séléctionné, faites d'abord un choix dans la liste des types valides à cet endroit.
+ Puis modifiez si nécessaire la correspondance des propriétés du type actuel vers le nouveau, et cliquez sur Sauver.
Le contenu a été republié.
Propriété actuelle
Type actuel
- Le type de document ne peut être changé, il n'y a pas d'alternatives valides pour cet emplacement.
+ Le type de document ne peut être changé car il n'y a pas d'alternative valide à cet endroit. Une alternative sera valide si elle est autorisée sous le parent du contenu sélectionné et si tous les éléments de contenu enfants existants peuvent être créés avec celle-ci.
Type de document modifié
- Mapper les propriétés
- Mapper à la propriété
+ Faire correspondre les propriétés
+ Faire correspondre à la propriété
Nouveau modèle
Nouveau type
aucun
Contenu
Choisir le nouveau Type de Document
- Le type de document du contenu séléctionné a a bien été changé en [new type] et les propriétés suivantes mappées :
+ Le type de document du contenu séléctionné a bien été changé en [new type] et les correspondances de propriétés suivantes effectuées :
en
- Impossible de terminer le mappage des propriétés car une ou plus des propriétés ont plus de un mappage défini.
- Seuls les types de documents valides pour cet emplacement sont affichés.
+ Impossible de terminer la correspondance des propriétés car une ou plusieurs propriétés ont plus d'une correspondance définie.
+ Seuls les types de documents valides à cet endroit sont affichés.
+ A été publié
A propos de cette page
Alias
(comment décririez-vous l'image oralement)
@@ -127,9 +134,10 @@
Type de Document
Edition
Expire le
- Cet élément a été changé après la publication
+ Cet élément a été modifié après la publication
Cet élément n'est pas publié
Dernière publication
+ Il n'y a aucun élément à afficher
Il n'y a aucun élément à afficher dans cette liste.
Type de Média
Lien vers des média(s)
@@ -137,71 +145,85 @@
Rôle
Type de membre
Aucune date choisie
- Titre de page
+ Titre de la page
Propriétés
- Ce document est publié mais n'est pas visible car son parent '%0%' est dépublié
+ Ce document est publié mais n'est pas visible car son parent '%0%' n'est pas publié
Oups : ce document est publié mais n'est pas présent dans le cache (erreur interne Umbraco)
+ Oups: ce document est publié mais son url entrerait en collision avec le contenu %0%
Publier
Statut de publication
Publié le
Dépublié le
Supprimer la date
Ordre de tri mis à jour
- Pour trier les noeuds, faites les glisser à l'aide de la souris ou cliquez sur les entêtes de colonne. Vous pouvez séléctionner plusieurs noeuds en gardant la touche "shift" ou "ctrl" enfoncée pendant votre séléction.
+ Pour trier les noeuds, faites-les simplement glisser à l'aide de la souris ou cliquez sur les entêtes de colonne. Vous pouvez séléctionner plusieurs noeuds en gardant la touche "shift" ou "ctrl" enfoncée pendant votre séléction.
Statistiques
Titre (optionnel)
+ Texte alternatif (optionnel)
Type
- Dépublié
+ Dépublier
Dernière édition
Date/heure à laquelle ce document a été édité
- Supprimer fichier(s)
+ Supprimer le(s) fichier(s)
Lien vers un document
Membre du/des groupe(s)
- Pas un membre du/des groupe(s)
- Elements enfants
+ Pas membre du/des groupe(s)
+ Eléments enfants
Cible
Cliquez pour télécharger
Faites glisser vos fichier ici...
+ Lien vers le média
+ ou cliquez ici pour choisir un fichier
+ Les seuls types de fichiers autorisés sont
+ Impossible de télécharger ce fichier, il n'a pas un type de fichier autorisé.
+ La taille maximum de fichier est
+
+
+ Créer un nouveau membre
+ Tous les membres
Où voulez-vous créer le nouveau %0%
Créer un élément sous
Choisissez un type et un titre
- "Types de documents".]]>
- "Types de médias".]]>
+ "Types de documents".]]>
+ "Types de médias".]]>
+ Type de document sans modèle
+ Nouveau répertoire
+ Nouveau type de données
Parcourir votre site
- Cacher
- Si Umbraco ne s'ouvre pas, vous devriez peut-être autoriser l'ouverture des popups pour ce site.
+ Si Umbraco ne s'ouvre pas, peut-être devez-vous autoriser l'ouverture des popups pour ce site.
s'est ouvert dans une nouvelle fenêtre
Redémarrer
Visiter
Bienvenue
- Stay
- Discard changes
- You have unsaved changes
- Are you sure you want to navigate away from this page? - you have unsaved changes
+ Rester
+ Invalider les changements
+ Vous avez des changements en cours
+ Etes-vous certain(e) de vouloir quitter cette page? - vous avez des changements en cours
- Name
+ Nom
Gérer les noms d'hôtes
Fermer cette fenêtre
- Êtes-vous sûr de vouloir supprimer
- Êtes-vous sûr de vouloir désactiver
+ Êtes-vous certain(e) de vouloir supprimer
+ Êtes-vous certain(e) de vouloir désactiver
Cochez cette case pour confirmer la suppression de %0% élément(s)
- Êtes-vous sûr ?
- Êtes-vous sûr ?
+ Êtes-vous certain(e)?
+ Êtes-vous certain(e)?
Couper
Editer une entrée du Dictionnaire
- Editer le langage
- Insérer une ancre
+ Modifier la langue
+ Insérer un lien local (ancre)
Insérer un caractère
- Insérer une entête graphique
+ Insérer un entête graphique
Insérer une image
Insérer un lien
Insérer une macro
@@ -209,75 +231,124 @@
Dernière modification
Lien
Lien interne :
- Si vous utilisez des ancres, insérer # avant le lien
- Ouvrir dans une nouvelle fenêtre ?
+ Si vous utilisez des ancres, insérez # au début du lien
+ Ouvrir dans une nouvelle fenêtre?
Paramètres de macro
Cette macro ne contient aucune propriété éditable
Coller
Editer les permissions pour
Les éléments dans la corbeille sont en cours de suppression. Ne fermez pas cette fenêtre avant que cette opération soit terminée.
La corbeille est maintenant vide
- Les éléments supprimés de la corbeille sont supprimés définitivement
- regexlib.com a actuellement des problèmes sur lesquels nous n'avons aucun contrôle. Excusez-nous pour le désagrément.]]>
- Rechercher une expression régulière Search for a regular expression to add validation to a form field. Example: 'email, 'zip-code' 'url'
+ Les éléments supprimés de la corbeille seront supprimés définitivement
+ regexlib.com rencontre actuellement des problèmes sur lesquels nous n'avons aucun contrôle. Nous sommes sincèrement désolés pour le désagrément.]]>
+ Rechercher une expression régulière à ajouter pour la validation d'un champ de formulaire. Exemple: 'email, 'zip-code', 'url'
Supprimer la macro
- Champ requis
+ Champ obligatoire
Le site a été réindéxé
- Le cache du site a été mis à jour. Tous les contenus publiés sont à jour. Et tous les contenus dépubliés sont rendus invisibles.
- Le cache du site va être mis à jour. Tous les contenus publiés seront à jour. Et tous les contenus dépubliés seront rendus invisibles.
+ Le cache du site a été mis à jour. Tous les contenus publiés sont maintenant à jour. Et tous les contenus dépubliés sont restés invisibles.
+ Le cache du site va être mis à jour. Tous les contenus publiés seront mis à jour. Et tous les contenus dépubliés resteront invisibles.
Nombre de colonnes
Nombre de lignes
- Définir un placeholder id en mettant un ID sur votre placeholder vous pouvez injecter du contenu à cet endroit depuis vos modèles enfants,
- en faisant référence à cet id au sein d'un élément <asp:content />.]]>
- Séléctionnez un placeholder id depuis la liste ci-dessous. Vous pouvez seulement
- choisir un ID depuis le modèle parent.]]>
- Cliquez pour voir l'image en taille maximale
- Séléctionner un élément
+ Définir un placeholder ID. En mettant un ID sur votre placeholder, vous pouvez injecter du contenu à cet endroit depuis les modèles enfants,
+ en faisant référence à cet ID au sein d'un élément <asp:content />.]]>
+ Séléctionnez un placeholder id dans la liste ci-dessous. Vous pouvez seulement
+ choisir un ID se trouvant dans le parent du modèle actuel.]]>
+ Cliquez sur l'image pour la voir en taille réelle
+ Sélectionner un élément
Voir l'élément de cache
+ Créer un répertoire...
+
+ Lier à l'original
+ La communauté la plus amicale
+
+ Lier à la page
+
+ Ouvre le document lié dans une nouvelle fenêtre ou un nouvel onglet
+ Ouvre le document lié dans l'entièreté de la fenêtre
+ Ouvre le document lié dans le conteneur parent
+
+ Lier à un media
+
+ Sélectionner le media
+ Sélectionner l'icône
+ Sélectionner l'élément
+ Sélectionner le lien
+ Sélectionner la macro
+ Sélectionner le contenu
+ Sélectionner le membre
+ Sélectionner le groupe de membres
+
+ Il n'y a pas de paramètres pour cette macro
+
+ Fournisseurs externes d'identification
+ Détails de l'exception
+ Trace d'exécution
+ Exception interne
+
+ Liez votre
+ Enlevez votre
+
+ compte
+
+ Sélectionner un éditeur
%0%' ci-dessous Vous pouvez ajouter d'autres langages depuis le menu ci-dessous "Langages".
+ Editez les différentes versions de langues pour l'élément de dictionaire '%0% ' ci-dessous. Vous pouvez ajouter d'autres langues depuis le menu ci-dessous "Langues".
]]>
Nom de Culture
Votre nom d'utilisateur
Votre mot de passe
+ Confirmation de votre mot de passe
Nommer %0%...
Entrez un nom...
+ Libellé...
+ Entrez une description...
Rechercher...
Filtrer...
+ Ajouter des tags (appuyer sur enter entre chaque tag)...
+ Entrez votre email
+
+ Autoriser à la racine
+ Seuls les Types de Contenu qui ont ceci coché peuvent être créés au niveau racine des arborescences de contenu et de media
Types de noeuds enfants autorisés
+ Composition de Type de Documents
Créer
- Supprimer onglet
+ Supprimer l'onglet
Description
Nouvel onglet
Onglet
Miniature
Activer la vue liste
+ Configure l'élément de contenu de sorte à afficher une liste de ses enfants que l'on peut trier et filtrer, les enfants ne seront pas affichés dans l'arborescence
+ Liste courante
+ Le type de donnée de la liste courante
+ Créer une liste personnalisée
+ Supprimer la liste personnalisée
- Ajouter une prévaleur
- Type de données en base de donées
+ Ajouter une valeur de base
+ Type de donnée en base de donées
GUID du Property Editor
Property editor
Boutons
Activer les paramètres avancés pour
Activer le menu contextuel
Taille maximale par défaut des images insérées
- CSS relatives
+ CSS associées
Afficher le libellé
Largeur et hauteur
- Vos données ont été sauvegardées, mais avant de publier votre page il y a des erreurs que vous devez corriger :
- Le Membership Provider n'autorise pas le changement des mots de passe (EnablePasswordRetrieval doit être définit à true)
+ Vos données ont été sauvegardées, mais avant de pouvoir publier votre page, il y a des erreurs que vous devez corriger :
+ Le Membership Provider n'autorise pas le changement des mots de passe (EnablePasswordRetrieval doit être défini à true)
%0% existe déjà
- Il y a des erreurs :
- Il y a des erreurs :
+ Des erreurs sont survenues :
+ Des erreurs sont survenues :
Le mot de passe doit contenir un minimum de %0% caractères et contenir au moins %1% caractère(s) non-alphanumerique
%0% doit être un entier
Le champ %0% dans l'onglet %1% est obligatoire
@@ -286,23 +357,30 @@
%0% n'est pas correctement formaté
+ Le serveur a retourné une erreur
Le type de fichier spécifié n'est pas autorisé par l'administrateur
- NOTE ! Même si CodeMirror est activé dans la configuration, il est désactivé sur Internet Explorer car il n'est pas stable sur ce navigateur.
- Remplissez l'alias et le nom de la nouvelle propriété
- Il y a un problème de droits de lecture/écriture sur un fichier ou dossier spécifique
- Entrez un titre
- Choisissez un type
- Vous allez définir une taille d'image supérieure à sa taille d'origine. Êtes-vous sûr ?
+ NOTE ! Même si CodeMirror est activé dans la configuration, il est désactivé dans Internet Explorer car il n'est pas suffisamment stable dans ce navigateur.
+ Veuillez remplir l'alias et le nom de la nouvelle propriété!
+ Il y a un problème de droits en lecture/écriture sur un fichier ou dossier spécifique
+ Erreur de chargement du script d'une Partial View (fichier : %0%)
+ Erreur de chargement du userControl '%0%'
+ Erreur de chargement d'un customControl (Assembly: %0%, Type: '%1%')
+ Erreur de chargement d'un script du MacroEngine (fichier : %0%)
+ "Erreur de parsing d'un fichier XSLT : %0%
+ "Erreur de lecture d'un fichier XSLT : %0%
+ Veuillez entrer un titre
+ Veuillez choisir un type
+ Vous allez définir une taille d'image supérieure à sa taille d'origine. Êtes-vous certain(e) de vouloir continuer?
Erreur dans le script Python
- Le script Python n'a pas été sauvegardé, car il contient des erreur(s)
+ Le script Python n'a pas été sauvegardé car il contient des erreurs
Noeud de départ supprimé, contactez votre administrateur
- Séléctionnez du contenu avant de changer le style
+ Veuillez sélectionner du contenu avant de changer le style
Aucun style actif disponible
- Placez le curseur à gauche des deux cellules que vous voulez fusionner
- Vous ne pouvez pas scinder une cellule que vous n'avez pas fusionné.
+ Veuillez placer le curseur à gauche des deux cellules que vous voulez fusionner
+ Vous ne pouvez pas scinder une cellule qui n'a pas été fusionnée.
Erreur dans le code source XSLT
- Le XSLT n'a pas été sauvegardé, car il contient des erreur(s)
- Il y a une erreur de configuration du type de données de cette propriété. Vérifiez ce type de données.
+ Le XSLT n'a pas été sauvegardé car il contient des erreurs
+ Il y a une erreur de configuration du type de données utilisé pour cette propriété, veuillez vérifier le type de données.
A propos
@@ -310,8 +388,10 @@
Actions
Ajouter
Alias
- Êtes-vous sûr ?
- Bordure
+ Tout
+ Êtes-vous certain(e)?
+ Retour
+ Bord
par
Annuler
Marge de cellule
@@ -320,13 +400,13 @@
Fermer la fenêtre
Commenter
Confirmer
- Contraindre les proportions
+ Conserver les proportions
Continuer
Copier
Créer
Base de données
Date
- Defaut
+ Défaut
Supprimer
Supprimé
Suppression...
@@ -336,26 +416,28 @@
Télécharger
Editer
Edité
- Elements
+ Eléments
Email
Erreur
Trouver
Hauteur
Aide
- Icone
+ Icône
Importer
Marge intérieure
Insérer
Installer
+ Non valide
Justifier
- Langage
- Layout
- Chargement
- Fermé
+ Langue
+ Mise en page
+ En cours de chargement
+ Bloqué
Connexion
Déconnexion
Déconnexion
Macro
+ Obligatoire
Déplacer
Plus
Nom
@@ -374,6 +456,7 @@
Propriétés
Email de réception des données de formulaire
Corbeille
+ Votre corbeille est vide
Restant
Renommer
Renouveller
@@ -381,12 +464,13 @@
Réessayer
Permissions
Rechercher
+ Désolé, nous ne pouvons pas trouver ce que vous recherchez
Serveur
Montrer
Afficher la page à l'envoi
Taille
Trier
- Submit
+ Envoyer
Type
Rechercher...
Haut
@@ -403,11 +487,45 @@
Oui
Dossier
Résultats de recherche
- Reorder
- I am done reordering
+ Réorganiser
+ J'ai fini de réorganiser
+ Prévisualiser
+ Modifier le mot de passe
+ vers
+ Liste
+ Sauvegarde...
+ actuel
+ Intégrer
+ sélectionné
+
+
+ Noir
+ Vert
+ Jaune
+ Orange
+ Bleu
+ Rouge
+
+
+ Ajouter un onglet
+ Ajouter une propriété
+ Ajouter un éditeur
+ Ajouter un modèle
+ Ajouter un noeud enfant
+ Ajouter un enfant
+
+ Editer le type de données
+
+ Parcourir les sections
+
+ Raccourcis
+ afficher les raccourcis
+
+ Passer à la vue en liste
+ Basculer vers l'autorisation comme racine
- Background color
+ Couleur de fond
Gras
Couleur de texte
Police
@@ -417,131 +535,127 @@
Page
- L'installeur n'a pas pu se connecter à la base de données.
- Impossible de modifier le fichier web.config file. Modifiez s'il vous plait la "connection string" manuellement.
- Votre base de données a été détectée et identifiée telle que
- Configurtion de la base de données
+ Le programme d'installation ne parvient pas à se connecter à la base de données.
+ Impossible de sauvegarder le fichier web.config. Veuillez modifier la "connection string" manuellement.
+ Votre base de données a été détectée et est identifiée comme étant
+ Configuration de la base de données
install pour installer la base de données Umbraco %0%
+ Appuyez sur le bouton installer pour installer la base de données Umbraco %0%
]]>
- Suivant pour procéder.]]>
- Base de données non trouvée ! Vérifiez les informations de la "connection string" dans le fichier web.config.
- Pour poursuivre, éditez le fichier "web.config" (avec Visual Studio ou votre éditeur de texte favori), scrollez jusqu'en bas, ajoutez une "connection string" dans la ligne "umbracoDbDSN" et sauvegardez votre fichier.
+ Suivant pour poursuivre.]]>
+ Base de données non trouvée ! Veuillez vérifier les informations de la "connection string" dans le fichier web.config.
+ Pour poursuivre, veuillez éditer le fichier "web.config" (avec Visual Studio ou votre éditeur de texte favori), scroller jusqu'en bas, ajouter le "connection string" pour votre base de données dans la ligne avec la clé "umbracoDbDSN" et sauvegarder le fichier.
Cliquez sur le bouton Réessayer lorsque cela est fait.
Plus d'informations sur l'édition du fichier web.config ici.
]]>
-
- Contactez votre administrateur système si nécessaire.
- Si vous installez Umbraco sur votre ordinateur, vous aurez probablement besoin de consulter votre administrateur système.]]>
+
+ Veuillez contacter votre fournisseur de services internet si nécessaire.
+ Si vous installez Umbraco sur un ordinateur ou un serveur local, vous aurez peut-être besoin de consulter votre administrateur système.]]>
Appuyez sur le bouton Upgrader pour mettre à jour votre base de données vers Umbraco %0%
- Ne vous inquiétez pas : aucun contenu ne sera supprimé et tout continuera à fonctionner parfaitement ensuite !
+ N'ayez pas d'inquiétude : aucun contenu ne sera supprimé et tout continuera à fonctionner parfaitement par après !
]]>
- Appuyez sur Suivant pour
- poursuivre. ]]>
+
+ Appuyez sur Suivant pour
+ poursuivre. ]]>
+
Suivant pour poursuivre la configuration]]>
- Le mot de passe par défaut doit être changé !]]>
- L'utilisateur par défaut a été désactivé ou n'a pas accès à Umbraco!Aucune action n'est requise. Cliquez sur Suivant pour poursuivre.]]>
- Le mot de passe par défaut a été modifié avec succès !Aucune action n'est requise. Cliquez sur Suivant pour poursuivre.]]>
- Le mot de passe a été changé !
-
- Umbraco créer un utilisateur par défaut avec le login ('admin') et le mot de passe ('default') .
- Il est important que ce mot de passe soit changé pour quelque-chose de sécurisé et unique.
-
-
- Cette étape va vérifier le mot de passe par défaut et vérifier s'il est nécessaire de le changer.
-
- ]]>
+ Le mot de passe par défaut doit être modifié !]]>
+ L'utilisateur par défaut a été désactivé ou n'a pas accès à Umbraco!Aucune autre action n'est requise. Cliquez sur Suivant pour poursuivre.]]>
+ Le mot de passe par défaut a été modifié avec succès depuis l'installation!Aucune autre action n'est requise. Cliquez sur Suivant pour poursuivre.]]>
+ Le mot de passe a été modifié !
+
+ ('admin') et le mot de passe ('default') . Il est important que ce mot de passe soit modifié en quelque-chose de sécurisé et unique.]]>
Pour bien commencer, regardez nos vidéos d'introduction
- En cliquant sur le bouton Suvant (ou en modifiant umbracoConfigurationStatus dans le fichier web.config), vous acceptez la licence de ce logiciel telle que spécifiée dans le champ ci-dessous. Remarque : cette distribution Umbraco consiste en deux licences différentes, la licence open source MIT pour le framework et la licence Umbraco freewarequi couvre l'UI.
+ En cliquant sur le bouton "Suivant" (ou en modifiant umbracoConfigurationStatus dans le fichier web.config), vous acceptez la licence de ce logiciel telle que spécifiée dans le champ ci-dessous. Veuillez noter que cette distribution Umbraco consiste en deux licences différentes, la licence open source MIT pour le framework et la licence Umbraco freeware qui couvre l'UI.
Pas encore installé.
- Fichiers et dossiers affectés
- Plus d'informations sur la définition des permissions
+ Fichiers et dossiers concernés
+ Plus d'informations sur la configuration des permissions
Vous devez donner à ASP.NET les droits de modification sur les fichiers/dossiers suivants
- Vos permissions sont presques parfaites !
- Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement parti d'Umbraco.]]>
- Comment le résoudre
+ Vos configurations de permissions sont presque parfaites !
+ Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement profit d'Umbraco.]]>
+ Comment résoudre
Cliquez ici pour lire la version texte
- tutoriel vidéo sur la définition des permissions pour Umbraco, ou lisez la version texte.]]>
- Vos permissions pourraient être problématiques !
+ tutoriel vidéo sur la définition des permissions des répertoires pour Umbraco, ou lisez la version texte.]]>
+ Vos configurations de permissions pourraient poser problème !
- Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement parti d'Umbraco.]]>
- Vos permissions ne sont pas prêtes pour Umbraco !
+ Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement profit d'Umbraco.]]>
+ Vos configurations de permissions ne sont pas prêtes pour Umbraco !
Pour faire fonctionner Umbraco, vous aurez besoin de mettre à jour les permissions sur les fichiers/dossiers.]]>
- Vos permissions sont parfaites !
- Vous êtes prêt à faire fonctionner Umbraco et installer des packages !]]>
+ Vos configurations de permissions sont parfaites !
+ Vous êtes prêt(e) à faire fonctionner Umbraco et à installer des packages !]]>
Résoudre un problème sur un dossier
- Suivz ce lien pour plus d'informations sur ASP.NET et la création de dossiers
+ Suivez ce lien pour plus d'informations sur les problèmes avec ASP.NET et la création de dossiers
Définir les permissions de dossier
Je veux démarrer "from scratch"
-
+ Apprenez comment)
- Vous pouvez toujours choisir d'installer Runway plus tard. Pour cela allez dans la séction "Développeur" et séléctionnez "Packages".
+ Vous pouvez toujours choisir d'installer Runway plus tard. Pour cela, allez dans la section "Développeur" et sélectionnez "Packages".
]]>
- Vous avez mis en oeuvre une plateforme Umbraco clean. Que voulez-vous faire ensuite ?
+ Vous venez de mettre en place une plateforme Umbraco toute nette. Que voulez-vous faire ensuite ?
Runway est installé
+ Les fondations en place. Choisissez les modules que vous souhaitez installer par-dessus
Voici la liste des modules recommandés, cochez ceux que vous souhaitez installer, ou regardez la liste complète des modules
]]>
Recommandé uniquement pour les utilisateurs expérimentés
Je veux commencer avec un site simple
-
+
- "Runway" est un simple site fournissant des types de documents et modèles basiques. L'installeur peut mettre en oeuvre Runway automatiquement pour vous,
- mais vous pouvez facilement léditer, lenrichir, ou le supprimer ensuite. Il n'est pas nécessaire et vous pouvez parfaitement utiliser Umbraco sans. Pour autant,
- Runway offre un socle facile basé sur des bonnes pratiques pour vous permettre de commencer rapidement.
- Si vous choisissez d'installer Runway, vous pouvez, de manière optionnelle, choisir des blocks appelés Runway Modules pour enrichir les pages du site.
+ "Runway" est un site simple qui fournit des types de documents et des modèles de base. L'installateur peut mettre en place Runway automatiquement pour vous,
+ mais vous pouvez facilement l'éditer, l'enrichir, ou le supprimer par la suite. Il n'est pas nécessaire, et vous pouvez parfaitement vous en passer pour utiliser Umbraco. Cela étant dit,
+ Runway offre une base facile, fondée sur des bonnes pratiques, pour vous permettre de commencer plus rapidement que jamais.
+ Si vous choisissez d'installer Runway, vous pouvez sélectionner en option des blocs de base, appelés Runway Modules, pour enrichir les pages de votre site.
Inclus avec Runway : Home page, Getting Started page, Installing Modules page.
Modules optionnels : Top Navigation, Sitemap, Contact, Gallery.
]]>
- Qu'est ce que Runway
- Step 1/5 : Licence
- Step 2/5 : Configuration base de données
- Step 3/5 : Validation des permissions de fichiers
- Step 4/5 : Sécurité Umbraco
- Step 5/5 : Umbraco est prêt
+ Qu'est-ce que Runway
+ Etape 1/5 : Accepter la licence
+ Etape 2/5 : Configuration de la base de données
+ Etape 3/5 : Validation des permissions de fichiers
+ Etape 4/5 : Sécurité Umbraco
+ Etape 5/5 : Umbraco est prêt
Merci d'avoir choisi Umbraco
Parcourir votre nouveau site
-Vous avez installé Runway, alors pourquoi pas jeter un oeil au look de votre nouveau site ?]]>
- Aide et information
-Obtenez de l'aide de notre award winning communauté, parcourez la documentation our regardez quelques vidéos sur "Comemnt construire un site simple", "Comment utiliser les packages" et un guide rapide sur la terminologie Umbraco]]>
+Vous avez installé Runway, alors pourquoi ne pas jeter un oeil au look de votre nouveau site ?]]>
+ Aide et informations complémentaires
+Obtenez de l'aide de notre "award winning" communauté, parcourez la documentation ou regardez quelques vidéos gratuites sur la manière de construire un site simple, d'utiliser les packages ainsi qu'un guide rapide sur la terminologie Umbraco]]>
Umbraco %0% est installé et prêt à être utilisé
- /web.config file et mettre à jour le paramètre AppSetting dans umbracoConfigurationStatus en bas de la valeur de '%0%' .]]>
- démarrer maintenant en cliquant sur le bouton "Lancer Umbraco" ci-dessous.
-Si vous débutez sur Umbraco, vous pouvez trouver plein de ressources sur nos pages "Getting Started".]]>
+ fichier /web.config et mettre à jour le paramètre AppSetting umbracoConfigurationStatus situé en bas à la valeur '%0%' .]]>
+ démarrer instantanément en cliquant sur le bouton "Lancer Umbraco" ci-dessous.
+Si vous débutez avec Umbraco , vous pouvez trouver une foule de ressources dans nos pages "Getting Started".]]>
Lancer Umbraco
-Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à ajouter du contenu, mettez à jour les templates et feuilles de styles ou ajoutez ds nouvelles fonctionnalités]]>
+Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à ajouter du contenu, à mettre à jour les modèles d'affichage et feuilles de styles ou à ajouter de nouvelles fonctionnalités]]>
La connexion à la base de données a échoué.
Umbraco Version 3
Umbraco Version 4
Regarder
- umbraco %0%, qu'il s'agisse d'une installation récente ou de la version 3.0
+ Umbraco %0%, qu'il s'agisse d'une nouvelle installation ou d'une mise à jour à partir de la version 3.0
- Appuyez sur Press "suivant" pour commencer.]]>
+ Appuyez sur "suivant" pour commencer l'assistant.]]>
Code de la Culture
Nom de la culture
- Vous avez été inactif, la déconnexion automatique se fera dans
+ Vous avez été inactif et la déconnexion aura lieu automatiquement dans
Renouvellez votre session maintenant pour sauvegarder votre travail
@@ -554,7 +668,18 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Joyeux samedi
Connectez-vous ci-dessous
La session a expiré
- © 2001 - %0% umbraco.com ]]>
+ © 2001 - %0% Umbraco.com ]]>
+ Mot de passe oublié?
+ Un email contenant un lien pour ré-initialiser votre mot de passe sera envoyé à l'adresse spécifiée
+ Un email contenant les instructions de ré-initialisation de votre mot de passe sera envoyée à l'adresse spécifiée si elle correspond à nos informations.
+ Revenir au formulaire de connexion
+ Veuillez fournir un nouveau mot de passe
+ Votre mot de passe a été mis à jour
+ Le lien sur lequel vous avez cliqué est non valide ou a expiré.
+ Umbraco: Ré-initialiser le mot de passe
+
+ Votre nom d'utilisateur pour vous connecter au back-office Umbraco est : %0% .Cliquez ici pour ré-initialiser votre mot de passe, ou recopiez cet URL dans votre navigateur :
%1%
]]>
+
Tableau de bord
@@ -562,21 +687,21 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Contenu
- Choisissez une page ci-dessous...
+ Choisissez la page au-dessus...
%0% a été copié dans %1%
- Choisissez, ci-dessous, où le document %0% doit être copié
+ Choisissez ci-dessous l'endroit où le document %0% doit être copié
%0% a été déplacé dans %1%
- Choisissez, ci-dessous, où le document %0% doit être déplacé
- a été choisi comme racine de votre contenu, cliquez sur 'ok' ci-dessous.
- Aucun noeud choisi, choisissez s'il vous plait un noeud dans la liste ci-dessus avant de cliquer sur 'ok'.
- Le noeud actuel n'est pas autorisé dans le noeud choisi à cause de son type
+ Choisissez ci-dessous l'endroit où le document %0% doit être déplacé
+ a été choisi comme racine de votre nouveau contenu, cliquez sur 'ok' ci-dessous.
+ Aucun noeud n'a encore été choisi, veuillez choisir un noeud dans la liste ci-dessus avant de cliquer sur 'ok'.
+ Le noeud actuel n'est pas autorisé sous le noeud choisi à cause de son type
Le noeud actuel ne peut pas être déplacé dans une de ses propres sous-pages
Le noeud actuel ne peut pas exister à la racine
- L'action n'est pas autorisée car vous n'avez pas les droits sur un ou plus des noeuds enfants.
- Relier les items copiés à l'original
+ L'action n'est pas autorisée car vous n'avez pas les droits suffisants sur un ou plusieurs noeuds enfants.
+ Relier les éléments copiés à l'original
- Editer vos notifications pour %0%
+ Editez vos notifications pour %0%
- Hi %0%
+
+ Hello %0%
Ceci est un email automatique pour vous informer que la tâche '%1%'
a été executée sur la page '%2%'
@@ -598,11 +724,11 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
-
Update summary:
+ Résumé de la mise à jour :
@@ -610,71 +736,71 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Bonne journée !
- Le Robot Umbraco vous salue
+ Avec les salutations du Robot Umbraco
]]>
La notification [%0%] à propos de %1% a été executée sur %2%
Notifications
- localisez le package. Les packages Umbraco ont généralement une extension .umb ou .zip.
+ Choisissez un package sur votre ordinateur en cliquant sur le bouton Parcourir
+ et localisez le package. Les packages Umbraco ont généralement une extension ".umb" ou ".zip".
]]>
Auteur
- Demon
+ Démo
Documentation
Meta data du package
Nom du package
- Le package ne contient aucun éléments
-
- Vous pouvez supprimer tranquillement le package de votre installation en cliquant sur "Désinstaller" ci-dessous.]]>
- Aucune mises à jour disponibles
- Options de package
+ Le package ne contient aucun élément
+
+ Vous pouvez supprimer tranquillement ce package de votre installation en cliquant sur "Désinstaller le package" ci-dessous.]]>
+ Aucune mise à jour disponible
+ Options du package
Package readme
- Package repository
+ Repository des packages
Confirmation de désinstallation
- Package was uninstalled
+ Le package a été désinstallé
Le package a été désinstallé avec succès
- Désintaller le package
-
- Remarque : tous les documents, media etc dépendants des éléments que vous supprimerez, arrêteront de fonctionner, ce qui peut provoquer une instabilité du système,
- désinstallez avec prudence. En cas de doute, contactez l'auteur du package.]]>
+ Désinstaller le package
+
+ Remarque : tous les documents, media etc. dépendant des éléments que vous supprimez vont cesser de fonctionner, ce qui peut provoquer une instabilité du système,
+ désinstallez donc avec prudence. En cas de doute, contactez l'auteur du package.]]>
Télécharger la mise à jour depuis le repository
Mettre à jour le package
Instructions de mise à jour
- Il y a une mise à jour disponible pour ce package. Vous pouvez la télécharger directement depuis le repository.
- Version de package
- Historique des version de package
+ Il y a une mise à jour disponible pour ce package. Vous pouvez la télécharger directement depuis le repository des packages Umbraco.
+ Version du package
+ Historique des versions du package
Voir le site internet du package
Coller en conservant le formatage (non recommandé)
- Le texte que vous tentez de coller contient des caractères spéciaux ou de formatage. Cela peut être dû à une copie d'un texte de Microsoft Word. Umbraco peut supprimer les caractères spéciaux et le formatage automatiquement, de manière à ce que le texte collé soit plus utilisable pour le Web.
- Coller en tant que texte brut sans formatage
- Coller, mais supprimer le formatage (recommendé)
+ Le texte que vous tentez de coller contient des caractères spéciaux ou du formatage. Cela peut être dû à une copie d'un texte depuis Microsoft Word. Umbraco peut supprimer automatiquement les caractères spéciaux et le formatage, de manière à ce que le texte collé convienne mieux pour le Web.
+ Coller en tant que texte brut sans aucun formatage
+ Coller, mais supprimer le formatage (recommandé)
Protection basée sur les rôles
via les groupes de membres Umbraco.]]>
- l'authentification basée sur les rôles.]]>
+ l'authentification basée sur les rôles.]]>
Page d'erreur
- Utilisé quand les gens sont connectés, mais n'ont pas accès
+ Utilisé pour les personnes connectées, mais qui n'ont pas accès
Choisissez comment restreindre l'accès à cette page
%0% est maintenant protégée
Protection supprimée de %0%
Page de connexion
- Choisissez la page qui a le formulaire de login
+ Choisissez la page qui contient le formulaire de connexion
Supprimer la protection
- Choisissez la page qui contient le formulaire de login et les messages d'erreur
- Piochez les roles qui ont accès à cette page
- Définissez l'identifiant et mot de passe pour cette page
+ Choisissez les pages qui contiennent le formulaire de connexion et les messages d'erreur
+ Choisissez les rôles qui ont accès à cette page
+ Définissez l'identifiant et le mot de passe pour cette page
Protection utilisateur unique
- Si vous souhaitez simplement mettre en place une protection par identifiant et mot de passe
+ Si vous souhaitez mettre en place une protection simple utilisant un identifiant et un mot de passe uniques
@@ -682,44 +808,47 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
%0% n'a pas pu être publié car cet élément est programmé pour être publié bientôt.
]]>
+
- Inclure les pages enfants non publiées
+ Inclure les pages enfant non publiées
Publication en cours - veuillez patienter...
- %0% sur %1% des pages ont été publiées...
+ %0% pages sur %1% ont été publiées...
%0% a été publié
%0% et ses pages enfants ont été publiées
- Publier %0% et ses pages enfants
- ok pour publier %0% et le rendre ainsi accessible publiquement.
- Vous pouvez publier cette page et ses sous pages en cochant publier tous les enfants ci-dessous.
+ Publier %0% et toutes ses pages enfant
+ Publier pour publier %0% et la rendre ainsi accessible publiquement.
+ Vous pouvez publier cette page et toutes ses sous-pages en cochant Inclure les pages enfant non pubiées ci-dessous.
]]>
- Vous n'avez configuré aucune couleurs approuvées
+ Vous n'avez configuré aucune couleur approuvée
- Ajouter un lien externe
- Ajouter un lien interne
- Ajouter
+ introduire un lien externe
+ choisir une page interne
Légende
- Page interne
- URL
- Descendre
- Monter
+ Lien
Ouvrir dans une nouvelle fenêtre
- Supprimer le lien
+ introduisez la légende à afficher
+ Introduiser le lien
+
+
+ Réinitialiser
Version actuelle
- Le texte en Rouge signifit qu'il a été supprimé de la version choisie, vert signifie ajouté ]]>
- Le document est passé à une version antérieure
+ Le texte en Rouge signifie qu'il a été supprimé de la version choisie, vert signifie ajouté ]]>
+ Le document a été restauré à une version antérieure
Ceci affiche la version choisie en tant que HTML, si vous souhaitez voir les différences entre les deux versions en même temps, utilisez la vue différentielle
Revenir à
Choisissez une version
@@ -737,16 +866,24 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Medias
Membres
Newsletters
- Paramètres
+ Configuration
Statistiques
Traduction
Utilisateurs
Aide
+ Formulaires
+ Analytics
+
+
+ aller à
+ Rubriques d'aided pour
+ Chapitres vidéo pour
+ Les meilleurs tutoriels vidéo Umbraco
- Template par défaut
+ Modèle par défaut
Clé de dictionnaire
- Pour importer un type de document, trouvez le fichier ".udt" sur votre ordinateur en cliquant sur le bouton "Parcourir" et cliquez sur "Importer" (une confirmation vous sera demandé à l'écran d'après)
+ Pour importer un type de document, trouvez le fichier ".udt" sur votre ordinateur en cliquant sur le bouton "Parcourir" et cliquez sur "Importer" (une confirmation vous sera demandée à l'écran suivant)
Titre du nouvel onglet
Type de noeud
Type
@@ -756,22 +893,29 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Onglet
Titre de l'onglet
Onglets
- Type de contenu master activé
+ Type de contenu de base activé
Ce type de contenu utilise
- en tant que type de contenu master, les onglets du type de contenu master ne sont pas affichés et peuvent seulement être modifiés dans le type de contenu master lui-même.
- Aucune propriétés définies dans cet onglet. Cliquez sur le lien "Ajouter une nouvelle propriété" en haut pour créer une nouvelle propriété.
- Type de contenu parent
- Créer le template correspondant
-
+ en tant que type de contenu de base. Les onglets du type de contenu de base ne sont pas affichés et peuvent seulement être modifiés à partir du type de contenu de base lui-même.
+ Aucune propriété définie dans cet onglet. Cliquez sur le lien "Ajouter une nouvelle propriété" en-haut pour créer une nouvelle propriété.
+ Type de contenu de base
+ Créer le modèle correspondant
+ Ajouter une icône
+
- Sort order
- Creation date
+ Ordre de tri
+ Date de création
Tri achevé.
- Faites glisser les différents éléments ci-dessous vers le haut ou le bas pour définir comment ils doivent être triés. Ou cliquez sur les entêtes de colonne pour trier la collection complète d'éléments
+ Faites glisser les différents éléments vers le haut ou vers le bas pour définir la manière dont ils doivent être organisés. Ou cliquez sur les entêtes de colonnes pour trier la collection complète d'éléments
Ne fermez pas cette fenêtre durant le tri.]]>
- La publication a été annulée par un extension tierce.
+ Validation
+ Les erreurs de validation doivent être corrigées avant de pouvoir sauvegarder l'élément
+ Echec
+ Permissions utilisateur insuffisantes, l'opération n'a pas pu être complétée
+ Annulation
+ L'opération a été annulée par une extension tierce
+ La publication a été annulée par une extension tierce.
Le type de propriété existe déjà
Type de propriété créé
Type de données : %1%]]>
@@ -779,33 +923,35 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Type de documet sauvegardé
Onglet créé
Onglet supprimé
- Onglet d'ID : %0% supprimé
+ Onglet avec l'ID : %0% supprimé
Feuille de style non sauvegardée
Feuille de style sauvegardée
Feuille de style sauvegardée sans erreurs
- Type de données sauvegardée
- Element de dictionnaire sauvegardé
+ Type de données sauvegardé
+ Elément de dictionnaire sauvegardé
La publication a échoué car la page parent n'est pas publiée
Contenu publié
et visible sur le site
- Content sauvegardé
- N'oubliez pas de publier pour rendre les changements visibles
+ Contenu sauvegardé
+ N'oubliez pas de publier pour rendre les modifications visibles
Envoyer pour approbation
- Les changements ont été envoyés pour approbation
+ Les modifications ont été envoyées pour approbation
Media sauvegardé
Media sauvegardé sans erreurs
Membre sauvegardé
- Propriété de feuille de style sauvegardé
- Feuille de style sauvegardé
- Template sauvegardé
- Erreur lors de la sauvegarde de l'utilisateur
+ Propriété de feuille de style sauvegardée
+ Feuille de style sauvegardée
+ Modèle sauvegardé
+ Erreur lors de la sauvegarde de l'utilisateur (consultez les logs)
Utilisateur sauvegardé
Type d'utilisateur sauvegardé
Fichier non sauvegardé
Le fichier n'a pas pu être sauvegardé. Vérifiez les permissions de fichier.
Fichier sauvegardé
Fichier sauvegardé sans erreurs
- Langage sauvegardé
+ Langue sauvegardée
+ Type de média sauvegardé
+ Type de membre sauvegardé
Le script Python n'a pas été sauvegardé
Le script Python n'a pas été sauvegardé à cause d'erreurs
Le script Python a été sauvegardé
@@ -818,30 +964,35 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Le XSLT contenait une erreur
Le XSLT n'a pas pu être sauvegardé, vérifiez les permissions de fichier
Le XSLT a été sauvegardé
- Aucune erreurs dans le XSLT
+ Aucune erreur dans le XSLT
Contenu publié
Vue partielle sauvegardée
Vue partielle sauvegardée sans erreurs !
Vue partielle non sauvegardée
Une erreur est survenue lors de la sauvegarde du fichier.
-
+ Vue script sauvegardée
+ Vue script sauvegardée sans erreur !
+ Vue script non sauvegardée
+ Une erreur est survenue lors de la sauvegarde du fichier.
+ Une erreur est survenue lors de la sauvegarde du fichier.
+
- Utilise la synthaxe CSS. Ex : h1, .redHeader, .blueTex
+ Utilise la syntaxe CSS. Ex : h1, .redHeader, .blueTex
Editer la feuille de style
Editer la propriété de feuille de style
- Nommer pour identifier la propriété dans le Rich Text Editor
+ Donner un nom pour identifier la propriété dans le Rich Text Editor
Prévisualiser
Styles
- Editer le modèle template
+ Editer le modèle
Insérer une zone de contenu
Insérer un placeholder de zone de contenu
Insérer un élément de dictionnaire
- Insert Macro
- Insert umbraco page field
- Modèle master
- Guide rapide aux tags des modèles Umbraco
+ Insérer une Macro
+ Insérer un champ de la page Umbraco
+ Modèle de base
+ Guide rapide concernant les tags des modèles Umbraco
Modèle
@@ -849,40 +1000,113 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Choisissez une mise en page
Ajouter une ligne
Ajouter du contenu
- Contenu goutte
+ Supprimer le contenu
Paramètres appliqués
- Ce contenu est pas autorisée ici
- Ce contenu est permis ici
+ Ce contenu n'est pas autorisé ici
+ Ce contenu est autorisé ici
Cliquez pour intégrer
- Cliquez pour insérer l'image
+ Cliquez pour insérer une image
Légende de l'image...
- Ecrire ici...
+ Ecrivez ici...
- Layouts Grid
- Layouts sont la superficie totale de travail pour l'éditeur de grille, en général, vous avez seulement besoin d'une ou deux configurations différentes
- Ajouter Grid Layout
- Ajustez la mise en page en définissant la largeur des colonnes et ajouter des sections supplémentaires
- Configurations des lignes
- Les lignes sont des cellules prédéfinies disposées horizontalement
- Ajouter une configuration de la ligne
- Ajustez la ligne en réglant la largeur des cellules et en ajoutant des cellules supplémentaires
+ Mises en pages de la Grid
+ Les mises en pages représentent la surface de travail globale pour l'éditeur de grille, en général, vous n'avez seulement besoin que d'une ou deux mises en pages différentes
+ Ajouter une mise en page de grille
+ Ajustez la mise en page en définissant la largeur des colonnes et en ajoutant des sections supplémentaires
+ Configurations des rangées
+ Les rangées sont des cellules prédéfinies disposées horizontalement
+ Ajouter une configuration de rangée
+ Ajustez la rangée en réglant la largeur des cellules et en ajoutant des cellules supplémentaires
Colonnes
Nombre total combiné de colonnes dans la configuration de la grille
Paramètres
- Configurer quels paramètres éditeurs peuvent changer
+ Configurez les paramètres qui peuvent être modifiés par les éditeurs
- Modes
- Configurer ce style éditeurs peuvent changer
+
+ Styles
+ Configurez les effets de style qui peuvent être modifiés par les éditeurs
- Les réglages seulement économiser si la configuration du json saisi est valide
+ Les paramètres ne seront sauvegardés que si la configuration json saisie est valide
Autoriser tous les éditeurs
- Autoriser toutes les configurations de lignes
+ Autoriser toutes les configurations de rangées
+ Configurer comme défaut
+ Choisir en plus
+ Choisir le défaut
+ ont été ajoutés
+
+
+ Compositions
+ Vous n'avez pas ajouté d'onglet
+ Ajouter un nouvel onglet
+ Ajouter un autre onglet
+ Hérité de
+ Ajouter une propriété
+ Label requis
+
+ Activer la vue en liste
+ Configure l'élément de contenu de manière à afficher ses éléments enfants sous forme d'une liste que l'on peut trier et filtrer, les enfants ne seront pas affichés dans l'arborescence
+
+ Modèles autorisés
+ Sélectionnez les modèles que les éditeurs sont autorisés à utiliser pour du contenu de ce type.
+ Autorisé comme racine
+ Autorisez les éditeurs à créer du contenu de ce type à la racine de l'arborescence de contenu.
+ Oui - autoriser du contenu de ce type à la racine
+
+ Types de noeuds enfants autorisés
+ Autorisez la création de contenu des types spécifiés sous le contenu de ce type-ci
+
+ Choisissez les noeuds enfants
+ Hériter des onglets et propriétés d'un type de document existant. De nouveaux onglets seront ajoutés au type de document actuel, ou fusionnés s'il existe un onglet avec un nom sililaire.
+ Ce type de contenu est utilisé dans une composition, et ne peut donc pas être lui-même un composé.
+ Il n'y a pas de type de contenu disponible à utiliser dans une composition.
+
+ Editeurs disponibles
+ Réutiliser
+ Configuration de l'éditeur
+
+ Configuration
+
+ Oui, supprimer
+
+ a été déplacé en-dessous
+ a été copié en-dessous
+ Sélectionnez le répertoire à déplacer
+ Sélectionnez le répertoire à copier
+ dans l'arborescence ci-dessous
+
+ Tous les types de document
+ Tous les documents
+ Tous les éléments media
+
+ utilisant ce type de document seront supprimés définitivement, veuillez confirmer que vous souhaitez les supprimer également.
+ utilisant ce type de media seront supprimés définitivement, veuillez confirmer que vous souhaitez les supprimer également.
+ utilisant ce type de membre seront supprimés définitivement, veuillez confirmer que vous souhaitez les supprimer également
+
+ et tous les documents utilisant ce type
+ et tous les éléments media utilisant ce type
+ et tous les membres utilisant ce type
+
+ utilisant cet éditeur seront mis à jour avec la nouvelle configuration
+
+ Le membre peut éditer
+ Afficher dans le profil du membre
+ l'onglet n'a pas d'ordonnancement
+
+
+
+ Création des modèles
+ ceci peut prendre un certain temps, ne vous inquiétez pas
+ Les modèles ont été générés
+ Les modèles n'ont pas pu être générés
+ La génération des modèles a échoué, veuillez consulter les erreurs dans le log Umbraco
+
+
Champ alternatif
Texte alternatif
@@ -896,67 +1120,68 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Formater comme une date
Encoder en HTML
Remplacera les caractères spéciaux par leur équivalent HTML.
- Sera insérer après la valeur du champ
- Sera inséré après la valeur du champ
+ Sera inséré après la valeur du champ
+ Sera inséré avant la valeur du champ
Minuscules
Aucun
- Inserer après le champ
- Inserer après le champ
- Recursif
- Supprimer les balises paragraphes
- Supprimera tous les <P>
+ Insérer après le champ
+ Insérer avant le champ
+ Récursif
+ Supprimer les balises de paragraphes
+ Supprimera toute balise <P> au début et à la fin du texte
Champs standards
Majuscules
- Encode en URL
- Formatera les caractères spéciaux de manière à ce qu'ils soient utilisés dans une URL
- Sera seulement utilisé quand toutes les valeurs ci-dessous seront vides
- Ce champ sera utilisé seulement si le champ primaire est vide
- Yes, with time. Separator:
+ Encode pour URL
+ Formatera les caractères spéciaux de manière à ce qu'ils soient reconnus dans une URL
+ Sera seulement utilisé si toutes les valeurs des champs ci-dessus sont vides
+ Ce champ sera utilisé seulement si le champ initial est vide
+ Oui, avec l'heure. Séparateur:
Tâches qui vous sont assignées
- vous sont assignées. Pour voir une vue détaillée incluant les commentaires, cliquez sur "Details" ou juste le nom de la page.
- Vous pouvez télécharger la format au format XML en cliquant sur le lien "Télécharger XML".
- Pour terminer une tâche de traduction, allez sur Details, puis cliquer sur le bouton "Terminer tâche".
+ vous sont assignées. Pour voir un aperçu détaillé incluant les commentaires, cliquez sur "Détails" ou juste sur le nom de la page.
+ Vous pouvez aussi télécharger la page au format XML en cliquant sur le lien "Télécharger XML".
+ Pour clôturer une tâche de traduction, allez sur Détails, puis cliquez sur le bouton "Terminer la tâche".
]]>
- Terminer tâche
+ Terminer la tâche
Détails
- Télécharger toutes les traductions au format XML
+ Télécharger toutes les tâches de traductions au format XML
Télécharger XML
Télécharger la DTD XML
Champs
Inclure les pages enfants
[%0%] tâches de traductions pour %1%
- Aucun utilisateurs traducteurs trouvés. Vous devez créer un utilisateur traducteur avant d'envoyer votre contenu pour traduction
- Tâches que vous avez créé
- créées par vous. Pour voir une vue détaillée incluant les commentaires,
- cliquez sur "Détails" ou juste le nom de la page. Vous pouvez aussi télécharger la page au format XML en cliquant sur le lien "Télécharger XML".
- Pour terminer une tâche de traduction, allez sur Details, puis cliquer sur le bouton "Terminer tâche".
+ Aucun utilisateur traducteur trouvé. Veuillez créer un utilisateur traducteur avant d'envoyer du contenu pour traduction
+ Tâches que vous avez créées
+ que vous avez créées. Pour voir un aperçu détaillé incluant les commentaires,
+ cliquez sur "Détails" ou juste sur le nom de la page. Vous pouvez aussi télécharger la page au format XML en cliquant sur le lien "Télécharger XML".
+ Pour clôturer une tâche de traduction, allez sur Détails, puis cliquez sur le bouton "Terminer tâche".
]]>
- La page '%0%' a été envoyé pour traduction
+ La page '%0%' a été envoyée pour traduction
+ Veuillez choisir la langue dans laquelle le contenu doit être traduit
Envoyer la page '%0%' pour traduction
Assignée par
- Tâches ouvertures
+ Tâches ouvertes
Nombre total de mots
Traduire en
Traduction complétée.
- Vous pouvez prévisualiser les pages que vous avez traduites, en cliquant ci-dessous. Si la page originale est trouvée, vous aurez la comparaison entre les deux pages.
- Traductio échouée, il semble que le fichier XML soit corrompu
+ Vous pouvez prévisualiser les pages que vous avez traduites en cliquant ci-dessous. Si la page originale est trouvée, vous verrez une comparaison entre les deux pages.
+ Traduction échouée, il se pourrait que fichier XML soit corrompu
Options de traduction
Traducteur
Uploader le fichier de traduction XML
@@ -965,20 +1190,21 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Navigateur de cache
Corbeille
Packages créés
- Typesde données
+ Types de données
Dictionnaire
Packages installés
- Installer un skin
+ Installer une skin
Installer un starter kit
- Langages
+ Langues
Installer un package local
Macros
- Types de médias
+ Types de média
Membres
Groupes de membres
Rôles
Types de membres
Types de documents
+ Types de relations
Packages
Packages
Fichiers Python
@@ -990,53 +1216,166 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Feuilles de style
Modèles
Fichiers XSLT
+ Analytique
- Nouvelle mise à jour prête
- %0% est prêt, cliquez ici pour télécharger
+ Nouvelle mise à jour disponible
+ %0% est disponible, cliquez ici pour télécharger
Aucune connexion au serveur
- Erreur lors de la recherche de mises à jour. Vérifiez la stack trace pour obtenir plus d'informations sur l'erreur.
+ Erreur lors de la recherche de mises à jour. Veuillez vérifier le stack trace pour obtenir plus d'informations sur l'erreur.
Administrateur
Champ catégorie
Changer le mot de passe
- Changez votre mot de passe
+ Nouveau mot de passe
Confirmez votre nouveau mot de passe
- Vous pouvez changer votre mot de passe d'accès à Umbraco en remplissant le formulaire ci-dessous puis en cliquant sur le bouton "Changer le mot de passe"
+ Vous pouvez changer votre mot de passe d'accès au Back Office Umbraco en remplissant le formulaire ci-dessous puis en cliquant sur le bouton "Changer le mot de passe"
Canal de contenu
Champ description
Désactiver l'utilisateur
Type de document
Editeur
Champ extrait
- Langage
+ Langue
Identifiant
- Noeud de départ dans la librarie de médias
+ Noeud de départ dans la librarie de média
Sections
Désactiver l'accès Umbraco
+ Ancien mot de passe
Mot de passe
Réinitialiser le mot de passe
- Your password has been changed!
- Confirmez s'il vous plait votre nouveau mot de passe
- Entrez votre nouveau mot de passe
+ Votre mot de passe a été modifié!
+ Veuillez confirmer votre nouveau mot de passe
+ Introduisez votre nouveau mot de passe
Votre nouveau mot de passe ne peut être vide !
Mot de passe actuel
Mot de passe actuel invalide
- Il y avait une différence entre le nouveau mot de passe et le mot de passe confirmé. Veuillez réessayer
- Le mot de passe confirmé ne match pas le nouveau mot de passe saisi
- Remplacer les permissions des noeuds enfants
- Vous modifiez actuellement les permissions pour les pages :
- Choisissez les pages pour lesquelles modifier les permissions
+ Il y a une différence entre le nouveau mot de passe et le mot de passe confirmé. Veuillez réessayer.
+ Le mot de passe confirmé ne correspond pas au nouveau mot de passe saisi!
+ Remplacer les permissions sur les noeuds enfants
+ Vous êtes en train de modifiez les permissions pour les pages :
+ Choisissez les pages dont les permissions doivent être modifiées
Rechercher tous les enfants
Noeud de départ du contenu
Nom d'utilisateur
Permissions utilisateur
Type d'utilisateur
- Types d'utilisateur
+ Types d'utilisateurs
Rédacteur
+ Traducteur
+ Modifier
Votre profil
Votre historique récent
La session expire dans
-
+
+ Validation
+ Valider comme email
+ Valider comme nombre
+ Valider comme Url
+ ...ou introduisez une validation spécifique
+ Champ obligatoire
+
+
+
+ La valeur est égale à la valeur recommandée : '%0%'.
+ La valeur du XPath '%2%' a été fixée à '%1%' dans le fichier de configuration '%3%'.
+ La valeur attendue pour '%2%' dans le fichier de configuration '%3%' est '%1%', mais la valeur trouvée est '%0%'.
+ La valeur inattendue '%0%' a été trouvée pour '%2%' dans le fichier de configuration '%3%'.
+
+
+ Custom errors est fixé à la valeur '%0%'.
+ Custom errors est pour la moment fixé à la valeur '%0%'. Il est recommandé de le fixer la valeur à '%1%' avant la mise en ligne.
+ Custom errors a été rectifié avec succès à la valeur '%0%'.
+
+ MacroErrors est fixé à la valeur '%0%'.
+ MacroErrors est fixé à la valeur '%0%', ce qui empêchera certaines ou même toutes les pages de votre site de se charger complètement en cas d'erreur dans les macros. La rectification de ceci fixera la valeur à '%1%'.
+ MacroErrors est maintenant fixé à la valeur '%0%'.
+
+
+ Try Skip IIS Custom Errors est fixé à la valeur '%0%' et vous utilisez IIS version '%1%'.
+ Try Skip IIS Custom Errors est actuellement fixé à '%0%'. Il est recommandé de fixer la valeur à '%1%' pour votre version IIS (%2%).
+ Try Skip IIS Custom Errors a été rectifié avec succès à la valeur '%0%'.
+
+
+ Le fichier n'existe pas : '%0%'.
+ '%0%' dans le fichier config '%1%' .]]>
+ Une erreur est survenue, consultez le log pour voir l'erreur complète : %0%.
+
+ Total XML : %0%, Total : %1%
+ Total XML : %0%, Total : %1%
+ Total XML : %0%, Total publié : %1%
+
+ Erreur de validation du certificat : '%0%'
+ Erreur en essayant de contacter l'URL %0% - '%1%'
+ Vous êtes actuellement %0% à voir le site via le schéma HTTPS.
+ La valeur appSetting 'umbracoUseSSL' est fixée à 'false' dans votre fichier web.config. Une fois que vous donnerez accès à ce site en utilisant le schéma HTTPS, cette valeur devra être mise à 'true'.
+ La valeur appSetting 'umbracoUseSSL' est fixée à '%0%' dans votre fichier web.config, vos cookies sont %1% marqués comme étant sécurisés.
+ Impossible de mettre à jour la configuration 'umbracoUseSSL' dans votre fichier web.config. Erreur : %0%
+
+
+ Activer HTTPS
+ Fixe la configuration 'umbracoSSL' à 'true' dans la section appSettings du fichier web.config.
+ La configuration appSetting 'umbracoUseSSL' est maintenant fixée à 'true' dans votre fichier web.config, vos cookies seront marqués comme étant sécurisés.
+
+ Corriger
+ Impossible de corriger une vérification avec un type de comparaison 'ShouldNotEqual'.
+ Impossible de corriger une vérification avec un type de comparaison 'ShouldEqual' avec une valeur spécifiée.
+ La valeur de correction n'est pas fournie.
+
+ Le mode de compilation Debug est désactivé.
+ Le mode de compilation Debug a été désactivé avec succès.
+ Le mode de compilation Debug est actuellement activé. Il est recommandé de désactiver ce paramètre avant la mise en ligne.
+
+ Le mode tracing est désactivé.
+ Le mode tracing est actuellement activé. Il est recommandé de désactiver cette configuration avant la mise en ligne.
+ Le mode tracing a été désactivé avec succès.
+
+ Tous les répertoires ont les configurations de permissions adéquates.
+
+ %0%.]]>
+ %0%. Aucune action n'est requise s'il n'y a pas de nécessité d'y écrire.]]>
+
+ Tous les fichiers ont les configurations de permissions adéquates.
+
+ %0%.]]>
+ %0%. Aucune action n'est requise s'il n'y a pas de nécessité d'y écrire.]]>
+
+ X-Frame-Options, utilisé pour contrôler si un site peut être intégré dans un autre via IFRAME, a été trouvé.]]>
+ X-Frame-Options , utilisé pour contrôler si un site peut être intégré dans un autre via IFRAME, n'a pas été trouvé.]]>
+ Configurez le Header dans le fichier Config
+ Ajoute une valeur dans la section httpProtocol/customHeaders du fichier web.config afin d'éviter que le site ne soit intégré dans d'autres sites via IFRAME.
+ Une configuration générant un header qui empêche l'intégration du site par d'autres sites via IFRAME a été ajoutée à votre fichier web.config.
+ Impossible de modifier le fichier web.config. Erreur : %0%
+
+
+ %0%.]]>
+ Aucun header révélant des informations à propos de la technologie du site web n'a été trouvé.
+
+ La section system.net/mailsettings n'a pas pu être trouvée dans le fichier Web.config.
+ Dans la section system.net/mailsettings du fichier Web.config, le "host" n'est pas configuré.
+ La configuration SMTP est correcte et le service fonctionne comme prévu.
+ Le serveur SMTP configuré avec le host '%0%' et le port '%1%' n'a pas pu être contacté. Veuillez vérifier et vous assurer que la configuration SMTP est correcte dans la section system.net/mailsettings du fichier Web.config.
+
+ %0%.]]>
+ %0%.]]>
+
From 8ee98ebdf9a944f26dd1e00a32a099e784e48ec2 Mon Sep 17 00:00:00 2001
From: michael
Date: Fri, 9 Sep 2016 13:37:24 +0200
Subject: [PATCH 09/48] Review and add translation labels to language file
fr.xml (sync with latest en version)
---
src/Umbraco.Web.UI/umbraco/config/lang/fr.xml | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
index 2e5ee51bac..7a050d603a 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
@@ -209,6 +209,29 @@
Vous avez des changements en cours
Etes-vous certain(e) de vouloir quitter cette page? - vous avez des changements en cours
+
+ Terminé
+ %0% élément supprimé
+ %0% éléments supprimés
+ %0% élément sur %1% supprimé
+ %0% éléments sur %1% supprimés
+ %0% élément publié
+ %0% éléments publiés
+ %0% élément sur %1% publié
+ %0% éléments sur %1% publiés
+ %0% élément dépublié
+ %0% éléments dépubliés
+ %0% élément sur %1% dépublié
+ %0% éléments sur %1% dépubliés
+ %0% élément déplacé
+ %0% éléments déplacés
+ %0% élément sur %1% déplacé
+ %0% éléments sur %1% déplacés
+ %0% élément copié
+ %0% éléments copiés
+ %0% élément sur %1% copié
+ %0% éléments sur %1% copiés
+
Nom
Gérer les noms d'hôtes
From 6407c249779291d4d295443a56ee73d823f93f7e Mon Sep 17 00:00:00 2001
From: Michael Latouche
Date: Sat, 10 Sep 2016 01:51:45 +0200
Subject: [PATCH 10/48] Update fr.xml
---
src/Umbraco.Web.UI/umbraco/config/lang/fr.xml | 28 -------------------
1 file changed, 28 deletions(-)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
index 8a75e4a7e0..7a050d603a 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
@@ -232,34 +232,6 @@
%0% élément sur %1% copié
%0% éléments sur %1% copiés
-
- Done
-
- Deleted %0% item
- Deleted %0% items
- Deleted %0% out of %1% item
- Deleted %0% out of %1% items
-
- Published %0% item
- Published %0% items
- Published %0% out of %1% item
- Published %0% out of %1% items
-
- Unpublished %0% item
- Unpublished %0% items
- Unpublished %0% out of %1% item
- Unpublished %0% out of %1% items
-
- Moved %0% item
- Moved %0% items
- Moved %0% out of %1% item
- Moved %0% out of %1% items
-
- Copied %0% item
- Copied %0% items
- Copied %0% out of %1% item
- Copied %0% out of %1% items
-
Nom
Gérer les noms d'hôtes
From 2d0725fbade7a4d13ff1778547c545762373bc58 Mon Sep 17 00:00:00 2001
From: michael
Date: Thu, 22 Sep 2016 12:15:13 +0200
Subject: [PATCH 11/48] update with latest labels from en_us.xml
---
src/Umbraco.Web.UI/umbraco/config/lang/fr.xml | 45 +++++++++++++++++--
1 file changed, 41 insertions(+), 4 deletions(-)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
index 7a050d603a..40e18ce298 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
@@ -149,6 +149,7 @@
Propriétés
Ce document est publié mais n'est pas visible car son parent '%0%' n'est pas publié
Oups : ce document est publié mais n'est pas présent dans le cache (erreur interne Umbraco)
+ Oups: impossible d'obtenir cet url (erreur interne - voir fichier log)
Oups: ce document est publié mais son url entrerait en collision avec le contenu %0%
Publier
Statut de publication
@@ -170,7 +171,9 @@
Pas membre du/des groupe(s)
Eléments enfants
Cible
-
+ Ceci se traduit par l'heure suivante sur le serveur :
+ Qu'est-ce que cela signifie?]]>
+
Cliquez pour télécharger
Faites glisser vos fichier ici...
@@ -211,22 +214,27 @@
Terminé
+
%0% élément supprimé
%0% éléments supprimés
%0% élément sur %1% supprimé
%0% éléments sur %1% supprimés
+
%0% élément publié
%0% éléments publiés
%0% élément sur %1% publié
%0% éléments sur %1% publiés
+
%0% élément dépublié
%0% éléments dépubliés
%0% élément sur %1% dépublié
%0% éléments sur %1% dépubliés
+
%0% élément déplacé
%0% éléments déplacés
%0% élément sur %1% déplacé
%0% éléments sur %1% déplacés
+
%0% élément copié
%0% éléments copiés
%0% élément sur %1% copié
@@ -452,6 +460,7 @@
Installer
Non valide
Justifier
+ Libellé
Langue
Mise en page
En cours de chargement
@@ -592,7 +601,8 @@
Le mot de passe par défaut a été modifié avec succès depuis l'installation!Aucune autre action n'est requise. Cliquez sur Suivant pour poursuivre.]]>
Le mot de passe a été modifié !
- ('admin') et le mot de passe ('default') . Il est important que ce mot de passe soit modifié en quelque-chose de sécurisé et unique.]]>
+ ('admin') et le mot de passe ('default') . Il est important que ce mot de passe soit modifié en quelque-chose de sécurisé et unique.
+ ]]>
Pour bien commencer, regardez nos vidéos d'introduction
En cliquant sur le bouton "Suivant" (ou en modifiant umbracoConfigurationStatus dans le fichier web.config), vous acceptez la licence de ce logiciel telle que spécifiée dans le champ ci-dessous. Veuillez noter que cette distribution Umbraco consiste en deux licences différentes, la licence open source MIT pour le framework et la licence Umbraco freeware qui couvre l'UI.
Pas encore installé.
@@ -690,7 +700,8 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Joyeux vendredi
Joyeux samedi
Connectez-vous ci-dessous
- La session a expiré
+ Identifiez-vous avec
+ La session a expiré
© 2001 - %0% Umbraco.com ]]>
Mot de passe oublié?
Un email contenant un lien pour ré-initialiser votre mot de passe sera envoyé à l'adresse spécifiée
@@ -800,6 +811,14 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Version du package
Historique des versions du package
Voir le site internet du package
+ Package déjà installé
+ Ce package ne peut pas être installé, il nécessite au minimum la version Umbraco %0%
+ Désinstallation...
+ Téléchargement...
+ Import...
+ Installation...
+ Redémarrage, veuillez patienter...
+ Terminé, votre navigateur va être rafraîchi, veuillez patienter...
Coller en conservant le formatage (non recommandé)
@@ -1401,4 +1420,22 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
%0%.]]>
%0%.]]>
-
+
+
+ Désactiver URL tracker
+ Activer URL tracker
+ URL original
+ Redirigé Vers
+ Aucune redirection n'a été créée
+ Lorsqu'une page publiée est renommée ou déplacée, une redirection sera automatiquement créée vers la nouvelle page.
+ Supprimer
+ Etes-vous certain(e) de vouloir supprimer la redirection de '%0%' vers '%1%'?
+ Redirection d'URL supprimée.
+ Erreur lors de la suppression de la redirection d'URL.
+ Etes-vous certain(e) de vouloir désactiver le URL tracker?
+ URL tracker est maintenant désactivé.
+ Erreur lors de la désactivation de l'URL tracker, plus d'information disponible dans votre fichier log.
+ URL tracker est maintenant activé.
+ Erreur lors de l'activation de l'URL tracker, plus d'information disponible dans votre fichier log.
+
+
From f0db4824ec26595a03fd26fbb95380f0a4492394 Mon Sep 17 00:00:00 2001
From: michael
Date: Fri, 30 Sep 2016 23:20:17 +0200
Subject: [PATCH 12/48] Solve issue U4-8782 : E-mail validation when editing
user + update error message when creating new user to use localized text
---
src/Umbraco.Web.UI/umbraco/create/User.ascx | 2 +-
.../umbraco/users/EditUser.aspx.cs | 24 +++++++++++++++++++
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI/umbraco/create/User.ascx b/src/Umbraco.Web.UI/umbraco/create/User.ascx
index deb139c557..3a00811776 100644
--- a/src/Umbraco.Web.UI/umbraco/create/User.ascx
+++ b/src/Umbraco.Web.UI/umbraco/create/User.ascx
@@ -22,7 +22,7 @@
ControlToValidate="Email" ValidateEmptyText="false" OnServerValidate="EmailExistsCheck">
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
index d9605506b4..bec91d3313 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
@@ -173,9 +173,29 @@ namespace umbraco.cms.presentation.user
passw.Controls.Add(passwordChanger);
passw.Controls.Add(validatorContainer);
+ // Add email validator
+ var emailValidation = new CustomValidator
+ {
+ ID = "EmailValidator",
+ ErrorMessage = ui.Text("errorHandling", "errorRegExpWithoutTab", "E-mail", CurrentUser),
+ Display = ValidatorDisplay.None
+ };
+ emailValidation.ServerValidate += EmailValidator_OnServerValidate;
+
+ var validationSummary = new ValidationSummary
+ {
+ ID = "validationSummary",
+ DisplayMode = ValidationSummaryDisplayMode.BulletList,
+ CssClass = "error"
+ };
+
+ pp.addProperty(validationSummary);
+
pp.addProperty(ui.Text("user", "username", UmbracoUser), uname);
pp.addProperty(ui.Text("user", "loginname", UmbracoUser), lname);
pp.addProperty(ui.Text("user", "password", UmbracoUser), passw);
+
+ pp.addProperty(emailValidation);
pp.addProperty(ui.Text("email", UmbracoUser), email);
pp.addProperty(ui.Text("user", "usertype", UmbracoUser), userType);
pp.addProperty(ui.Text("user", "language", UmbracoUser), userLanguage);
@@ -227,6 +247,10 @@ namespace umbraco.cms.presentation.user
.SyncTree(UID.ToString(), IsPostBack);
}
+ private void EmailValidator_OnServerValidate(object source, ServerValidateEventArgs args)
+ {
+ args.IsValid = MembershipProviderBase.IsEmailValid(email.Text);
+ }
void sectionValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
From f1c8fb7b17e3676203bcf2a5c91fcd1887d5b40a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dennis=20J=C3=B8rgensen?=
Date: Sat, 15 Oct 2016 22:00:03 +0200
Subject: [PATCH 13/48] Added validation to the user edit-form in the
backoffice
---
.../umbraco/users/EditUser.aspx.cs | 77 +++++++++++++++++--
src/umbraco.controls/pane.cs | 16 ++++
2 files changed, 86 insertions(+), 7 deletions(-)
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
index d9605506b4..a25d62665b 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
@@ -30,6 +30,8 @@ using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using PropertyType = umbraco.cms.businesslogic.propertytype.PropertyType;
+using System.Text.RegularExpressions;
+using System.Text;
namespace umbraco.cms.presentation.user
{
@@ -43,11 +45,16 @@ namespace umbraco.cms.presentation.user
CurrentApp = DefaultApps.users.ToString();
}
protected HtmlTable macroProperties;
- protected TextBox uname = new TextBox();
- protected TextBox lname = new TextBox();
+ protected TextBox uname = new TextBox() { ID = "uname" };
+ protected RequiredFieldValidator unameValidator = new RequiredFieldValidator();
+ protected TextBox lname = new TextBox() { ID = "lname" };
+ protected RequiredFieldValidator lnameValidator = new RequiredFieldValidator();
+ protected CustomValidator lnameCustomValidator = new CustomValidator();
protected PlaceHolder passw = new PlaceHolder();
protected CheckBoxList lapps = new CheckBoxList();
- protected TextBox email = new TextBox();
+ protected TextBox email = new TextBox() { ID = "email" };
+ protected RequiredFieldValidator emailValidator = new RequiredFieldValidator();
+ protected CustomValidator emailCustomValidator = new CustomValidator();
protected DropDownList userType = new DropDownList();
protected DropDownList userLanguage = new DropDownList();
protected CheckBox NoConsole = new CheckBox();
@@ -173,10 +180,10 @@ namespace umbraco.cms.presentation.user
passw.Controls.Add(passwordChanger);
passw.Controls.Add(validatorContainer);
- pp.addProperty(ui.Text("user", "username", UmbracoUser), uname);
- pp.addProperty(ui.Text("user", "loginname", UmbracoUser), lname);
+ pp.addProperty(ui.Text("user", "username", UmbracoUser), uname, unameValidator);
+ pp.addProperty(ui.Text("user", "loginname", UmbracoUser), lname, lnameValidator, lnameCustomValidator);
pp.addProperty(ui.Text("user", "password", UmbracoUser), passw);
- pp.addProperty(ui.Text("email", UmbracoUser), email);
+ pp.addProperty(ui.Text("email", UmbracoUser), email, emailValidator, emailCustomValidator);
pp.addProperty(ui.Text("user", "usertype", UmbracoUser), userType);
pp.addProperty(ui.Text("user", "language", UmbracoUser), userLanguage);
@@ -219,6 +226,50 @@ namespace umbraco.cms.presentation.user
sectionValidator.CssClass = "error";
sectionValidator.Style.Add("color", "red");
+ unameValidator.ControlToValidate = uname.ID;
+ unameValidator.Display = ValidatorDisplay.Dynamic;
+ unameValidator.ErrorMessage = ui.Text("defaultdialogs", "requiredField", UmbracoUser);
+ unameValidator.CssClass = "error";
+ unameValidator.Style.Add("color", "red");
+ unameValidator.Style.Add("margin-left", "5px");
+ unameValidator.Style.Add("line-height", "28px");
+
+ lnameValidator.ControlToValidate = lname.ID;
+ lnameValidator.Display = ValidatorDisplay.Dynamic;
+ lnameValidator.ErrorMessage = ui.Text("defaultdialogs", "requiredField", UmbracoUser);
+ lnameValidator.CssClass = "error";
+ lnameValidator.Style.Add("color", "red");
+ lnameValidator.Style.Add("margin-left", "5px");
+ lnameValidator.Style.Add("line-height", "28px");
+
+ lnameCustomValidator.ServerValidate += LnameCustomValidator_ServerValidate;
+ lnameCustomValidator.Display = ValidatorDisplay.Dynamic;
+ lnameCustomValidator.ControlToValidate = lname.ID;
+ var localizedLname = ui.Text("user", "loginname", UmbracoUser);
+ lnameCustomValidator.ErrorMessage = ui.Text("errorHandling", "errorExistsWithoutTab", localizedLname, UmbracoUser);
+ lnameCustomValidator.CssClass = "error";
+ lnameCustomValidator.Style.Add("color", "red");
+ lnameCustomValidator.Style.Add("margin-left", "5px");
+ lnameCustomValidator.Style.Add("line-height", "28px");
+
+ emailValidator.ControlToValidate = email.ID;
+ emailValidator.Display = ValidatorDisplay.Dynamic;
+ emailValidator.ErrorMessage = ui.Text("defaultdialogs", "requiredField", UmbracoUser);
+ emailValidator.CssClass = "error";
+ emailValidator.Style.Add("color", "red");
+ emailValidator.Style.Add("margin-left", "5px");
+ emailValidator.Style.Add("line-height", "28px");
+
+ emailCustomValidator.ServerValidate += EmailCustomValidator_ServerValidate;
+ emailCustomValidator.Display = ValidatorDisplay.Dynamic;
+ emailCustomValidator.ControlToValidate = email.ID;
+ var localizedEmail = ui.Text("general", "email", UmbracoUser);
+ emailCustomValidator.ErrorMessage = ui.Text("errorHandling", "errorRegExpWithoutTab", localizedEmail, UmbracoUser);
+ emailCustomValidator.CssClass = "error";
+ emailCustomValidator.Style.Add("color", "red");
+ emailCustomValidator.Style.Add("margin-left", "5px");
+ emailCustomValidator.Style.Add("line-height", "28px");
+
SetupForm();
SetupChannel();
@@ -227,6 +278,16 @@ namespace umbraco.cms.presentation.user
.SyncTree(UID.ToString(), IsPostBack);
}
+ private void LnameCustomValidator_ServerValidate(object source, ServerValidateEventArgs args)
+ {
+ var usersWithLoginName = ApplicationContext.Services.UserService.GetByUsername(lname.Text);
+ args.IsValid = usersWithLoginName == null || usersWithLoginName.Id == u.Id;
+ }
+
+ private void EmailCustomValidator_ServerValidate(object source, ServerValidateEventArgs args)
+ {
+ args.IsValid = Regex.IsMatch(email.Text.Trim(), @"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$");
+ }
void sectionValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
@@ -531,7 +592,9 @@ namespace umbraco.cms.presentation.user
}
else
{
- ClientTools.ShowSpeechBubble(speechBubbleIcon.error, ui.Text("speechBubbles", "editUserError", UmbracoUser), "");
+ ClientTools.ShowSpeechBubble(speechBubbleIcon.error,
+ ui.Text("speechBubbles", "validationFailedHeader", UmbracoUser),
+ ui.Text("speechBubbles", "validationFailedMessage", UmbracoUser));
}
}
diff --git a/src/umbraco.controls/pane.cs b/src/umbraco.controls/pane.cs
index 0facc946a3..499695debf 100644
--- a/src/umbraco.controls/pane.cs
+++ b/src/umbraco.controls/pane.cs
@@ -36,6 +36,22 @@ namespace umbraco.uicontrols
set { m_title = value; }
}
+ public void addProperty(string Caption, Control C, params BaseValidator[] validators)
+ {
+
+ PropertyPanel pp = new PropertyPanel();
+ pp.Controls.Add(C);
+
+ foreach (var validator in validators)
+ {
+ validator.Display = ValidatorDisplay.Dynamic;
+ pp.Controls.Add(validator);
+ }
+ pp.Text = Caption;
+
+ this.Controls.Add(pp);
+ }
+
public void addProperty(string Caption, Control C)
{
From ac0159f67e6fe14193db98626f891785aac7c667 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dennis=20J=C3=B8rgensen?=
Date: Sat, 15 Oct 2016 22:47:35 +0200
Subject: [PATCH 14/48] Changed style for form controls and text for
email-field
---
src/Umbraco.Web.UI.Client/src/less/main.less | 2 +-
.../umbraco.presentation/umbraco/users/EditUser.aspx.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less
index 00cdd4f9b1..85ab4e6e56 100644
--- a/src/Umbraco.Web.UI.Client/src/less/main.less
+++ b/src/Umbraco.Web.UI.Client/src/less/main.less
@@ -157,7 +157,7 @@ h5.-black {
}
.controls-row {
- padding-top: 5px;
+ padding-bottom: 5px;
margin-left: 240px;
}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
index a25d62665b..d371016910 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
@@ -183,7 +183,7 @@ namespace umbraco.cms.presentation.user
pp.addProperty(ui.Text("user", "username", UmbracoUser), uname, unameValidator);
pp.addProperty(ui.Text("user", "loginname", UmbracoUser), lname, lnameValidator, lnameCustomValidator);
pp.addProperty(ui.Text("user", "password", UmbracoUser), passw);
- pp.addProperty(ui.Text("email", UmbracoUser), email, emailValidator, emailCustomValidator);
+ pp.addProperty(ui.Text("general", "email", UmbracoUser), email, emailValidator, emailCustomValidator);
pp.addProperty(ui.Text("user", "usertype", UmbracoUser), userType);
pp.addProperty(ui.Text("user", "language", UmbracoUser), userLanguage);
From fb83b9dca3e6dc208770607e028f64725003c09f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dennis=20J=C3=B8rgensen?=
Date: Sun, 16 Oct 2016 18:45:01 +0200
Subject: [PATCH 15/48] Changed email validation to the validation used when
creating users (MembershipProviderBase).
---
.../umbraco.presentation/umbraco/users/EditUser.aspx.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
index d371016910..0006218cc7 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs
@@ -286,7 +286,7 @@ namespace umbraco.cms.presentation.user
private void EmailCustomValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
- args.IsValid = Regex.IsMatch(email.Text.Trim(), @"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$");
+ args.IsValid = MembershipProviderBase.IsEmailValid(email.Text.Trim());
}
void sectionValidator_ServerValidate(object source, ServerValidateEventArgs args)
From f753dfa2a710917ca863c5c0b33ad21425f9a2f6 Mon Sep 17 00:00:00 2001
From: Claus
Date: Thu, 20 Oct 2016 13:56:52 +0200
Subject: [PATCH 16/48] minor updates and a little reformatting.
---
src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 4 +-
src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 +-
.../umbraco/config/lang/en_us.xml | 2 +-
.../settings/EditDictionaryItem.aspx.cs | 216 +++++++++---------
4 files changed, 107 insertions(+), 117 deletions(-)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
index 130878323e..ffb0a9d96b 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
@@ -308,10 +308,10 @@
Rediger de forskellige sprogversioner for ordbogselementet '%0%' herunder. Du tilføjer flere sprog under 'sprog' i menuen til venstre
]]>
Kulturnavn
- Her kan du ændre nøglen på ordbogselementet.
+ Rediger navnet på ordbogselementet.
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
index 38376231b7..f710e54215 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
@@ -327,7 +327,7 @@
Edit the different language versions for the dictionary item '%0% ' below You can add additional languages under the 'languages' in the menu on the left
]]>
Culture Name
- Here you can change the key of the dictionary item.
+ Edit the key of the dictionary item.
%0%' below You can add additional languages under the 'languages' in the menu on the left
]]>
Culture Name
- Here you can change the key of the dictionary item.
+ Edit the key of the dictionary item.
- /// Summary description for EditDictionaryItem.
- ///
+ ///
+ /// Summary description for EditDictionaryItem.
+ ///
[WebformsPageTreeAuthorize(Constants.Trees.Dictionary)]
- public partial class EditDictionaryItem : BasePages.UmbracoEnsuredPage
- {
-
- protected LiteralControl keyTxt = new LiteralControl();
- protected uicontrols.TabView tbv = new uicontrols.TabView();
- private System.Collections.ArrayList languageFields = new System.Collections.ArrayList();
+ public partial class EditDictionaryItem : BasePages.UmbracoEnsuredPage
+ {
+
+ protected LiteralControl keyTxt = new LiteralControl();
+ protected uicontrols.TabView tbv = new uicontrols.TabView();
+ private System.Collections.ArrayList languageFields = new System.Collections.ArrayList();
private cms.businesslogic.Dictionary.DictionaryItem currentItem;
- protected TextBox boxChangeKey;
- protected Label labelChangeKey;
- protected User currentUser;
+ protected TextBox boxChangeKey;
+ protected Label labelChangeKey;
+ protected Literal txt;
+ protected User currentUser;
- protected void Page_Load(object sender, System.EventArgs e)
- {
- currentItem = new cms.businesslogic.Dictionary.DictionaryItem(int.Parse(Request.QueryString["id"]));
- currentUser = getUser();
+ protected void Page_Load(object sender, System.EventArgs e)
+ {
+ currentItem = new cms.businesslogic.Dictionary.DictionaryItem(int.Parse(Request.QueryString["id"]));
+ currentUser = getUser();
- // Put user code to initialize the page here
- Panel1.hasMenu = true;
- Panel1.Text = ui.Text("editdictionary") + ": " + currentItem.key;
+ // Put user code to initialize the page here
+ Panel1.hasMenu = true;
+ Panel1.Text = ui.Text("editdictionary") + ": " + currentItem.key;
- var save = Panel1.Menu.NewButton();
+ var save = Panel1.Menu.NewButton();
save.Text = ui.Text("save");
save.Click += save_Click;
- save.ToolTip = ui.Text("save");
+ save.ToolTip = ui.Text("save");
save.ID = "save";
save.ButtonType = uicontrols.MenuButtonType.Primary;
uicontrols.Pane p = new uicontrols.Pane();
- Literal txt = new Literal();
- txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, currentUser) + "
";
- p.addProperty(txt);
-
- foreach (cms.businesslogic.language.Language l in cms.businesslogic.language.Language.getAll)
- {
-
- TextBox languageBox = new TextBox();
- languageBox.TextMode = TextBoxMode.MultiLine;
- languageBox.ID = l.id.ToString();
- languageBox.CssClass = "umbEditorTextFieldMultiple";
-
- if (!IsPostBack)
- languageBox.Text = currentItem.Value(l.id);
-
- languageFields.Add(languageBox);
- p.addProperty(l.FriendlyName, languageBox);
-
- }
-
boxChangeKey = new TextBox
{
ID = "changeKey-" + currentItem.id,
@@ -81,42 +54,64 @@ namespace umbraco.settings
ID = "changeKeyLabel",
CssClass = "text-error"
};
-
+
p.addProperty(new Literal
{
- Text = "
" + ui.Text("dictionaryItem", "changeKey", currentUser) + "
"
+ Text = "" + ui.Text("dictionaryItem", "changeKey", currentUser) + "
"
});
p.addProperty(boxChangeKey);
p.addProperty(labelChangeKey);
+
+ txt = new Literal();
+ txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, currentUser) + "
";
+ p.addProperty(txt);
+
+ foreach (cms.businesslogic.language.Language l in cms.businesslogic.language.Language.getAll)
+ {
+
+ TextBox languageBox = new TextBox();
+ languageBox.TextMode = TextBoxMode.MultiLine;
+ languageBox.ID = l.id.ToString();
+ languageBox.CssClass = "umbEditorTextFieldMultiple";
+
+ if (!IsPostBack)
+ languageBox.Text = currentItem.Value(l.id);
+
+ languageFields.Add(languageBox);
+ p.addProperty(l.FriendlyName, languageBox);
+
+ }
+
+
if (!IsPostBack)
- {
- var path = BuildPath(currentItem);
- ClientTools
- .SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree().Tree.Alias)
- .SyncTree(path, false);
- }
+ {
+ var path = BuildPath(currentItem);
+ ClientTools
+ .SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree().Tree.Alias)
+ .SyncTree(path, false);
+ }
Panel1.Controls.Add(p);
- }
+ }
- private string BuildPath(cms.businesslogic.Dictionary.DictionaryItem current)
- {
- var parentPath = current.IsTopMostItem() ? "" : BuildPath(current.Parent) + ",";
- return parentPath + current.id;
- }
+ private string BuildPath(cms.businesslogic.Dictionary.DictionaryItem current)
+ {
+ var parentPath = current.IsTopMostItem() ? "" : BuildPath(current.Parent) + ",";
+ return parentPath + current.id;
+ }
void save_Click(object sender, EventArgs e)
{
- foreach (TextBox t in languageFields)
+ foreach (TextBox t in languageFields)
{
//check for null but allow empty string!
// http://issues.umbraco.org/issue/U4-1931
- if (t.Text != null)
+ if (t.Text != null)
{
- currentItem.setValue(int.Parse(t.ID),t.Text);
- }
- }
+ currentItem.setValue(int.Parse(t.ID), t.Text);
+ }
+ }
labelChangeKey.Text = ""; // reset error text
var newKey = boxChangeKey.Text;
@@ -140,55 +135,50 @@ namespace umbraco.settings
var path = BuildPath(currentItem);
ClientTools.SyncTree(path, true);
}
- }
+ }
+ txt.Text = "" + ui.Text("dictionaryItem", "description", currentItem.key, currentUser) + "
";
+ ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), "");
+ }
- ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "dictionaryItemSaved"), "");
- }
+ #region Web Form Designer generated code
- #region Web Form Designer generated code
- override protected void OnInit(EventArgs e)
- {
- //
- // CODEGEN: This call is required by the ASP.NET Web Form Designer.
- //
- /*
- tbv.ID="tabview1";
- tbv.Width = 400;
- tbv.Height = 200;
- */
+ override protected void OnInit(EventArgs e)
+ {
+ InitializeComponent();
+ base.OnInit(e);
+ }
- InitializeComponent();
- base.OnInit(e);
- }
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
- }
- #endregion
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ }
+
+ #endregion
+ private class languageTextbox : TextBox
+ {
- private class languageTextbox : TextBox
- {
-
- private int _languageid;
- public int languageid
- {
- set {_languageid = value;}
- get {return _languageid;}
- }
- public languageTextbox(int languageId) : base() {
- this.TextMode = TextBoxMode.MultiLine;
- this.Rows = 10;
- this.Columns = 40;
- this.Attributes.Add("style", "margin: 3px; width: 98%;");
-
- this.languageid = languageId;
- }
- }
- }
+ private int _languageid;
+
+ public int languageid
+ {
+ set { _languageid = value; }
+ get { return _languageid; }
+ }
+
+ public languageTextbox(int languageId) : base()
+ {
+ this.TextMode = TextBoxMode.MultiLine;
+ this.Rows = 10;
+ this.Columns = 40;
+ this.Attributes.Add("style", "margin: 3px; width: 98%;");
+
+ this.languageid = languageId;
+ }
+ }
+ }
}
From ee8efbb1ab66498425da856d2dca69ba4063e16e Mon Sep 17 00:00:00 2001
From: Shannon
Date: Fri, 21 Oct 2016 16:11:57 +0200
Subject: [PATCH 17/48] Adds notes about bulk copy
---
src/Umbraco.Core/Persistence/PetaPocoExtensions.cs | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
index a72621d1a5..610b3d9b02 100644
--- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
+++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
@@ -195,6 +195,16 @@ namespace Umbraco.Core.Persistence
///
public static void BulkInsertRecords(this Database db, IEnumerable collection, Transaction tr, bool commitTrans = false)
{
+ //TODO: We should change this to use BulkCopy, as an example see:
+ // https://ayende.com/blog/4137/nhibernate-perf-tricks
+ // Even though this just generates lots of raw sql INSERT statements BulkCopy is the fastest it can possibly be
+ // and we should be able to do this using the current connection from the PetaPoco db instance (and would probably be much cleaner)
+ //
+ // BulkCopy is available for SQL Server and MySqlBulkLoader is available for MySql, pretty sure BulkCopy works for SQLCE so
+ // we should be covered and of course could fallback to this method if that is not our database. But we would get huge perf
+ // increases for this.
+
+
//don't do anything if there are no records.
if (collection.Any() == false)
return;
From d64757f3b07c12e67c02a9127eb419da9d67c9ab Mon Sep 17 00:00:00 2001
From: Shannon
Date: Fri, 21 Oct 2016 16:17:13 +0200
Subject: [PATCH 18/48] U4-9093 Fix SQL used to rebuild the media & content xml
structures in the data integrity check
---
.../Repositories/ContentRepository.cs | 29 ++++++++-----------
1 file changed, 12 insertions(+), 17 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 9222769247..b4ff7be361 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -183,30 +183,25 @@ namespace Umbraco.Core.Persistence.Repositories
if (contentTypeIds == null)
{
var subQuery = new Sql()
- .Select("DISTINCT cmsContentXml.nodeId")
- .From()
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId);
+ .Select("id")
+ .From(SqlSyntax)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
- foreach (var id in contentTypeIds)
- {
- var id1 = id;
- var subQuery = new Sql()
- .Select("cmsDocument.nodeId")
- .From()
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
- .Where(dto => dto.Published)
- .Where(dto => dto.ContentTypeId == id1);
+ var subQuery = new Sql()
+ .Select("DISTINCT umbracoNode.id")
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax)
+ .On(SqlSyntax, left => left.NodeId, right => right.NodeId)
+ .WhereIn(dto => dto.ContentTypeId, contentTypeIds, SqlSyntax)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId);
- var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
- Database.Execute(deleteSql);
- }
+ var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
+ Database.Execute(deleteSql);
}
//now insert the data, again if something fails here, the whole transaction is reversed
From 4f77caee0297072d885c7194aa2a84af118db2c0 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Fri, 21 Oct 2016 16:23:32 +0200
Subject: [PATCH 19/48] U4-9093 Fix SQL used to rebuild the media & content xml
structures in the data integrity check
---
.../Repositories/MediaRepository.cs | 34 +++++++------------
.../Repositories/MemberRepository.cs | 34 +++++++------------
2 files changed, 24 insertions(+), 44 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
index 8c9bec71d4..e311ac9297 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
@@ -172,36 +172,26 @@ namespace Umbraco.Core.Persistence.Repositories
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
- var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var subQuery = new Sql()
- .Select("DISTINCT cmsContentXml.nodeId")
- .From()
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
- .Where(dto => dto.NodeObjectType == mediaObjectType);
+ .Select("id")
+ .From(SqlSyntax)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
- foreach (var id in contentTypeIds)
- {
- var id1 = id;
- var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
- var subQuery = new Sql()
- .Select("DISTINCT cmsContentXml.nodeId")
- .From()
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
- .Where(dto => dto.NodeObjectType == mediaObjectType)
- .Where(dto => dto.ContentTypeId == id1);
+ var subQuery = new Sql()
+ .Select("DISTINCT umbracoNode.id")
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax)
+ .On(SqlSyntax, left => left.NodeId, right => right.NodeId)
+ .WhereIn(dto => dto.ContentTypeId, contentTypeIds, SqlSyntax)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId);
- var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
- Database.Execute(deleteSql);
- }
+ var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
+ Database.Execute(deleteSql);
}
//now insert the data, again if something fails here, the whole transaction is reversed
diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
index 0f0e797f17..379139b4f7 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
@@ -389,36 +389,26 @@ namespace Umbraco.Core.Persistence.Repositories
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
- var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
var subQuery = new Sql()
- .Select("DISTINCT cmsContentXml.nodeId")
- .From()
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
- .Where(dto => dto.NodeObjectType == memberObjectType);
+ .Select("id")
+ .From(SqlSyntax)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
- foreach (var id in contentTypeIds)
- {
- var id1 = id;
- var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
- var subQuery = new Sql()
- .Select("DISTINCT cmsContentXml.nodeId")
- .From()
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
- .Where(dto => dto.NodeObjectType == memberObjectType)
- .Where(dto => dto.ContentTypeId == id1);
+ var subQuery = new Sql()
+ .Select("DISTINCT umbracoNode.id")
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax)
+ .On(SqlSyntax, left => left.NodeId, right => right.NodeId)
+ .WhereIn(dto => dto.ContentTypeId, contentTypeIds, SqlSyntax)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId);
- var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
- Database.Execute(deleteSql);
- }
+ var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
+ Database.Execute(deleteSql);
}
//now insert the data, again if something fails here, the whole transaction is reversed
From 1a8f53d1dec180220bd56ec617191c9f6b3225a8 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Fri, 21 Oct 2016 18:16:02 +0200
Subject: [PATCH 20/48] Fixes tests
---
.../Persistence/Repositories/ContentRepository.cs | 6 ++++--
.../Persistence/Repositories/MediaRepository.cs | 2 +-
.../Persistence/Repositories/MemberRepository.cs | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index b4ff7be361..7d61470747 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -193,8 +193,10 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
var subQuery = new Sql()
- .Select("DISTINCT umbracoNode.id")
- .From(SqlSyntax)
+ .Select("umbracoNode.id as nodeId")
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax)
+ .On(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin(SqlSyntax)
.On(SqlSyntax, left => left.NodeId, right => right.NodeId)
.WhereIn(dto => dto.ContentTypeId, contentTypeIds, SqlSyntax)
diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
index e311ac9297..598c9e912d 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
@@ -183,7 +183,7 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
var subQuery = new Sql()
- .Select("DISTINCT umbracoNode.id")
+ .Select("umbracoNode.id as nodeId")
.From(SqlSyntax)
.InnerJoin(SqlSyntax)
.On(SqlSyntax, left => left.NodeId, right => right.NodeId)
diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
index 379139b4f7..216fc223db 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
@@ -400,7 +400,7 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
var subQuery = new Sql()
- .Select("DISTINCT umbracoNode.id")
+ .Select("umbracoNode.id as nodeId")
.From(SqlSyntax)
.InnerJoin(SqlSyntax)
.On(SqlSyntax, left => left.NodeId, right => right.NodeId)
From 92f5ad220ac9b12556e38a7e883b3ec58b80223e Mon Sep 17 00:00:00 2001
From: Shannon
Date: Fri, 21 Oct 2016 18:17:31 +0200
Subject: [PATCH 21/48] oops, changes back so we have less joins, we don't need
to query on cmsDocument or published, we know it will be that
---
.../Persistence/Repositories/ContentRepository.cs | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 7d61470747..cafbabd286 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -194,9 +194,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var subQuery = new Sql()
.Select("umbracoNode.id as nodeId")
- .From(SqlSyntax)
- .InnerJoin(SqlSyntax)
- .On(SqlSyntax, left => left.NodeId, right => right.NodeId)
+ .From(SqlSyntax)
.InnerJoin(SqlSyntax)
.On(SqlSyntax, left => left.NodeId, right => right.NodeId)
.WhereIn(dto => dto.ContentTypeId, contentTypeIds, SqlSyntax)
From dadcbc8b31248d24d1f9fcf95af4e884f0d51cf1 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 25 Oct 2016 12:29:17 +0200
Subject: [PATCH 22/48] U4-9107 Change BulkInsertRecords to use BulkCopy or
TableDirect (SQLCE)
---
src/Umbraco.Core/DatabaseContext.cs | 2 +-
.../Persistence/BulkDataReader.cs | 1511 ++++++++++
.../DefinitionFactory.cs | 8 +-
.../Persistence/DatabaseSchemaHelper.cs | 2 +-
.../Initial/DatabaseSchemaCreation.cs | 2 +-
.../Persistence/PetaPocoExtensions.cs | 394 ++-
.../Persistence/PocoDataDataReader.cs | 159 ++
.../Repositories/ContentRepository.cs | 6 +-
.../Persistence/SqlSyntax/DbTypes.cs | 1 +
.../MicrosoftSqlSyntaxProviderBase.cs | 108 +
src/Umbraco.Core/Umbraco.Core.csproj | 2 +
.../Persistence/BulkDataReaderTests.cs | 2432 +++++++++++++++++
.../Persistence/PetaPocoCachesTest.cs | 198 ++
.../Persistence/PetaPocoExtensionsTest.cs | 354 ++-
.../MySqlSyntaxProviderTests.cs | 2 +-
.../SqlCeSyntaxProviderTests.cs | 2 +-
src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +
17 files changed, 4901 insertions(+), 284 deletions(-)
create mode 100644 src/Umbraco.Core/Persistence/BulkDataReader.cs
create mode 100644 src/Umbraco.Core/Persistence/PocoDataDataReader.cs
create mode 100644 src/Umbraco.Tests/Persistence/BulkDataReaderTests.cs
create mode 100644 src/Umbraco.Tests/Persistence/PetaPocoCachesTest.cs
diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs
index 05aba6b97d..585f1d51cf 100644
--- a/src/Umbraco.Core/DatabaseContext.cs
+++ b/src/Umbraco.Core/DatabaseContext.cs
@@ -174,7 +174,7 @@ namespace Umbraco.Core
///
public void ConfigureEmbeddedDatabaseConnection()
{
- const string providerName = "System.Data.SqlServerCe.4.0";
+ const string providerName = Constants.DatabaseProviders.SqlCe;
var connectionString = GetEmbeddedDatabaseConnectionString();
SaveConnectionString(connectionString, providerName);
diff --git a/src/Umbraco.Core/Persistence/BulkDataReader.cs b/src/Umbraco.Core/Persistence/BulkDataReader.cs
new file mode 100644
index 0000000000..8df4dd536e
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/BulkDataReader.cs
@@ -0,0 +1,1511 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Data;
+using System.Data.Common;
+using System.Data.SqlClient;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core.Persistence
+{
+ ///
+ /// A base implementation of that is suitable for .
+ ///
+ ///
+ ///
+ /// Borrowed from Microsoft:
+ /// See: https://blogs.msdn.microsoft.com/anthonybloesch/2013/01/23/bulk-loading-data-with-idatareader-and-sqlbulkcopy/
+ ///
+ /// This implementation is designed to be very memory efficient requiring few memory resources and to support
+ /// rapid transfer of data to SQL Server.
+ ///
+ /// Subclasses should implement , ,
+ /// , , .
+ /// If they contain disposable resources they should override .
+ ///
+ /// SD: Alternatively, we could have used a LinqEntityDataReader which is nicer to use but it uses quite a lot of reflection and
+ /// I thought this would just be quicker.
+ /// Simple example of that: https://github.com/gridsum/DataflowEx/blob/master/Gridsum.DataflowEx/Databases/BulkDataReader.cs
+ /// Full example of that: https://github.com/matthewschrager/Repository/blob/master/Repository.EntityFramework/EntityDataReader.cs
+ /// So we know where to find that if we ever need it, these would convert any Linq data source to an IDataReader
+ ///
+ ///
+ internal abstract class BulkDataReader : IDataReader
+ {
+
+ #region Fields
+
+ ///
+ /// The containing the input row set's schema information
+ /// requires to function correctly.
+ ///
+ private DataTable _schemaTable = new DataTable();
+
+ ///
+ /// The mapping from the row set input to the target table's columns.
+ ///
+ private List _columnMappings = new List();
+
+ #endregion
+
+ #region Subclass utility routines
+
+ ///
+ /// The mapping from the row set input to the target table's columns.
+ ///
+ ///
+ /// If necessary, will be called to initialize the mapping.
+ ///
+ public ReadOnlyCollection ColumnMappings
+ {
+ get
+ {
+ if (this._columnMappings.Count == 0)
+ {
+ // Need to add the column definitions and mappings.
+ AddSchemaTableRows();
+
+ if (this._columnMappings.Count == 0)
+ {
+ throw new InvalidOperationException("AddSchemaTableRows did not add rows.");
+ }
+
+ Debug.Assert(this._schemaTable.Rows.Count == FieldCount);
+ }
+
+ return new ReadOnlyCollection(_columnMappings);
+ }
+ }
+
+ ///
+ /// The name of the input row set's schema.
+ ///
+ ///
+ /// This may be different from the target schema but usually they are identical.
+ ///
+ protected abstract string SchemaName
+ {
+ get;
+ }
+
+ ///
+ /// The name of the input row set's table.
+ ///
+ ///
+ /// This may be different from the target table but usually they are identical.
+ ///
+ protected abstract string TableName
+ {
+ get;
+ }
+
+ ///
+ /// Adds the input row set's schema to the object.
+ ///
+ ///
+ /// Call
+ /// to do this for each row.
+ ///
+ ///
+ protected abstract void AddSchemaTableRows();
+
+ ///
+ /// For each , the optional columns that may have values.
+ ///
+ ///
+ /// This is used for checking the parameters of .
+ ///
+ ///
+ private static readonly Dictionary> AllowedOptionalColumnCombinations = new Dictionary>
+ {
+ { SqlDbType.BigInt, new List { } },
+ { SqlDbType.Binary, new List { SchemaTableColumn.ColumnSize } },
+ { SqlDbType.Bit, new List { } },
+ { SqlDbType.Char, new List { SchemaTableColumn.ColumnSize } },
+ { SqlDbType.Date, new List { } },
+ { SqlDbType.DateTime, new List { } },
+ { SqlDbType.DateTime2, new List { SchemaTableColumn.NumericPrecision } },
+ { SqlDbType.DateTimeOffset, new List { SchemaTableColumn.NumericPrecision } },
+ { SqlDbType.Decimal, new List { SchemaTableColumn.NumericPrecision, SchemaTableColumn.NumericScale } },
+ { SqlDbType.Float, new List { SchemaTableColumn.NumericPrecision, SchemaTableColumn.NumericScale } },
+ { SqlDbType.Image, new List { } },
+ { SqlDbType.Int, new List { } },
+ { SqlDbType.Money, new List { } },
+ { SqlDbType.NChar, new List { SchemaTableColumn.ColumnSize } },
+ { SqlDbType.NText, new List { } },
+ { SqlDbType.NVarChar, new List { SchemaTableColumn.ColumnSize } },
+ { SqlDbType.Real, new List { } },
+ { SqlDbType.SmallDateTime, new List { } },
+ { SqlDbType.SmallInt, new List { } },
+ { SqlDbType.SmallMoney, new List { } },
+ { SqlDbType.Structured, new List { } },
+ { SqlDbType.Text, new List { } },
+ { SqlDbType.Time, new List { SchemaTableColumn.NumericPrecision } },
+ { SqlDbType.Timestamp, new List { } },
+ { SqlDbType.TinyInt, new List { } },
+ { SqlDbType.Udt, new List { BulkDataReader.DataTypeNameSchemaColumn } },
+ { SqlDbType.UniqueIdentifier, new List { } },
+ { SqlDbType.VarBinary, new List { SchemaTableColumn.ColumnSize } },
+ { SqlDbType.VarChar, new List { SchemaTableColumn.ColumnSize } },
+ { SqlDbType.Variant, new List { } },
+ { SqlDbType.Xml, new List { BulkDataReader.XmlSchemaCollectionDatabaseSchemaColumn, BulkDataReader.XmlSchemaCollectionOwningSchemaSchemaColumn, BulkDataReader.XmlSchemaCollectionNameSchemaColumn } }
+ };
+
+ ///
+ /// A helper method to support .
+ ///
+ ///
+ /// This methds does extensive argument checks. These errors will cause hard to diagnose exceptions in latter
+ /// processing so it is important to detect them when they can be easily associated with the code defect.
+ ///
+ ///
+ /// The combination of values for the parameters is not supported.
+ ///
+ ///
+ /// A null value for the parameter is not supported.
+ ///
+ ///
+ /// The name of the column.
+ ///
+ ///
+ /// The size of the column which may be null if not applicable.
+ ///
+ ///
+ /// The precision of the column which may be null if not applicable.
+ ///
+ ///
+ /// The scale of the column which may be null if not applicable.
+ ///
+ ///
+ /// Are the column values unique (i.e. never duplicated)?
+ ///
+ ///
+ /// Is the column part of the primary key?
+ ///
+ ///
+ /// Is the column nullable (i.e. optional)?
+ ///
+ ///
+ /// The corresponding .
+ ///
+ ///
+ /// The schema name of the UDT.
+ ///
+ ///
+ /// The type name of the UDT.
+ ///
+ ///
+ /// For XML columns the schema collection's database name. Otherwise, null.
+ ///
+ ///
+ /// For XML columns the schema collection's schema name. Otherwise, null.
+ ///
+ ///
+ /// For XML columns the schema collection's name. Otherwise, null.
+ ///
+ ///
+ protected void AddSchemaTableRow(string columnName,
+ int? columnSize,
+ short? numericPrecision,
+ short? numericScale,
+ bool isUnique,
+ bool isKey,
+ bool allowDbNull,
+ SqlDbType providerType,
+ string udtSchema,
+ string udtType,
+ string xmlSchemaCollectionDatabase,
+ string xmlSchemaCollectionOwningSchema,
+ string xmlSchemaCollectionName)
+ {
+ if (string.IsNullOrEmpty(columnName))
+ {
+ throw new ArgumentException("columnName must be a nonempty string.");
+ }
+ else if (columnSize.HasValue && columnSize.Value <= 0)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+ else if (numericPrecision.HasValue && numericPrecision.Value <= 0)
+ {
+ throw new ArgumentOutOfRangeException("numericPrecision");
+ }
+ else if (numericScale.HasValue && numericScale.Value < 0)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+
+ List allowedOptionalColumnList;
+
+ if (BulkDataReader.AllowedOptionalColumnCombinations.TryGetValue(providerType, out allowedOptionalColumnList))
+ {
+ if ((columnSize.HasValue && !allowedOptionalColumnList.Contains(SchemaTableColumn.ColumnSize)) ||
+ (numericPrecision.HasValue && !allowedOptionalColumnList.Contains(SchemaTableColumn.NumericPrecision)) ||
+ (numericScale.HasValue && !allowedOptionalColumnList.Contains(SchemaTableColumn.NumericScale)) ||
+ (udtSchema != null && !allowedOptionalColumnList.Contains(BulkDataReader.DataTypeNameSchemaColumn)) ||
+ (udtType != null && !allowedOptionalColumnList.Contains(BulkDataReader.DataTypeNameSchemaColumn)) ||
+ (xmlSchemaCollectionDatabase != null && !allowedOptionalColumnList.Contains(BulkDataReader.XmlSchemaCollectionDatabaseSchemaColumn)) ||
+ (xmlSchemaCollectionOwningSchema != null && !allowedOptionalColumnList.Contains(BulkDataReader.XmlSchemaCollectionOwningSchemaSchemaColumn)) ||
+ (xmlSchemaCollectionName != null && !allowedOptionalColumnList.Contains(BulkDataReader.XmlSchemaCollectionNameSchemaColumn)))
+ {
+ throw new ArgumentException("Columns are set that are incompatible with the value of providerType.");
+ }
+ }
+ else
+ {
+ throw new ArgumentException("providerType is unsupported.");
+ }
+
+ Type dataType; // Corresponding CLR type.
+ string dataTypeName; // Corresponding SQL Server type.
+ bool isLong = false; // Is the column a large value column (e.g. nvarchar(max))?
+
+ switch (providerType)
+ {
+ case SqlDbType.BigInt:
+ dataType = typeof(long);
+ dataTypeName = "bigint";
+ break;
+
+ case SqlDbType.Binary:
+ dataType = typeof(byte[]);
+
+ if (!columnSize.HasValue)
+ {
+ throw new ArgumentException("columnSize must be specified for \"binary\" type columns.");
+ }
+ else if (columnSize > 8000)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "binary({0})",
+ columnSize.Value);
+ break;
+
+ case SqlDbType.Bit:
+ dataType = typeof(bool);
+ dataTypeName = "bit";
+ break;
+
+ case SqlDbType.Char:
+ dataType = typeof(string);
+
+ if (!columnSize.HasValue)
+ {
+ throw new ArgumentException("columnSize must be specified for \"char\" type columns.");
+ }
+ else if (columnSize > 8000)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "char({0})",
+ columnSize.Value);
+ break;
+
+ case SqlDbType.Date:
+ dataType = typeof(DateTime);
+ dataTypeName = "date";
+ break;
+
+ case SqlDbType.DateTime:
+ dataType = typeof(DateTime);
+ dataTypeName = "datetime";
+ break;
+
+ case SqlDbType.DateTime2:
+ dataType = typeof(DateTime);
+
+ if (numericPrecision.HasValue)
+ {
+ if (numericPrecision.Value > 7)
+ {
+ throw new ArgumentOutOfRangeException("numericPrecision");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "datetime2({0})",
+ numericPrecision.Value);
+ }
+ else
+ {
+ dataTypeName = "datetime2";
+ }
+ break;
+
+ case SqlDbType.DateTimeOffset:
+ dataType = typeof(DateTimeOffset);
+
+ if (numericPrecision.HasValue)
+ {
+ if (numericPrecision.Value > 7)
+ {
+ throw new ArgumentOutOfRangeException("numericPrecision");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "datetimeoffset({0})",
+ numericPrecision.Value);
+ }
+ else
+ {
+ dataTypeName = "datetimeoffset";
+ }
+ break;
+
+ case SqlDbType.Decimal:
+ dataType = typeof(decimal);
+
+ if (!numericPrecision.HasValue || !numericScale.HasValue)
+ {
+ throw new ArgumentException("numericPrecision and numericScale must be specified for \"decimal\" type columns.");
+ }
+ else if (numericPrecision > 38)
+ {
+ throw new ArgumentOutOfRangeException("numericPrecision");
+ }
+ else if (numericScale.Value > numericPrecision.Value)
+ {
+ throw new ArgumentException("numericScale must not be larger than numericPrecision for \"decimal\" type columns.");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "decimal({0}, {1})",
+ numericPrecision.Value,
+ numericScale.Value);
+ break;
+
+ case SqlDbType.Float:
+ dataType = typeof(double);
+
+ if (!numericPrecision.HasValue)
+ {
+ throw new ArgumentException("numericPrecision must be specified for \"float\" type columns");
+ }
+ else if (numericPrecision > 53)
+ {
+ throw new ArgumentOutOfRangeException("numericPrecision");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "float({0})",
+ numericPrecision.Value);
+ break;
+
+ case SqlDbType.Image:
+ dataType = typeof(byte[]);
+ dataTypeName = "image";
+ break;
+
+ case SqlDbType.Int:
+ dataType = typeof(int);
+ dataTypeName = "int";
+ break;
+
+ case SqlDbType.Money:
+ dataType = typeof(decimal);
+ dataTypeName = "money";
+ break;
+
+ case SqlDbType.NChar:
+ dataType = typeof(string);
+
+ if (!columnSize.HasValue)
+ {
+ throw new ArgumentException("columnSize must be specified for \"nchar\" type columns");
+ }
+ else if (columnSize > 4000)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "nchar({0})",
+ columnSize.Value);
+ break;
+
+ case SqlDbType.NText:
+ dataType = typeof(string);
+ dataTypeName = "ntext";
+ break;
+
+ case SqlDbType.NVarChar:
+ dataType = typeof(string);
+
+ if (columnSize.HasValue)
+ {
+ if (columnSize > 4000)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "nvarchar({0})",
+ columnSize.Value);
+ }
+ else
+ {
+ isLong = true;
+
+ dataTypeName = "nvarchar(max)";
+ }
+ break;
+
+ case SqlDbType.Real:
+ dataType = typeof(float);
+ dataTypeName = "real";
+ break;
+
+ case SqlDbType.SmallDateTime:
+ dataType = typeof(DateTime);
+ dataTypeName = "smalldatetime";
+ break;
+
+ case SqlDbType.SmallInt:
+ dataType = typeof(Int16);
+ dataTypeName = "smallint";
+ break;
+
+ case SqlDbType.SmallMoney:
+ dataType = typeof(decimal);
+ dataTypeName = "smallmoney";
+ break;
+
+ // SqlDbType.Structured not supported because it related to nested rowsets.
+
+ case SqlDbType.Text:
+ dataType = typeof(string);
+ dataTypeName = "text";
+ break;
+
+ case SqlDbType.Time:
+ dataType = typeof(TimeSpan);
+
+ if (numericPrecision.HasValue)
+ {
+ if (numericPrecision > 7)
+ {
+ throw new ArgumentOutOfRangeException("numericPrecision");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "time({0})",
+ numericPrecision.Value);
+ }
+ else
+ {
+ dataTypeName = "time";
+ }
+ break;
+
+
+ // SqlDbType.Timestamp not supported because rowversions are not settable.
+
+ case SqlDbType.TinyInt:
+ dataType = typeof(byte);
+ dataTypeName = "tinyint";
+ break;
+
+ case SqlDbType.Udt:
+ if (string.IsNullOrEmpty(udtSchema))
+ {
+ throw new ArgumentException("udtSchema must be nonnull and nonempty for \"UDT\" columns.");
+ }
+ else if (string.IsNullOrEmpty(udtType))
+ {
+ throw new ArgumentException("udtType must be nonnull and nonempty for \"UDT\" columns.");
+ }
+
+ dataType = typeof(object);
+ using (SqlCommandBuilder commandBuilder = new SqlCommandBuilder())
+ {
+ dataTypeName = commandBuilder.QuoteIdentifier(udtSchema) + "." + commandBuilder.QuoteIdentifier(udtType);
+ }
+ break;
+
+ case SqlDbType.UniqueIdentifier:
+ dataType = typeof(Guid);
+ dataTypeName = "uniqueidentifier";
+ break;
+
+ case SqlDbType.VarBinary:
+ dataType = typeof(byte[]);
+
+ if (columnSize.HasValue)
+ {
+ if (columnSize > 8000)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "varbinary({0})",
+ columnSize.Value);
+ }
+ else
+ {
+ isLong = true;
+
+ dataTypeName = "varbinary(max)";
+ }
+ break;
+
+ case SqlDbType.VarChar:
+ dataType = typeof(string);
+
+ if (columnSize.HasValue)
+ {
+ if (columnSize > 8000)
+ {
+ throw new ArgumentOutOfRangeException("columnSize");
+ }
+
+ dataTypeName = string.Format(CultureInfo.InvariantCulture,
+ "varchar({0})",
+ columnSize.Value);
+ }
+ else
+ {
+ isLong = true;
+
+ dataTypeName = "varchar(max)";
+ }
+ break;
+
+ case SqlDbType.Variant:
+ dataType = typeof(object);
+ dataTypeName = "sql_variant";
+ break;
+
+ case SqlDbType.Xml:
+ dataType = typeof(string);
+
+ if (xmlSchemaCollectionName == null)
+ {
+ if (xmlSchemaCollectionDatabase != null || xmlSchemaCollectionOwningSchema != null)
+ {
+ throw new ArgumentException("xmlSchemaCollectionDatabase and xmlSchemaCollectionOwningSchema must be null if xmlSchemaCollectionName is null for \"xml\" columns.");
+ }
+
+ dataTypeName = "xml";
+ }
+ else
+ {
+ if (xmlSchemaCollectionName.Length == 0)
+ {
+ throw new ArgumentException("xmlSchemaCollectionName must be nonempty or null for \"xml\" columns.");
+ }
+ else if (xmlSchemaCollectionDatabase != null &&
+ xmlSchemaCollectionDatabase.Length == 0)
+ {
+ throw new ArgumentException("xmlSchemaCollectionDatabase must be null or nonempty for \"xml\" columns.");
+ }
+ else if (xmlSchemaCollectionOwningSchema != null &&
+ xmlSchemaCollectionOwningSchema.Length == 0)
+ {
+ throw new ArgumentException("xmlSchemaCollectionOwningSchema must be null or nonempty for \"xml\" columns.");
+ }
+
+ System.Text.StringBuilder schemaCollection = new System.Text.StringBuilder("xml(");
+
+ if (xmlSchemaCollectionDatabase != null)
+ {
+ schemaCollection.Append("[" + xmlSchemaCollectionDatabase + "]");
+ }
+
+ schemaCollection.Append("[" + (xmlSchemaCollectionOwningSchema == null ? SchemaName : xmlSchemaCollectionOwningSchema) + "]");
+ schemaCollection.Append("[" + xmlSchemaCollectionName + "]");
+
+ dataTypeName = schemaCollection.ToString();
+ }
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException("providerType");
+
+ }
+
+ this._schemaTable.Rows.Add(columnName,
+ _schemaTable.Rows.Count,
+ columnSize,
+ numericPrecision,
+ numericScale,
+ isUnique,
+ isKey,
+ "TraceServer",
+ "TraceWarehouse",
+ columnName,
+ SchemaName,
+ TableName,
+ dataType,
+ allowDbNull,
+ providerType,
+ false, // isAliased
+ false, // isExpression
+ false, // isIdentity,
+ false, // isAutoIncrement,
+ false, // isRowVersion,
+ false, // isHidden,
+ isLong,
+ true, // isReadOnly,
+ dataType,
+ dataTypeName,
+ xmlSchemaCollectionDatabase,
+ xmlSchemaCollectionOwningSchema,
+ xmlSchemaCollectionName);
+
+ this._columnMappings.Add(new SqlBulkCopyColumnMapping(columnName, columnName));
+ }
+
+ #endregion
+
+ #region Constructors
+
+ private const string IsIdentitySchemaColumn = "IsIdentity";
+
+ private const string DataTypeNameSchemaColumn = "DataTypeName";
+
+ private const string XmlSchemaCollectionDatabaseSchemaColumn = "XmlSchemaCollectionDatabase";
+
+ private const string XmlSchemaCollectionOwningSchemaSchemaColumn = "XmlSchemaCollectionOwningSchema";
+
+ private const string XmlSchemaCollectionNameSchemaColumn = "XmlSchemaCollectionName";
+
+ ///
+ /// Constructor.
+ ///
+ protected BulkDataReader()
+ {
+ this._schemaTable.Locale = System.Globalization.CultureInfo.InvariantCulture;
+
+ DataColumnCollection columns = _schemaTable.Columns;
+
+ columns.Add(SchemaTableColumn.ColumnName, typeof(System.String));
+ columns.Add(SchemaTableColumn.ColumnOrdinal, typeof(System.Int32));
+ columns.Add(SchemaTableColumn.ColumnSize, typeof(System.Int32));
+ columns.Add(SchemaTableColumn.NumericPrecision, typeof(System.Int16));
+ columns.Add(SchemaTableColumn.NumericScale, typeof(System.Int16));
+ columns.Add(SchemaTableColumn.IsUnique, typeof(System.Boolean));
+ columns.Add(SchemaTableColumn.IsKey, typeof(System.Boolean));
+ columns.Add(SchemaTableOptionalColumn.BaseServerName, typeof(System.String));
+ columns.Add(SchemaTableOptionalColumn.BaseCatalogName, typeof(System.String));
+ columns.Add(SchemaTableColumn.BaseColumnName, typeof(System.String));
+ columns.Add(SchemaTableColumn.BaseSchemaName, typeof(System.String));
+ columns.Add(SchemaTableColumn.BaseTableName, typeof(System.String));
+ columns.Add(SchemaTableColumn.DataType, typeof(System.Type));
+ columns.Add(SchemaTableColumn.AllowDBNull, typeof(System.Boolean));
+ columns.Add(SchemaTableColumn.ProviderType, typeof(System.Int32));
+ columns.Add(SchemaTableColumn.IsAliased, typeof(System.Boolean));
+ columns.Add(SchemaTableColumn.IsExpression, typeof(System.Boolean));
+ columns.Add(BulkDataReader.IsIdentitySchemaColumn, typeof(System.Boolean));
+ columns.Add(SchemaTableOptionalColumn.IsAutoIncrement, typeof(System.Boolean));
+ columns.Add(SchemaTableOptionalColumn.IsRowVersion, typeof(System.Boolean));
+ columns.Add(SchemaTableOptionalColumn.IsHidden, typeof(System.Boolean));
+ columns.Add(SchemaTableColumn.IsLong, typeof(System.Boolean));
+ columns.Add(SchemaTableOptionalColumn.IsReadOnly, typeof(System.Boolean));
+ columns.Add(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(System.Type));
+ columns.Add(BulkDataReader.DataTypeNameSchemaColumn, typeof(System.String));
+ columns.Add(BulkDataReader.XmlSchemaCollectionDatabaseSchemaColumn, typeof(System.String));
+ columns.Add(BulkDataReader.XmlSchemaCollectionOwningSchemaSchemaColumn, typeof(System.String));
+ columns.Add(BulkDataReader.XmlSchemaCollectionNameSchemaColumn, typeof(System.String));
+ }
+
+ #endregion
+
+ #region IDataReader
+
+ ///
+ /// Gets a value indicating the depth of nesting for the current row. (Inherited from .)
+ ///
+ ///
+ /// does not support nested result sets so this method always returns 0.
+ ///
+ ///
+ public int Depth
+ {
+ get { return 0; }
+ }
+
+ ///
+ /// Gets the number of columns in the current row. (Inherited from .)
+ ///
+ ///
+ public int FieldCount
+ {
+ get { return GetSchemaTable().Rows.Count; }
+ }
+
+ ///
+ /// Is the bulk copy process open?
+ ///
+ bool _isOpen = true;
+
+ ///
+ /// Gets a value indicating whether the data reader is closed. (Inherited from .)
+ ///
+ ///
+ public bool IsClosed
+ {
+ get { return !_isOpen; }
+ }
+
+ ///
+ /// Gets the column located at the specified index. (Inherited from .)
+ ///
+ ///
+ /// No column with the specified index was found.
+ ///
+ ///
+ /// The zero-based index of the column to get.
+ ///
+ ///
+ /// The column located at the specified index as an .
+ ///
+ ///
+ public object this[int i]
+ {
+ get { return GetValue(i); }
+ }
+
+ ///
+ /// Gets the column with the specified name. (Inherited from .)
+ ///
+ ///
+ /// No column with the specified name was found.
+ ///
+ ///
+ /// The name of the column to find.
+ ///
+ ///
+ /// The column located at the specified name as an .
+ ///
+ ///
+ public object this[string name]
+ {
+ get { return GetValue(GetOrdinal(name)); }
+ }
+
+ ///
+ /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. (Inherited from .)
+ ///
+ ///
+ /// Always returns -1 which is the expected behaviour for statements.
+ ///
+ ///
+ public virtual int RecordsAffected
+ {
+ get { return -1; }
+ }
+
+ ///
+ /// Closes the . (Inherited from .)
+ ///
+ ///
+ public void Close()
+ {
+ this._isOpen = false;
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public bool GetBoolean(int i)
+ {
+ return (bool)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public byte GetByte(int i)
+ {
+ return (byte)GetValue(i);
+ }
+
+ ///
+ /// Reads a stream of bytes from the specified column offset into the buffer as an array, starting at the given buffer offset.
+ /// (Inherited from .)
+ ///
+ ///
+ /// If you pass a buffer that is null, returns the length of the row in bytes.
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The index within the field from which to start the read operation.
+ ///
+ ///
+ /// The buffer into which to read the stream of bytes.
+ ///
+ ///
+ /// The index for buffer to start the read operation.
+ ///
+ ///
+ /// The number of bytes to read.
+ ///
+ ///
+ /// The actual number of bytes read.
+ ///
+ ///
+ public long GetBytes(int i,
+ long fieldOffset,
+ byte[] buffer,
+ int bufferoffset,
+ int length)
+ {
+ byte[] data = (byte[])GetValue(i);
+
+ if (buffer != null)
+ {
+ Array.Copy(data, fieldOffset, buffer, bufferoffset, length);
+ }
+
+ return data.LongLength;
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public char GetChar(int i)
+ {
+ char result;
+
+ object data = GetValue(i);
+ char? dataAsChar = data as char?;
+ char[] dataAsCharArray = data as char[];
+ string dataAsString = data as string;
+
+ if (dataAsChar.HasValue)
+ {
+ result = dataAsChar.Value;
+ }
+ else if (dataAsCharArray != null &&
+ dataAsCharArray.Length == 1)
+ {
+ result = dataAsCharArray[0];
+ }
+ else if (dataAsString != null &&
+ dataAsString.Length == 1)
+ {
+ result = dataAsString[0];
+ }
+ else
+ {
+ throw new InvalidOperationException("GetValue did not return a Char compatible type.");
+ }
+
+ return result;
+ }
+
+ ///
+ /// Reads a stream of characters from the specified column offset into the buffer as an array, starting at the given buffer offset.
+ /// (Inherited from .)
+ ///
+ ///
+ /// If you pass a buffer that is null, returns the length of the row in bytes.
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The index within the field from which to start the read operation.
+ ///
+ ///
+ /// The buffer into which to read the stream of characters.
+ ///
+ ///
+ /// The index for buffer to start the read operation.
+ ///
+ ///
+ /// The number of characters to read.
+ ///
+ ///
+ /// The actual number of characters read.
+ ///
+ ///
+ public long GetChars(int i,
+ long fieldoffset,
+ char[] buffer,
+ int bufferoffset,
+ int length)
+ {
+ object data = GetValue(i);
+
+ string dataAsString = data as string;
+ char[] dataAsCharArray = data as char[];
+
+ if (dataAsString != null)
+ {
+ dataAsCharArray = dataAsString.ToCharArray((int)fieldoffset, length);
+ }
+ else if (dataAsCharArray == null)
+ {
+ throw new InvalidOperationException("GetValue did not return either a Char array or a String.");
+ }
+
+ if (buffer != null)
+ {
+ Array.Copy(dataAsCharArray, fieldoffset, buffer, bufferoffset, length);
+ }
+
+ return dataAsCharArray.LongLength;
+ }
+
+ ///
+ /// Returns an IDataReader for the specified column ordinal. (Inherited from .)
+ ///
+ ///
+ /// does not support nested result sets so this method always returns null.
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The for the specified column ordinal (null).
+ ///
+ ///
+ public IDataReader GetData(int i)
+ {
+ if (i < 0 || i >= this.FieldCount)
+ {
+ throw new ArgumentOutOfRangeException("i");
+ }
+
+ return null;
+ }
+
+ ///
+ /// The data type information for the specified field. (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The data type information for the specified field.
+ ///
+ ///
+ public string GetDataTypeName(int i)
+ {
+ return GetFieldType(i).Name;
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public DateTime GetDateTime(int i)
+ {
+ return (DateTime)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a .
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ public DateTimeOffset GetDateTimeOffset(int i)
+ {
+ return (DateTimeOffset)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public decimal GetDecimal(int i)
+ {
+ return (Decimal)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public double GetDouble(int i)
+ {
+ return (double)GetValue(i);
+ }
+
+ ///
+ /// Gets the information corresponding to the type of that would be returned from .
+ /// (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The information corresponding to the type of that would be returned from .
+ ///
+ ///
+ public Type GetFieldType(int i)
+ {
+ return (Type)GetSchemaTable().Rows[i][SchemaTableColumn.DataType];
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public float GetFloat(int i)
+ {
+ return (float)this[i];
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public Guid GetGuid(int i)
+ {
+ return (Guid)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public short GetInt16(int i)
+ {
+ return (short)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public int GetInt32(int i)
+ {
+ return (int)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public long GetInt64(int i)
+ {
+ return (long)GetValue(i);
+ }
+
+ ///
+ /// Gets the name for the field to find. (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The name of the field or the empty string (""), if there is no value to return.
+ ///
+ ///
+ public string GetName(int i)
+ {
+ return (string)GetSchemaTable().Rows[i][SchemaTableColumn.ColumnName];
+ }
+
+ ///
+ /// Return the index of the named field. (Inherited from .)
+ ///
+ ///
+ /// The index of the named field was not found.
+ ///
+ ///
+ /// The name of the field to find.
+ ///
+ ///
+ /// The index of the named field.
+ ///
+ ///
+ public int GetOrdinal(string name)
+ {
+ if (name == null) // Empty strings are handled as a IndexOutOfRangeException.
+ {
+ throw new ArgumentNullException("name");
+ }
+
+ int result = -1;
+
+ int rowCount = FieldCount;
+
+ DataRowCollection schemaRows = GetSchemaTable().Rows;
+
+ // Case sensitive search
+ for (int ordinal = 0; ordinal < rowCount; ordinal++)
+ {
+ if (String.Equals((string)schemaRows[ordinal][SchemaTableColumn.ColumnName], name, StringComparison.Ordinal))
+ {
+ result = ordinal;
+ }
+ }
+
+ if (result == -1)
+ {
+ // Case insensitive search.
+ for (int ordinal = 0; ordinal < rowCount; ordinal++)
+ {
+ if (String.Equals((string)schemaRows[ordinal][SchemaTableColumn.ColumnName], name, StringComparison.OrdinalIgnoreCase))
+ {
+ result = ordinal;
+ }
+ }
+ }
+
+ if (result == -1)
+ {
+ throw new IndexOutOfRangeException(name);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Returns a that describes the column metadata of the . (Inherited from .)
+ ///
+ ///
+ /// The is closed.
+ ///
+ ///
+ /// A that describes the column metadata.
+ ///
+ ///
+ public DataTable GetSchemaTable()
+ {
+ if (IsClosed)
+ {
+ throw new InvalidOperationException("The IDataReader is closed.");
+ }
+
+ if (_schemaTable.Rows.Count == 0)
+ {
+ // Need to add the column definitions and mappings
+ _schemaTable.TableName = TableName;
+
+ AddSchemaTableRows();
+
+ Debug.Assert(_schemaTable.Rows.Count == FieldCount);
+ }
+
+ return _schemaTable;
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public string GetString(int i)
+ {
+ return (string)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a .
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ public TimeSpan GetTimeSpan(int i)
+ {
+ return (TimeSpan)GetValue(i);
+ }
+
+ ///
+ /// Gets the value of the specified column as a . (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column.
+ ///
+ ///
+ public abstract object GetValue(int i);
+
+ ///
+ /// Populates an array of objects with the column values of the current record. (Inherited from .)
+ ///
+ ///
+ /// was null.
+ ///
+ ///
+ /// An array of to copy the attribute fields into.
+ ///
+ ///
+ /// The number of instances of in the array.
+ ///
+ ///
+ public int GetValues(object[] values)
+ {
+ if (values == null)
+ {
+ throw new ArgumentNullException("values");
+ }
+
+ int fieldCount = Math.Min(FieldCount, values.Length);
+
+ for (int i = 0; i < fieldCount; i++)
+ {
+ values[i] = GetValue(i);
+ }
+
+ return fieldCount;
+ }
+
+ ///
+ /// Return whether the specified field is set to null. (Inherited from .)
+ ///
+ ///
+ /// The index passed was outside the range of 0 through .
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// True if the specified field is set to null; otherwise, false.
+ ///
+ ///
+ public bool IsDBNull(int i)
+ {
+ object data = GetValue(i);
+
+ return data == null || Convert.IsDBNull(data);
+ }
+
+ ///
+ /// Advances the data reader to the next result, when reading the results of batch SQL statements. (Inherited from .)
+ ///
+ ///
+ /// for returns a single result set so false is always returned.
+ ///
+ ///
+ /// True if there are more rows; otherwise, false. for returns a single result set so false is always returned.
+ ///
+ ///
+ public bool NextResult()
+ {
+ return false;
+ }
+
+ ///
+ /// Advances the to the next record. (Inherited from .)
+ ///
+ ///
+ /// True if there are more rows; otherwise, false.
+ ///
+ ///
+ public abstract bool Read();
+
+ #endregion
+
+ #region IDisposable
+
+ ///
+ /// Has the object been disposed?
+ ///
+ bool _disposed = false;
+
+ ///
+ /// Dispose of any disposable and expensive resources.
+ ///
+ ///
+ /// Is this call the result of a call?
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this._disposed)
+ {
+ this._disposed = true;
+
+ if (disposing)
+ {
+ if (_schemaTable != null)
+ {
+ _schemaTable.Dispose();
+ this._schemaTable = null;
+ }
+
+ this._columnMappings = null;
+
+ this._isOpen = false;
+
+ GC.SuppressFinalize(this);
+ }
+ }
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. (Inherited from .)
+ ///
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ///
+ /// has no unmanaged resources but a subclass may thus a finalizer is required.
+ ///
+ ~BulkDataReader()
+ {
+ Dispose(false);
+ }
+
+ #endregion
+
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs
index 9b08279716..e3c35e01b4 100644
--- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
{
internal static class DefinitionFactory
{
- public static TableDefinition GetTableDefinition(Type modelType)
+ public static TableDefinition GetTableDefinition(ISqlSyntaxProvider syntaxProvider, Type modelType)
{
//Looks for PetaPoco's TableNameAtribute for the name of the table
//If no attribute is set we use the name of the Type as the default convention
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
//Otherwise use the name of the property itself as the default convention
var columnAttribute = propertyInfo.FirstAttribute();
string columnName = columnAttribute != null ? columnAttribute.Name : propertyInfo.Name;
- var columnDefinition = GetColumnDefinition(modelType, propertyInfo, columnName, tableName);
+ var columnDefinition = GetColumnDefinition(syntaxProvider, modelType, propertyInfo, columnName, tableName);
tableDefinition.Columns.Add(columnDefinition);
//Creates a foreignkey definition and adds it to the collection on the table definition
@@ -58,7 +58,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
return tableDefinition;
}
- public static ColumnDefinition GetColumnDefinition(Type modelType, PropertyInfo propertyInfo, string columnName, string tableName)
+ public static ColumnDefinition GetColumnDefinition(ISqlSyntaxProvider syntaxProvider, Type modelType, PropertyInfo propertyInfo, string columnName, string tableName)
{
var definition = new ColumnDefinition{ Name = columnName, TableName = tableName, ModificationType = ModificationType.Create };
@@ -110,7 +110,7 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
{
//Special case for MySQL as it can't have multiple default DateTime values, which
//is what the umbracoServer table definition is trying to create
- if (SqlSyntaxContext.SqlSyntaxProvider is MySqlSyntaxProvider && definition.TableName == "umbracoServer" &&
+ if (syntaxProvider is MySqlSyntaxProvider && definition.TableName == "umbracoServer" &&
definition.TableName.ToLowerInvariant() == "lastNotifiedDate".ToLowerInvariant())
return definition;
diff --git a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
index bcc1528d3f..944d5a8a74 100644
--- a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
@@ -99,7 +99,7 @@ namespace Umbraco.Core.Persistence
public void CreateTable(bool overwrite, Type modelType)
{
- var tableDefinition = DefinitionFactory.GetTableDefinition(modelType);
+ var tableDefinition = DefinitionFactory.GetTableDefinition(_syntaxProvider, modelType);
var tableName = tableDefinition.Name;
string createSql = _syntaxProvider.Format(tableDefinition);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
index 423c847c47..ab477953b4 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
@@ -159,7 +159,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
foreach (var item in OrderedTables.OrderBy(x => x.Key))
{
- var tableDefinition = DefinitionFactory.GetTableDefinition(item.Value);
+ var tableDefinition = DefinitionFactory.GetTableDefinition(_sqlSyntaxProvider, item.Value);
result.TableDefinitions.Add(tableDefinition);
}
diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
index 610b3d9b02..d1667456e6 100644
--- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
+++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
@@ -3,9 +3,13 @@ using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
+using System.Data.SqlServerCe;
using System.Linq;
using System.Text.RegularExpressions;
+using MySql.Data.MySqlClient;
+using StackExchange.Profiling.Data;
using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -150,7 +154,7 @@ namespace Umbraco.Core.Persistence
{
//this fancy regex will only match a single @ not a double, etc...
var regex = new Regex("(?(overwrite);
}
+ ///
+ /// Performs the bulk insertion in the context of a current transaction with an optional parameter to complete the transaction
+ /// when finished
+ ///
+ ///
+ ///
+ ///
public static void BulkInsertRecords(this Database db, IEnumerable collection)
{
//don't do anything if there are no records.
@@ -180,10 +191,97 @@ namespace Umbraco.Core.Persistence
using (var tr = db.GetTransaction())
{
- db.BulkInsertRecords(collection, tr, true);
+ db.BulkInsertRecords(collection, tr, SqlSyntaxContext.SqlSyntaxProvider, true);
}
}
+ ///
+ /// Performs the bulk insertion in the context of a current transaction with an optional parameter to complete the transaction
+ /// when finished
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// If this is false this will try to just generate bulk insert statements instead of using the current SQL platform's bulk
+ /// insert logic. For SQLCE, bulk insert statements do not work so if this is false it will insert one at a time.
+ ///
+ ///
+ /// The number of items inserted
+ public static int BulkInsertRecords(this Database db,
+ IEnumerable collection,
+ Transaction tr,
+ ISqlSyntaxProvider syntaxProvider,
+ bool useNativeSqlPlatformBulkInsert = true,
+ bool commitTrans = false)
+ {
+
+ //don't do anything if there are no records.
+ if (collection.Any() == false)
+ {
+ return 0;
+ }
+
+ var pd = Database.PocoData.ForType(typeof(T));
+ if (pd == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T));
+
+ try
+ {
+ int processed = 0;
+
+ var usedNativeSqlPlatformInserts = useNativeSqlPlatformBulkInsert
+ && NativeSqlPlatformBulkInsertRecords(db, syntaxProvider, pd, collection, out processed);
+
+ if (usedNativeSqlPlatformInserts == false)
+ {
+ //if it is sql ce or it is a sql server version less than 2008, we need to do individual inserts.
+ var sqlServerSyntax = syntaxProvider as SqlServerSyntaxProvider;
+ if ((sqlServerSyntax != null && (int) sqlServerSyntax.GetVersionName(db) < (int) SqlServerVersionName.V2008)
+ || syntaxProvider is SqlCeSyntaxProvider)
+ {
+ //SqlCe doesn't support bulk insert statements!
+ foreach (var poco in collection)
+ {
+ db.Insert(poco);
+ }
+ }
+ else
+ {
+ //we'll need to generate insert statements instead
+
+ string[] sqlStatements;
+ var cmds = db.GenerateBulkInsertCommand(pd, collection, out sqlStatements);
+ for (var i = 0; i < sqlStatements.Length; i++)
+ {
+ using (var cmd = cmds[i])
+ {
+ cmd.CommandText = sqlStatements[i];
+ cmd.ExecuteNonQuery();
+ processed++;
+ }
+ }
+ }
+ }
+
+ if (commitTrans)
+ {
+ tr.Complete();
+ }
+ return processed;
+ }
+ catch
+ {
+ if (commitTrans)
+ {
+ tr.Dispose();
+ }
+ throw;
+ }
+
+ }
+
///
/// Performs the bulk insertion in the context of a current transaction with an optional parameter to complete the transaction
/// when finished
@@ -193,64 +291,10 @@ namespace Umbraco.Core.Persistence
///
///
///
+ [Obsolete("Use the method that specifies an SqlSyntaxContext instance instead")]
public static void BulkInsertRecords(this Database db, IEnumerable collection, Transaction tr, bool commitTrans = false)
{
- //TODO: We should change this to use BulkCopy, as an example see:
- // https://ayende.com/blog/4137/nhibernate-perf-tricks
- // Even though this just generates lots of raw sql INSERT statements BulkCopy is the fastest it can possibly be
- // and we should be able to do this using the current connection from the PetaPoco db instance (and would probably be much cleaner)
- //
- // BulkCopy is available for SQL Server and MySqlBulkLoader is available for MySql, pretty sure BulkCopy works for SQLCE so
- // we should be covered and of course could fallback to this method if that is not our database. But we would get huge perf
- // increases for this.
-
-
- //don't do anything if there are no records.
- if (collection.Any() == false)
- return;
-
- try
- {
- //if it is sql ce or it is a sql server version less than 2008, we need to do individual inserts.
- var sqlServerSyntax = SqlSyntaxContext.SqlSyntaxProvider as SqlServerSyntaxProvider;
-
- if ((sqlServerSyntax != null && (int)sqlServerSyntax.GetVersionName(db) < (int)SqlServerVersionName.V2008)
- || SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
- {
- //SqlCe doesn't support bulk insert statements!
-
- foreach (var poco in collection)
- {
- db.Insert(poco);
- }
- }
- else
- {
- string[] sqlStatements;
- var cmds = db.GenerateBulkInsertCommand(collection, db.Connection, out sqlStatements);
- for (var i = 0; i < sqlStatements.Length; i++)
- {
- using (var cmd = cmds[i])
- {
- cmd.CommandText = sqlStatements[i];
- cmd.ExecuteNonQuery();
- }
- }
- }
-
- if (commitTrans)
- {
- tr.Complete();
- }
- }
- catch
- {
- if (commitTrans)
- {
- tr.Dispose();
- }
- throw;
- }
+ db.BulkInsertRecords(collection, tr, SqlSyntaxContext.SqlSyntaxProvider, commitTrans);
}
///
@@ -259,8 +303,8 @@ namespace Umbraco.Core.Persistence
///
///
///
- ///
///
+ ///
/// Sql commands with populated command parameters required to execute the sql statement
///
/// The limits for number of parameters are 2100 (in sql server, I think there's many more allowed in mysql). So
@@ -269,33 +313,24 @@ namespace Umbraco.Core.Persistence
/// that is max. I've reduced it to 2000 anyways.
///
internal static IDbCommand[] GenerateBulkInsertCommand(
- this Database db,
- IEnumerable collection,
- IDbConnection connection,
+ this Database db,
+ Database.PocoData pd,
+ IEnumerable collection,
out string[] sql)
{
- //A filter used below a few times to get all columns except result cols and not the primary key if it is auto-incremental
- Func, bool> includeColumn = (data, column) =>
- {
- if (column.Value.ResultColumn) return false;
- if (data.TableInfo.AutoIncrement && column.Key == data.TableInfo.PrimaryKey) return false;
- return true;
- };
-
- var pd = Database.PocoData.ForType(typeof(T));
var tableName = db.EscapeTableName(pd.TableInfo.TableName);
//get all columns to include and format for sql
- var cols = string.Join(", ",
+ var cols = string.Join(", ",
pd.Columns
- .Where(c => includeColumn(pd, c))
+ .Where(c => IncludeColumn(pd, c))
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
var itemArray = collection.ToArray();
//calculate number of parameters per item
- var paramsPerItem = pd.Columns.Count(i => includeColumn(pd, i));
-
+ var paramsPerItem = pd.Columns.Count(i => IncludeColumn(pd, i));
+
//Example calc:
// Given: we have 4168 items in the itemArray, each item contains 8 command parameters (values to be inserterted)
// 2100 / 8 = 262.5
@@ -316,14 +351,14 @@ namespace Umbraco.Core.Persistence
.Skip(tIndex * (int)itemsPerTrans)
.Take((int)itemsPerTrans);
- var cmd = db.CreateCommand(connection, "");
+ var cmd = db.CreateCommand(db.Connection, string.Empty);
var pocoValues = new List();
var index = 0;
foreach (var poco in itemsForTrans)
{
var values = new List();
//get all columns except result cols and not the primary key if it is auto-incremental
- foreach (var i in pd.Columns.Where(x => includeColumn(pd, x)))
+ foreach (var i in pd.Columns.Where(x => IncludeColumn(pd, x)))
{
db.AddParam(cmd, i.Value.GetValue(poco), "@");
values.Add(string.Format("{0}{1}", "@", index++));
@@ -331,14 +366,211 @@ namespace Umbraco.Core.Persistence
pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")");
}
- var sqlResult = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
+ var sqlResult = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
sqlQueries.Add(sqlResult);
commands.Add(cmd);
}
sql = sqlQueries.ToArray();
- return commands.ToArray();
+ return commands.ToArray();
+ }
+
+ ///
+ /// A filter used below a few times to get all columns except result cols and not the primary key if it is auto-incremental
+ ///
+ ///
+ ///
+ ///
+ private static bool IncludeColumn(Database.PocoData data, KeyValuePair column)
+ {
+ if (column.Value.ResultColumn) return false;
+ if (data.TableInfo.AutoIncrement && column.Key == data.TableInfo.PrimaryKey) return false;
+ return true;
+ }
+
+ ///
+ /// Bulk insert records with Sql BulkCopy or TableDirect or whatever sql platform specific bulk insert records should be used
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The number of records inserted
+ private static bool NativeSqlPlatformBulkInsertRecords(Database db, ISqlSyntaxProvider syntaxProvider, Database.PocoData pd, IEnumerable collection, out int processed)
+ {
+
+ var dbConnection = db.Connection;
+
+ //unwrap the profiled connection if there is one
+ var profiledConnection = dbConnection as ProfiledDbConnection;
+ if (profiledConnection != null)
+ {
+ dbConnection = profiledConnection.InnerConnection;
+ }
+
+ //check if it's SQL or SqlCe
+
+ var sqlConnection = dbConnection as SqlConnection;
+ if (sqlConnection != null)
+ {
+ processed = BulkInsertRecordsSqlServer(db, (SqlServerSyntaxProvider)syntaxProvider, pd, collection);
+ return true;
+ }
+
+ var sqlCeConnection = dbConnection as SqlCeConnection;
+ if (sqlCeConnection != null)
+ {
+ processed = BulkInsertRecordsSqlCe(db, pd, collection);
+ return true;
+ }
+
+ //could not use the SQL server's specific bulk insert operations
+ processed = 0;
+ return false;
+
+ }
+
+ ///
+ /// Logic used to perform bulk inserts with SqlCe's TableDirect
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static int BulkInsertRecordsSqlCe(Database db,
+ Database.PocoData pd,
+ IEnumerable collection)
+ {
+ var cols = pd.Columns.ToArray();
+
+ using (var cmd = db.CreateCommand(db.Connection, string.Empty))
+ {
+ cmd.CommandText = pd.TableInfo.TableName;
+ cmd.CommandType = CommandType.TableDirect;
+ //cmd.Transaction = GetTypedTransaction(db.Connection.);
+
+ //get the real command
+ using (var sqlCeCommand = GetTypedCommand(cmd))
+ {
+ // This seems to cause problems, I think this is primarily used for retrieval, not
+ // inserting. see: https://msdn.microsoft.com/en-us/library/system.data.sqlserverce.sqlcecommand.indexname%28v=vs.100%29.aspx?f=255&MSPPError=-2147217396
+ //sqlCeCommand.IndexName = pd.TableInfo.PrimaryKey;
+
+ var count = 0;
+ using (var rs = sqlCeCommand.ExecuteResultSet(ResultSetOptions.Updatable))
+ {
+ var rec = rs.CreateRecord();
+
+ foreach (var item in collection)
+ {
+ for (var i = 0; i < cols.Length; i++)
+ {
+ //skip the index if this shouldn't be included (i.e. PK)
+ if (IncludeColumn(pd, cols[i]))
+ {
+ var val = cols[i].Value.GetValue(item);
+ rec.SetValue(i, val);
+ }
+ }
+ rs.Insert(rec);
+ count++;
+ }
+ }
+ return count;
+ }
+
+ }
+ }
+
+ ///
+ /// Logic used to perform bulk inserts with SqlServer's BulkCopy
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static int BulkInsertRecordsSqlServer(Database db, SqlServerSyntaxProvider sqlSyntaxProvider,
+ Database.PocoData pd, IEnumerable collection)
+ {
+ //NOTE: We need to use the original db.Connection here to create the command, but we need to pass in the typed
+ // connection below to the SqlBulkCopy
+ using (var cmd = db.CreateCommand(db.Connection, string.Empty))
+ {
+ using (var copy = new SqlBulkCopy(
+ GetTypedConnection(db.Connection),
+ SqlBulkCopyOptions.Default,
+ GetTypedTransaction(cmd.Transaction))
+ {
+ BulkCopyTimeout = 10000,
+ DestinationTableName = pd.TableInfo.TableName
+ })
+ {
+ //var cols = pd.Columns.Where(x => IncludeColumn(pd, x)).Select(x => x.Value).ToArray();
+
+ using (var bulkReader = new PocoDataDataReader(collection, pd, sqlSyntaxProvider))
+ {
+ copy.WriteToServer(bulkReader);
+
+ return bulkReader.RecordsAffected;
+ }
+ }
+ }
+ }
+
+
+ ///
+ /// Returns the underlying connection as a typed connection - this is used to unwrap the profiled mini profiler stuff
+ ///
+ ///
+ ///
+ ///
+ private static TConnection GetTypedConnection(IDbConnection connection)
+ where TConnection : class, IDbConnection
+ {
+ var profiled = connection as ProfiledDbConnection;
+ if (profiled != null)
+ {
+ return profiled.InnerConnection as TConnection;
+ }
+ return connection as TConnection;
+ }
+
+ ///
+ /// Returns the underlying connection as a typed connection - this is used to unwrap the profiled mini profiler stuff
+ ///
+ ///
+ ///
+ ///
+ private static TTransaction GetTypedTransaction(IDbTransaction connection)
+ where TTransaction : class, IDbTransaction
+ {
+ var profiled = connection as ProfiledDbTransaction;
+ if (profiled != null)
+ {
+ return profiled.WrappedTransaction as TTransaction;
+ }
+ return connection as TTransaction;
+ }
+
+ ///
+ /// Returns the underlying connection as a typed connection - this is used to unwrap the profiled mini profiler stuff
+ ///
+ ///
+ ///
+ ///
+ private static TCommand GetTypedCommand(IDbCommand command)
+ where TCommand : class, IDbCommand
+ {
+ var profiled = command as ProfiledDbCommand;
+ if (profiled != null)
+ {
+ return profiled.InternalCommand as TCommand;
+ }
+ return command as TCommand;
}
[Obsolete("Use the DatabaseSchemaHelper instead")]
@@ -415,8 +647,8 @@ namespace Umbraco.Core.Persistence
return ApplicationContext.Current.DatabaseContext.DatabaseProvider;
}
-
+
}
-
+
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/PocoDataDataReader.cs b/src/Umbraco.Core/Persistence/PocoDataDataReader.cs
new file mode 100644
index 0000000000..b0479a311a
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/PocoDataDataReader.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence
+{
+ ///
+ /// A data reader used for reading collections of PocoData entity types
+ ///
+ ///
+ /// We are using a custom data reader so that tons of memory is not consumed when rebuilding this table, previously
+ /// we'd generate SQL insert statements, but we'd have to put all of the XML structures into memory first. Alternatively
+ /// we can use .net's DataTable, but this also requires putting everything into memory. By using a DataReader we don't have to
+ /// store every content item and it's XML structure in memory to get it into the DB, we can stream it into the db with this
+ /// reader.
+ ///
+ internal class PocoDataDataReader : BulkDataReader
+ where TSyntax : ISqlSyntaxProvider
+ {
+ private readonly MicrosoftSqlSyntaxProviderBase _sqlSyntaxProvider;
+ private readonly TableDefinition _tableDefinition;
+ private readonly Database.PocoColumn[] _readerColumns;
+ private readonly IEnumerator _enumerator;
+ private readonly ColumnDefinition[] _columnDefinitions;
+ private int _recordsAffected = -1;
+
+ public PocoDataDataReader(
+ IEnumerable dataSource,
+ Database.PocoData pd,
+ MicrosoftSqlSyntaxProviderBase sqlSyntaxProvider)
+ {
+ if (dataSource == null) throw new ArgumentNullException("dataSource");
+ if (sqlSyntaxProvider == null) throw new ArgumentNullException("sqlSyntaxProvider");
+
+ _tableDefinition = DefinitionFactory.GetTableDefinition(sqlSyntaxProvider, pd.type);
+ if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.type);
+
+ _readerColumns = pd.Columns.Select(x => x.Value).ToArray();
+ _sqlSyntaxProvider = sqlSyntaxProvider;
+ _enumerator = dataSource.GetEnumerator();
+ _columnDefinitions = _tableDefinition.Columns.ToArray();
+
+ }
+
+ protected override string SchemaName
+ {
+ get { return _tableDefinition.SchemaName; }
+ }
+
+ protected override string TableName
+ {
+ get { return _tableDefinition.Name; }
+ }
+
+ public override int RecordsAffected
+ {
+ get { return _recordsAffected <= 0 ? -1 : _recordsAffected; }
+ }
+
+ ///
+ /// This will automatically add the schema rows based on the Poco table definition and the columns passed in
+ ///
+ protected override void AddSchemaTableRows()
+ {
+ //var colNames = _readerColumns.Select(x => x.ColumnName).ToArray();
+ //foreach (var col in _columnDefinitions.Where(x => colNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
+ foreach (var col in _columnDefinitions)
+ {
+ var sqlDbType = SqlDbType.NVarChar;
+ if (col.HasSpecialDbType)
+ {
+ //get the SqlDbType from the 'special type'
+ switch (col.DbType)
+ {
+ case SpecialDbTypes.NTEXT:
+ sqlDbType = SqlDbType.NText;
+ break;
+ case SpecialDbTypes.NCHAR:
+ sqlDbType = SqlDbType.NChar;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ else if (col.Type.HasValue)
+ {
+ //get the SqlDbType from the DbType
+ sqlDbType = _sqlSyntaxProvider.GetSqlDbType(col.Type.Value);
+ }
+ else
+ {
+ //get the SqlDbType from the clr type
+ sqlDbType = _sqlSyntaxProvider.GetSqlDbType(col.PropertyType);
+ }
+
+ AddSchemaTableRow(
+ col.Name,
+ col.Size > 0 ? (int?)col.Size : null,
+ col.Precision > 0 ? (short?)col.Precision : null,
+ null, col.IsUnique, col.IsIdentity, col.IsNullable, sqlDbType,
+ null, null, null, null, null);
+ }
+
+ }
+
+ ///
+ /// Get the value from the column index for the current object
+ ///
+ ///
+ ///
+ public override object GetValue(int i)
+ {
+ if (_enumerator.Current != null)
+ {
+ return _readerColumns[i].GetValue(_enumerator.Current);
+ //return _columnDefinitions[i]. .GetValue(_enumerator.Current);
+ }
+
+ return null;
+ //TODO: Or throw ?
+ }
+
+ ///
+ /// Advance the cursor
+ ///
+ ///
+ public override bool Read()
+ {
+ var result = _enumerator.MoveNext();
+ if (result)
+ {
+ if (_recordsAffected == -1)
+ {
+ _recordsAffected = 0;
+ }
+ _recordsAffected++;
+ }
+ return result;
+ }
+
+ ///
+ /// Ensure the enumerator is disposed
+ ///
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ if (disposing)
+ {
+ _enumerator.Dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 9222769247..9827382b00 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -248,12 +248,12 @@ namespace Umbraco.Core.Persistence.Repositories
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
- select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
+ select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() });
//bulk insert it into the database
- Database.BulkInsertRecords(xmlItems, tr);
+ var count = Database.BulkInsertRecords(xmlItems, tr, SqlSyntax);
- processed += xmlItems.Length;
+ processed += count;
pageIndex++;
} while (processed < total);
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/DbTypes.cs b/src/Umbraco.Core/Persistence/SqlSyntax/DbTypes.cs
index 507db230cc..c9ef4d1f35 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/DbTypes.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/DbTypes.cs
@@ -4,6 +4,7 @@ using System.Data;
namespace Umbraco.Core.Persistence.SqlSyntax
{
+ //TODO: TSyntax should be removed, it's not used/needed here
public class DbTypes
where TSyntax : ISqlSyntaxProvider
{
diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MicrosoftSqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MicrosoftSqlSyntaxProviderBase.cs
index 449f5fb3b1..1ea600b6e4 100644
--- a/src/Umbraco.Core/Persistence/SqlSyntax/MicrosoftSqlSyntaxProviderBase.cs
+++ b/src/Umbraco.Core/Persistence/SqlSyntax/MicrosoftSqlSyntaxProviderBase.cs
@@ -1,4 +1,6 @@
using System;
+using System.Data;
+using System.Linq;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
@@ -133,5 +135,111 @@ namespace Umbraco.Core.Persistence.SqlSyntax
throw new ArgumentOutOfRangeException("columnType");
}
}
+
+ ///
+ /// This uses a the DbTypeMap created and custom mapping to resolve the SqlDbType
+ ///
+ ///
+ ///
+ public virtual SqlDbType GetSqlDbType(Type clrType)
+ {
+ var dbType = DbTypeMap.ColumnDbTypeMap.First(x => x.Key == clrType).Value;
+ return GetSqlDbType(dbType);
+ }
+
+ ///
+ /// Returns the mapped SqlDbType for the DbType specified
+ ///
+ ///
+ ///
+ public virtual SqlDbType GetSqlDbType(DbType dbType)
+ {
+ var sqlDbType = SqlDbType.NVarChar;
+
+ //SEE: https://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
+ // and https://msdn.microsoft.com/en-us/library/yy6y35y8%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
+ switch (dbType)
+ {
+ case DbType.AnsiString:
+ sqlDbType = SqlDbType.VarChar;
+ break;
+ case DbType.Binary:
+ sqlDbType = SqlDbType.VarBinary;
+ break;
+ case DbType.Byte:
+ sqlDbType = SqlDbType.TinyInt;
+ break;
+ case DbType.Boolean:
+ sqlDbType = SqlDbType.Bit;
+ break;
+ case DbType.Currency:
+ sqlDbType = SqlDbType.Money;
+ break;
+ case DbType.Date:
+ sqlDbType = SqlDbType.Date;
+ break;
+ case DbType.DateTime:
+ sqlDbType = SqlDbType.DateTime;
+ break;
+ case DbType.Decimal:
+ sqlDbType = SqlDbType.Decimal;
+ break;
+ case DbType.Double:
+ sqlDbType = SqlDbType.Float;
+ break;
+ case DbType.Guid:
+ sqlDbType = SqlDbType.UniqueIdentifier;
+ break;
+ case DbType.Int16:
+ sqlDbType = SqlDbType.SmallInt;
+ break;
+ case DbType.Int32:
+ sqlDbType = SqlDbType.Int;
+ break;
+ case DbType.Int64:
+ sqlDbType = SqlDbType.BigInt;
+ break;
+ case DbType.Object:
+ sqlDbType = SqlDbType.Variant;
+ break;
+ case DbType.SByte:
+ throw new NotSupportedException("Inferring a SqlDbType from SByte is not supported.");
+ case DbType.Single:
+ sqlDbType = SqlDbType.Real;
+ break;
+ case DbType.String:
+ sqlDbType = SqlDbType.NVarChar;
+ break;
+ case DbType.Time:
+ sqlDbType = SqlDbType.Time;
+ break;
+ case DbType.UInt16:
+ throw new NotSupportedException("Inferring a SqlDbType from UInt16 is not supported.");
+ case DbType.UInt32:
+ throw new NotSupportedException("Inferring a SqlDbType from UInt32 is not supported.");
+ case DbType.UInt64:
+ throw new NotSupportedException("Inferring a SqlDbType from UInt64 is not supported.");
+ case DbType.VarNumeric:
+ throw new NotSupportedException("Inferring a VarNumeric from UInt64 is not supported.");
+ case DbType.AnsiStringFixedLength:
+ sqlDbType = SqlDbType.Char;
+ break;
+ case DbType.StringFixedLength:
+ sqlDbType = SqlDbType.NChar;
+ break;
+ case DbType.Xml:
+ sqlDbType = SqlDbType.Xml;
+ break;
+ case DbType.DateTime2:
+ sqlDbType = SqlDbType.DateTime2;
+ break;
+ case DbType.DateTimeOffset:
+ sqlDbType = SqlDbType.DateTimeOffset;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ return sqlDbType;
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index bf70f6afd6..c28cea24cd 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -415,6 +415,7 @@
+
@@ -472,6 +473,7 @@
+
diff --git a/src/Umbraco.Tests/Persistence/BulkDataReaderTests.cs b/src/Umbraco.Tests/Persistence/BulkDataReaderTests.cs
new file mode 100644
index 0000000000..b1e1a79ddb
--- /dev/null
+++ b/src/Umbraco.Tests/Persistence/BulkDataReaderTests.cs
@@ -0,0 +1,2432 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Data;
+using System.Data.Common;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using Umbraco.Core.Persistence;
+
+namespace Umbraco.Tests.Persistence
+{
+ ///
+ /// Unit tests for .
+ ///
+ ///
+ /// Borrowed from Microsoft:
+ /// See: https://blogs.msdn.microsoft.com/anthonybloesch/2013/01/23/bulk-loading-data-with-idatareader-and-sqlbulkcopy/
+ ///
+ [TestFixture]
+ public class BulkDataReaderTest
+ {
+
+ #region Test constants
+
+ ///
+ /// The schema name.
+ ///
+ private const string testSchemaName = "TestSchema";
+
+ ///
+ /// The table name.
+ ///
+ private const string testTableName = "TestTable";
+
+ ///
+ /// The test UDT schema name.
+ ///
+ private const string testUdtSchemaName = "UdtSchema";
+
+ ///
+ /// The test UDT name.
+ ///
+ private const string testUdtName = "TestUdt";
+
+ ///
+ /// The test XML schema collection database name.
+ ///
+ private const string testXmlSchemaCollectionDatabaseName = "XmlDatabase";
+
+ ///
+ /// The test XML schema collection owning schema name.
+ ///
+ private const string testXMLSchemaCollectionSchemaName = "XmlSchema";
+
+ ///
+ /// The test XML schema collection name.
+ ///
+ private const string testXMLSchemaCollectionName = "Xml";
+
+ #endregion
+
+ #region Schema tests
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ [Test]
+ public void ColumnMappingsTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ ReadOnlyCollection columnMappings = testReader.ColumnMappings;
+
+ Assert.IsTrue(columnMappings.Count > 0);
+ Assert.AreEqual(columnMappings.Count, testReader.FieldCount);
+
+ foreach (SqlBulkCopyColumnMapping columnMapping in columnMappings)
+ {
+ Assert.AreEqual(columnMapping.SourceColumn, columnMapping.DestinationColumn);
+ }
+ }
+ }
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ [Test]
+ public void GetDataTypeNameTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.FieldCount > 0);
+
+ for (int currentColumn = 0; currentColumn < testReader.FieldCount; currentColumn++)
+ {
+ Assert.AreEqual(testReader.GetDataTypeName(currentColumn), ((Type)testReader.GetSchemaTable().Rows[currentColumn][SchemaTableColumn.DataType]).Name);
+ }
+ }
+ }
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ [Test]
+ public void GetFieldTypeTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.FieldCount > 0);
+
+ for (int currentColumn = 0; currentColumn < testReader.FieldCount; currentColumn++)
+ {
+ Assert.AreEqual(testReader.GetFieldType(currentColumn), testReader.GetSchemaTable().Rows[currentColumn][SchemaTableColumn.DataType]);
+ }
+ }
+ }
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ [Test]
+ public void GetOrdinalTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.FieldCount > 0);
+
+ for (int currentColumn = 0; currentColumn < testReader.FieldCount; currentColumn++)
+ {
+ Assert.AreEqual(testReader.GetOrdinal(testReader.GetName(currentColumn)), currentColumn);
+
+ Assert.AreEqual(testReader.GetOrdinal(testReader.GetName(currentColumn).ToUpperInvariant()), currentColumn);
+ }
+ }
+ }
+
+ ///
+ /// Test that functions correctly.
+ ///
+ ///
+ /// uses to test legal schema combinations.
+ ///
+ ///
+ [Test]
+ public void GetSchemaTableTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.IsNotNull(schemaTable);
+ Assert.IsTrue(schemaTable.Rows.Count > 0);
+ Assert.AreEqual(schemaTable.Rows.Count, BulkDataReaderSubclass.ExpectedResultSet.Count);
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for null column names.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowNullColumnNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = null;
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.BigInt;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for empty column names.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowEmptyColumnNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = string.Empty;
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.BigInt;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for nonpositive column sizes.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowNonpositiveColumnSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 0;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.NVarChar;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for nonpositive numeric precision.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowNonpositiveNumericPrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 0;
+ testReader.NumericScale = 0;
+ testReader.ProviderType = SqlDbType.Decimal;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for negative numeric scale.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowNegativeNumericScaleTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 5;
+ testReader.NumericScale = -1;
+ testReader.ProviderType = SqlDbType.Decimal;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for binary column without a column size.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowBinaryWithoutSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Binary;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for binary column with a column size that is too large (>8000).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowBinaryWithTooLargeSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 8001;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Binary;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for char column without a column size.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowCharWithoutSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Char;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for char column with a column size that is too large (>8000).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowCharWithTooLargeSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 8001;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Char;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for decimal column without a column precision.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowDecimalWithoutPrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = 5;
+ testReader.ProviderType = SqlDbType.Decimal;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for decimal column without a column scale.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowDecimalWithoutScaleTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 20;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Decimal;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for decimal column with a column precision that is too large (>38).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowDecimalWithTooLargePrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 39;
+ testReader.NumericScale = 5;
+ testReader.ProviderType = SqlDbType.Decimal;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for decimal column with a column scale that is larger than the column precision.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowDecimalWithTooLargeScaleTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 20;
+ testReader.NumericScale = 21;
+ testReader.ProviderType = SqlDbType.Decimal;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for datetime2 column with a column size that has a precision that is too large (>7).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowDateTime2WithTooLargePrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 8;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.DateTime2;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for datetimeoffset column with a column size that has a precision that is too large (>7).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowDateTimeOffsetWithTooLargePrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 8;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.DateTimeOffset;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for nchar column without a precision.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowFloatWithoutPrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Float;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for float column with a column precision that is too large (>53).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowFloatWithTooLargePrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 54;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Float;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for nchar column without a column size.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowNCharWithoutSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.NChar;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for nchar column with a column size that is too large (>4000).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowNCharWithTooLargeSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 4001;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.NChar;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for nvarchar column with a column size that is too large (>4000).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowNVarCharWithTooLargeSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 4001;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.NVarChar;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for time column with a column precision that is too large (>7).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowTimeWithTooLargePrecisionTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 8;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Time;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for missing UDT schema name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowUdtMissingSchemaNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Udt;
+ testReader.UdtSchema = null;
+ testReader.UdtType = "Type";
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for empty UDT schema name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowUdtEmptySchemaNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Udt;
+ testReader.UdtSchema = string.Empty;
+ testReader.UdtType = "Type";
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for missing UDT name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowUdtMissingNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Udt;
+ testReader.UdtSchema = "Schema";
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for empty UDT name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowUdtEmptyNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Udt;
+ testReader.UdtSchema = "Schema";
+ testReader.UdtType = string.Empty;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for varbinary column with a column size that is too large (>8000).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowVarBinaryWithTooLargeSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 8001;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.VarBinary;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for varchar column with a column size that is too large (>8000).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowVarCharWithTooLargeSizeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 8001;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.VarChar;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for null xml collection name but with a name for the database.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowXmlNullNameWithDatabaseNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Xml;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = "Database";
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for null xml collection name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowXmlNullNameWithOwningSchemaNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Xml;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = "Schema";
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for empty xml collection database name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowXmlEmptyDatabaseNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Xml;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = string.Empty;
+ testReader.XmlSchemaCollectionOwningSchema = "Schema";
+ testReader.XmlSchemaCollectionName = "Xml";
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for empty xml collection owning schema name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowXmlEmptyOwningSchemaNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Xml;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = "Database";
+ testReader.XmlSchemaCollectionOwningSchema = string.Empty;
+ testReader.XmlSchemaCollectionName = "Xml";
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for empty xml collection name.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AddSchemaTableRowXmlEmptyNameTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Xml;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = "Database";
+ testReader.XmlSchemaCollectionOwningSchema = "Schema";
+ testReader.XmlSchemaCollectionName = string.Empty;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for a structured column (which is illegal).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowStructuredTypeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Structured;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws a for a timestamp column (which is illegal).
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void AddSchemaTableRowTimestampTypeTest()
+ {
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.ProviderType = SqlDbType.Timestamp;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ DataTable schemaTable = testReader.GetSchemaTable(); ;
+ }
+ }
+
+ ///
+ /// Test that
+ /// throws an for a column with an unallowed optional column set.
+ ///
+ ///
+ /// Uses to test the illegal schema combination.
+ ///
+ ///
+ [Test]
+ public void AddSchemaTableRowUnallowedOptionalColumnTest()
+ {
+
+ // Column size set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = 5;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Bit, SqlDbType.Date, SqlDbType.DateTime, SqlDbType.DateTime2,
+ SqlDbType.DateTimeOffset, SqlDbType.Image, SqlDbType.Int, SqlDbType.Money, SqlDbType.Real,
+ SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney, SqlDbType.Structured, SqlDbType.Text,
+ SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt, SqlDbType.Udt, SqlDbType.UniqueIdentifier,
+ SqlDbType.Variant, SqlDbType.Xml })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // Numeric precision set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 5;
+ testReader.NumericScale = null;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.Image, SqlDbType.Int, SqlDbType.Money, SqlDbType.NChar,
+ SqlDbType.NText, SqlDbType.NVarChar, SqlDbType.Real, SqlDbType.SmallDateTime, SqlDbType.SmallInt,
+ SqlDbType.SmallMoney, SqlDbType.Structured, SqlDbType.Text, SqlDbType.Timestamp, SqlDbType.TinyInt,
+ SqlDbType.Udt, SqlDbType.UniqueIdentifier, SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant,
+ SqlDbType.Xml })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // Numeric scale set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 5;
+ testReader.NumericScale = 3;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.DateTime2, SqlDbType.DateTimeOffset, SqlDbType.Image, SqlDbType.Int,
+ SqlDbType.Money, SqlDbType.NChar, SqlDbType.NText, SqlDbType.NVarChar, SqlDbType.Real,
+ SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney, SqlDbType.Structured, SqlDbType.Text,
+ SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt, SqlDbType.Udt, SqlDbType.UniqueIdentifier,
+ SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant, SqlDbType.Xml })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // Numeric scale set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = 5;
+ testReader.NumericScale = 3;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.DateTime2, SqlDbType.DateTimeOffset, SqlDbType.Image, SqlDbType.Int,
+ SqlDbType.Money, SqlDbType.NChar, SqlDbType.NText, SqlDbType.NVarChar, SqlDbType.Real,
+ SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney, SqlDbType.Structured, SqlDbType.Text,
+ SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt, SqlDbType.Udt, SqlDbType.UniqueIdentifier,
+ SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant, SqlDbType.Xml })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // UDT type name set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.UdtSchema = null;
+ testReader.UdtType = "Type";
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.DateTime2, SqlDbType.DateTimeOffset, SqlDbType.Decimal, SqlDbType.Float,
+ SqlDbType.Image, SqlDbType.Int, SqlDbType.Money, SqlDbType.NChar, SqlDbType.NText,
+ SqlDbType.NVarChar, SqlDbType.Real, SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney,
+ SqlDbType.Structured, SqlDbType.Text, SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt,
+ SqlDbType.UniqueIdentifier, SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant, SqlDbType.Xml })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // UDT schema and type name set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.UdtSchema = "Schema";
+ testReader.UdtType = "Type";
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = null;
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.DateTime2, SqlDbType.DateTimeOffset, SqlDbType.Decimal, SqlDbType.Float,
+ SqlDbType.Image, SqlDbType.Int, SqlDbType.Money, SqlDbType.NChar, SqlDbType.NText,
+ SqlDbType.NVarChar, SqlDbType.Real, SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney,
+ SqlDbType.Structured, SqlDbType.Text, SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt,
+ SqlDbType.UniqueIdentifier, SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant, SqlDbType.Xml })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // XML type name set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = null;
+ testReader.XmlSchemaCollectionName = "Name";
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.DateTime2, SqlDbType.DateTimeOffset, SqlDbType.Decimal, SqlDbType.Float,
+ SqlDbType.Image, SqlDbType.Int, SqlDbType.Money, SqlDbType.NChar, SqlDbType.NText,
+ SqlDbType.NVarChar, SqlDbType.Real, SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney,
+ SqlDbType.Structured, SqlDbType.Text, SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt,
+ SqlDbType.UniqueIdentifier, SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant, SqlDbType.Udt })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // XML owning schema and type name set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = null;
+ testReader.XmlSchemaCollectionOwningSchema = "Schema";
+ testReader.XmlSchemaCollectionName = "Name";
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.DateTime2, SqlDbType.DateTimeOffset, SqlDbType.Decimal, SqlDbType.Float,
+ SqlDbType.Image, SqlDbType.Int, SqlDbType.Money, SqlDbType.NChar, SqlDbType.NText,
+ SqlDbType.NVarChar, SqlDbType.Real, SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney,
+ SqlDbType.Structured, SqlDbType.Text, SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt,
+ SqlDbType.UniqueIdentifier, SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant, SqlDbType.Udt })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+
+ // XML database, owning schema and type name set
+ using (BulkDataReaderSchemaTest testReader = new BulkDataReaderSchemaTest())
+ {
+ testReader.AllowDBNull = false;
+ testReader.ColumnName = "Name";
+ testReader.ColumnSize = null;
+ testReader.IsKey = false;
+ testReader.IsUnique = false;
+ testReader.NumericPrecision = null;
+ testReader.NumericScale = null;
+ testReader.UdtSchema = null;
+ testReader.UdtType = null;
+ testReader.XmlSchemaCollectionDatabase = "Database";
+ testReader.XmlSchemaCollectionOwningSchema = "Schema";
+ testReader.XmlSchemaCollectionName = "Name";
+
+ foreach (SqlDbType dbtype in new List { SqlDbType.BigInt, SqlDbType.Binary, SqlDbType.Bit, SqlDbType.Char, SqlDbType.Date,
+ SqlDbType.DateTime, SqlDbType.DateTime2, SqlDbType.DateTimeOffset, SqlDbType.Decimal, SqlDbType.Float,
+ SqlDbType.Image, SqlDbType.Int, SqlDbType.Money, SqlDbType.NChar, SqlDbType.NText,
+ SqlDbType.NVarChar, SqlDbType.Real, SqlDbType.SmallDateTime, SqlDbType.SmallInt, SqlDbType.SmallMoney,
+ SqlDbType.Structured, SqlDbType.Text, SqlDbType.Time, SqlDbType.Timestamp, SqlDbType.TinyInt,
+ SqlDbType.UniqueIdentifier, SqlDbType.VarBinary, SqlDbType.VarChar, SqlDbType.Variant, SqlDbType.Udt })
+ {
+ testReader.ProviderType = dbtype;
+
+ try
+ {
+ DataTable schemaTable = testReader.GetSchemaTable();
+
+ Assert.Fail();
+ }
+ catch (ArgumentException)
+ {
+ }
+ }
+ }
+ }
+
+ #endregion;
+
+ #region Rowset tests
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ [Test]
+ public void CloseTest()
+ {
+ BulkDataReaderSubclass testReader = new BulkDataReaderSubclass();
+
+ testReader.Close();
+
+ Assert.IsTrue(testReader.IsClosed);
+ }
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ /// Because nested row sets are not supported, this should always return 0;
+ ///
+ ///
+ [Test]
+ public void DepthTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ Assert.AreEqual(testReader.Depth, 0);
+ }
+ }
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ /// Because nested row sets are not supported, this should always return null;
+ ///
+ ///
+ [Test]
+ public void GetDataTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ Assert.IsTrue(testReader.FieldCount > 0);
+
+ Assert.IsNull(testReader.GetData(0));
+ }
+ }
+
+ ///
+ /// Test and related functions.
+ ///
+ ///
+ /// Uses to test legal schema combinations.
+ ///
+ [Test]
+ public void GetValueTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ // this[int]
+ for (int column = 0; column < BulkDataReaderSubclass.ExpectedResultSet.Count; column++)
+ {
+ Assert.AreEqual(testReader[column], BulkDataReaderSubclass.ExpectedResultSet[column]);
+ }
+
+ // this[string]
+ for (int column = 0; column < BulkDataReaderSubclass.ExpectedResultSet.Count; column++)
+ {
+ Assert.AreEqual(testReader[testReader.GetName(column)], BulkDataReaderSubclass.ExpectedResultSet[column]);
+
+ Assert.AreEqual(testReader[testReader.GetName(column).ToUpperInvariant()], BulkDataReaderSubclass.ExpectedResultSet[column]);
+ }
+
+ // GetValues
+ {
+ object[] values = new object[BulkDataReaderSubclass.ExpectedResultSet.Count];
+ object[] expectedValues = new object[BulkDataReaderSubclass.ExpectedResultSet.Count];
+
+ Assert.AreEqual(testReader.GetValues(values), values.Length);
+
+ BulkDataReaderSubclass.ExpectedResultSet.CopyTo(expectedValues, 0);
+
+ Assert.IsTrue(BulkDataReaderTest.ArraysMatch(values, expectedValues));
+ }
+
+ // Typed getters
+ {
+ int currentColumn = 0;
+
+ Assert.AreEqual(testReader.GetInt64(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ {
+ byte[] expectedResult = (byte[])BulkDataReaderSubclass.ExpectedResultSet[currentColumn];
+ int expectedLength = expectedResult.Length;
+ byte[] buffer = new byte[expectedLength];
+
+ Assert.AreEqual(testReader.GetBytes(currentColumn, 0, buffer, 0, expectedLength), expectedLength);
+
+ Assert.IsTrue(BulkDataReaderTest.ArraysMatch(buffer, expectedResult));
+ }
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetBoolean(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.IsDBNull(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn] == null);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetChar(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetChar(currentColumn), ((char[])BulkDataReaderSubclass.ExpectedResultSet[currentColumn])[0]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetChar(currentColumn), ((string)BulkDataReaderSubclass.ExpectedResultSet[currentColumn])[0]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+
+ {
+ char[] expectedResult = ((string)BulkDataReaderSubclass.ExpectedResultSet[currentColumn]).ToCharArray();
+ int expectedLength = expectedResult.Length;
+ char[] buffer = new char[expectedLength];
+
+ Assert.AreEqual(testReader.GetChars(currentColumn, 0, buffer, 0, expectedLength), expectedLength);
+
+ Assert.IsTrue(BulkDataReaderTest.ArraysMatch(buffer, expectedResult));
+ }
+
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDateTime(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDateTime(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDateTime(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDateTime(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDateTimeOffset(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDateTimeOffset(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDecimal(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDouble(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ {
+ byte[] expectedResult = (byte[])BulkDataReaderSubclass.ExpectedResultSet[currentColumn];
+ int expectedLength = expectedResult.Length;
+ byte[] buffer = new byte[expectedLength];
+
+ Assert.AreEqual(testReader.GetBytes(currentColumn, 0, buffer, 0, expectedLength), expectedLength);
+
+ Assert.IsTrue(BulkDataReaderTest.ArraysMatch(buffer, expectedResult));
+ }
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetInt32(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDecimal(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetFloat(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDateTime(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetInt16(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetDecimal(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetTimeSpan(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetTimeSpan(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetByte(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetValue(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetGuid(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ {
+ byte[] expectedResult = (byte[])BulkDataReaderSubclass.ExpectedResultSet[currentColumn];
+ int expectedLength = expectedResult.Length;
+ byte[] buffer = new byte[expectedLength];
+
+ Assert.AreEqual(testReader.GetBytes(currentColumn, 0, buffer, 0, expectedLength), expectedLength);
+
+ Assert.IsTrue(BulkDataReaderTest.ArraysMatch(buffer, expectedResult));
+ }
+ currentColumn++;
+
+ {
+ byte[] expectedResult = (byte[])BulkDataReaderSubclass.ExpectedResultSet[currentColumn];
+ int expectedLength = expectedResult.Length;
+ byte[] buffer = new byte[expectedLength];
+
+ Assert.AreEqual(testReader.GetBytes(currentColumn, 0, buffer, 0, expectedLength), expectedLength);
+
+ Assert.IsTrue(BulkDataReaderTest.ArraysMatch(buffer, expectedResult));
+ }
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetValue(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+
+ Assert.AreEqual(testReader.GetString(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn]);
+ currentColumn++;
+ }
+ }
+ }
+
+ ///
+ /// Test throws a when
+ /// the index is too small.
+ ///
+ ///
+ /// Uses to test the method.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void GetValueIndexTooSmallTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ object result = testReader.GetValue(-1);
+ }
+ }
+
+ ///
+ /// Test throws a when
+ /// the index is too large.
+ ///
+ ///
+ /// Uses to test the method.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void GetValueIndexTooLargeTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ object result = testReader.GetValue(testReader.FieldCount);
+ }
+ }
+
+ ///
+ /// Test throws a when
+ /// the index is too small.
+ ///
+ ///
+ /// Uses to test the method.
+ ///
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void GetDataIndexTooSmallTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ object result = testReader.GetData(-1);
+ }
+ }
+
+ ///
+ /// Test throws a when
+ /// the index is too large.
+ ///
+ ///
+ /// Uses to test the method.
+ ///
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void GetDataIndexTooLargeTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ object result = testReader.GetData(testReader.FieldCount);
+ }
+ }
+
+ ///
+ /// Test that functions correctly.
+ ///
+ ///
+ [Test]
+ public void IsDBNullTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ for (int currentColumn = 0; currentColumn < testReader.FieldCount; currentColumn++)
+ {
+ Assert.AreEqual(testReader.IsDBNull(currentColumn), BulkDataReaderSubclass.ExpectedResultSet[currentColumn] == null);
+ }
+ }
+ }
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ /// Because this is a single row set, this should always return false;
+ ///
+ ///
+ [Test]
+ public void NextResultTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsFalse(testReader.NextResult());
+ }
+ }
+
+ ///
+ /// Test that is functioning correctly.
+ ///
+ ///
+ /// Because this row set represents a data source, this should always return -1;
+ ///
+ ///
+ [Test]
+ public void RecordsAffectedTest()
+ {
+ using (BulkDataReaderSubclass testReader = new BulkDataReaderSubclass())
+ {
+ Assert.IsTrue(testReader.Read());
+
+ Assert.AreEqual(testReader.RecordsAffected, -1);
+ }
+ }
+
+ #endregion
+
+ #region Test IDisposable
+
+ ///
+ /// Test that the interface is functioning correctly.
+ ///
+ ///
+ ///
+ [Test]
+ public void IDisposableTest()
+ {
+ // Test the Dispose method
+ {
+ BulkDataReaderSubclass testReader = new BulkDataReaderSubclass();
+
+ testReader.Dispose();
+
+ Assert.IsTrue(testReader.IsClosed);
+ }
+
+ // Test the finalizer method
+ {
+ BulkDataReaderSubclass testReader = new BulkDataReaderSubclass();
+
+ testReader = null;
+
+ GC.Collect();
+
+ GC.WaitForPendingFinalizers();
+ }
+ }
+
+ #endregion
+
+ #region Utility
+
+ ///
+ /// Do the two arrays match exactly?
+ ///
+ ///
+ /// The type of the array elements.
+ ///
+ ///
+ /// The first array.
+ ///
+ ///
+ /// The second array.
+ ///
+ ///
+ /// True if the arrays have the same length and contents.
+ ///
+ private static bool ArraysMatch(ElementType[] left,
+ ElementType[] right)
+ {
+ if (left == null)
+ {
+ throw new ArgumentNullException("left");
+ }
+ else if (right == null)
+ {
+ throw new ArgumentNullException("left");
+ }
+
+ bool result = true;
+
+ if (left.Length != right.Length)
+ {
+ result = false;
+ }
+ else
+ {
+ for (int currentIndex = 0; currentIndex < left.Length; currentIndex++)
+ {
+ result &= object.Equals(left[currentIndex], right[currentIndex]);
+ }
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region Test stubs
+
+ ///
+ /// A subclass of used for testing its utility functions.
+ ///
+ private class BulkDataReaderSubclass : BulkDataReader
+ {
+
+ #region Constructors
+
+ ///
+ /// Constructor.
+ ///
+ public BulkDataReaderSubclass()
+ {
+ }
+
+ #endregion
+
+ #region BulkDataReader
+
+ ///
+ /// See .
+ ///
+ ///
+ /// Returns .
+ ///
+ protected override string SchemaName
+ {
+ get { return BulkDataReaderTest.testSchemaName; }
+ }
+
+ ///
+ /// See .
+ ///
+ ///
+ /// Returns .
+ ///
+ protected override string TableName
+ {
+ get { return BulkDataReaderTest.testTableName; }
+ }
+
+ ///
+ /// See
+ ///
+ ///
+ /// Creates a schema row for the various values.
+ ///
+ protected override void AddSchemaTableRows()
+ {
+ AddSchemaTableRow("BigInt", null, null, null, true, false, false, SqlDbType.BigInt, null, null, null, null, null);
+ AddSchemaTableRow("Binary_20", 20, null, null, false, true, false, SqlDbType.Binary, null, null, null, null, null);
+ AddSchemaTableRow("Bit", null, null, null, false, false, true, SqlDbType.Bit, null, null, null, null, null);
+ AddSchemaTableRow("Bit_null", null, null, null, false, false, true, SqlDbType.Bit, null, null, null, null, null);
+ AddSchemaTableRow("Char_Char", 1, null, null, false, false, false, SqlDbType.Char, null, null, null, null, null);
+ AddSchemaTableRow("Char_Char_Array", 1, null, null, false, false, false, SqlDbType.Char, null, null, null, null, null);
+ AddSchemaTableRow("Char_String", 1, null, null, false, false, false, SqlDbType.Char, null, null, null, null, null);
+ AddSchemaTableRow("Char_20_String", 20, null, null, false, false, false, SqlDbType.Char, null, null, null, null, null);
+ AddSchemaTableRow("Date", null, null, null, false, false, false, SqlDbType.Date, null, null, null, null, null);
+ AddSchemaTableRow("DateTime", null, null, null, false, false, false, SqlDbType.DateTime, null, null, null, null, null);
+ AddSchemaTableRow("DateTime2", null, null, null, false, false, false, SqlDbType.DateTime2, null, null, null, null, null);
+ AddSchemaTableRow("DateTime2_5", null, 5, null, false, false, false, SqlDbType.DateTime2, null, null, null, null, null);
+ AddSchemaTableRow("DateTimeOffset", null, null, null, false, false, false, SqlDbType.DateTimeOffset, null, null, null, null, null);
+ AddSchemaTableRow("DateTimeOffset_5", null, 5, null, false, false, false, SqlDbType.DateTimeOffset, null, null, null, null, null);
+ AddSchemaTableRow("Decimal_20_10", null, 20, 10, false, false, false, SqlDbType.Decimal, null, null, null, null, null);
+ AddSchemaTableRow("Float_50", null, 50, null, false, false, false, SqlDbType.Float, null, null, null, null, null);
+ AddSchemaTableRow("Image", null, null, null, false, false, false, SqlDbType.Image, null, null, null, null, null);
+ AddSchemaTableRow("Int", null, null, null, false, false, false, SqlDbType.Int, null, null, null, null, null);
+ AddSchemaTableRow("Money", null, null, null, false, false, false, SqlDbType.Money, null, null, null, null, null);
+ AddSchemaTableRow("NChar_20", 20, null, null, false, false, false, SqlDbType.NChar, null, null, null, null, null);
+ AddSchemaTableRow("NText", null, null, null, false, false, false, SqlDbType.NText, null, null, null, null, null);
+ AddSchemaTableRow("NVarChar_20", 20, null, null, false, false, false, SqlDbType.NVarChar, null, null, null, null, null);
+ AddSchemaTableRow("NVarChar_Max", null, null, null, false, false, false, SqlDbType.NVarChar, null, null, null, null, null);
+ AddSchemaTableRow("Real", null, null, null, false, false, false, SqlDbType.Real, null, null, null, null, null);
+ AddSchemaTableRow("SmallDateTime", null, null, null, false, false, false, SqlDbType.SmallDateTime, null, null, null, null, null);
+ AddSchemaTableRow("SmallInt", null, null, null, false, false, false, SqlDbType.SmallInt, null, null, null, null, null);
+ AddSchemaTableRow("SmallMoney", null, null, null, false, false, false, SqlDbType.SmallMoney, null, null, null, null, null);
+ AddSchemaTableRow("Text", null, null, null, false, false, false, SqlDbType.Text, null, null, null, null, null);
+ AddSchemaTableRow("Time", null, null, null, false, false, false, SqlDbType.Time, null, null, null, null, null);
+ AddSchemaTableRow("Time_5", null, 5, null, false, false, false, SqlDbType.Time, null, null, null, null, null);
+ AddSchemaTableRow("TinyInt", null, null, null, false, false, false, SqlDbType.TinyInt, null, null, null, null, null);
+ AddSchemaTableRow("Udt", null, null, null, false, false, false, SqlDbType.Udt, BulkDataReaderTest.testUdtSchemaName, BulkDataReaderTest.testUdtName, null, null, null);
+ AddSchemaTableRow("UniqueIdentifier", null, null, null, false, false, false, SqlDbType.UniqueIdentifier, null, null, null, null, null);
+ AddSchemaTableRow("VarBinary_20", 20, null, null, false, false, false, SqlDbType.VarBinary, null, null, null, null, null);
+ AddSchemaTableRow("VarBinary_Max", null, null, null, false, false, false, SqlDbType.VarBinary, null, null, null, null, null);
+ AddSchemaTableRow("VarChar_20", 20, null, null, false, false, false, SqlDbType.VarChar, null, null, null, null, null);
+ AddSchemaTableRow("VarChar_Max", null, null, null, false, false, false, SqlDbType.VarChar, null, null, null, null, null);
+ AddSchemaTableRow("Variant", null, null, null, false, false, false, SqlDbType.Variant, null, null, null, null, null);
+ AddSchemaTableRow("Xml_Database", null, null, null, false, false, false, SqlDbType.Xml, null, null, BulkDataReaderTest.testXmlSchemaCollectionDatabaseName, BulkDataReaderTest.testXMLSchemaCollectionSchemaName, BulkDataReaderTest.testXMLSchemaCollectionName);
+ AddSchemaTableRow("Xml_Database_XML", null, null, null, false, false, false, SqlDbType.Xml, null, null, BulkDataReaderTest.testXmlSchemaCollectionDatabaseName, BulkDataReaderTest.testXMLSchemaCollectionSchemaName, BulkDataReaderTest.testXMLSchemaCollectionName);
+ AddSchemaTableRow("Xml_Schema", null, null, null, false, false, false, SqlDbType.Xml, null, null, null, BulkDataReaderTest.testXMLSchemaCollectionSchemaName, BulkDataReaderTest.testXMLSchemaCollectionName);
+ AddSchemaTableRow("Xml_Xml", null, null, null, false, false, false, SqlDbType.Xml, null, null, null, null, BulkDataReaderTest.testXMLSchemaCollectionName);
+ AddSchemaTableRow("Xml", null, null, null, false, false, false, SqlDbType.Xml, null, null, null, null, null);
+ }
+
+ ///
+ /// The result set returned by the .
+ ///
+ public static readonly ReadOnlyCollection ExpectedResultSet = new ReadOnlyCollection(new List
+ {
+ (long)10,
+ new byte[20],
+ true,
+ null,
+ 'c',
+ new char[] { 'c' },
+ "c",
+ "char 20",
+ DateTime.UtcNow,
+ DateTime.UtcNow,
+ DateTime.UtcNow,
+ DateTime.UtcNow,
+ DateTimeOffset.UtcNow,
+ DateTimeOffset.UtcNow,
+ (decimal)10.5,
+ (double)10.5,
+ new byte[20],
+ (int)10,
+ (decimal)10.5,
+ "nchar 20",
+ "ntext",
+ "nvarchar 20",
+ "nvarchar max",
+ (float)10.5,
+ DateTime.UtcNow,
+ (short)10,
+ (decimal)10.5,
+ "text",
+ DateTime.UtcNow.TimeOfDay,
+ DateTime.UtcNow.TimeOfDay,
+ (byte)10,
+ new object(),
+ Guid.NewGuid(),
+ new byte[20],
+ new byte[20],
+ "varchar 20",
+ "varchar max",
+ (int)10,
+ @" ",
+ @" ",
+ @" ",
+ @" ",
+ @" "
+ });
+
+ ///
+ /// See
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// The value of the column in .
+ ///
+ ///
+ public override object GetValue(int i)
+ {
+ return BulkDataReaderSubclass.ExpectedResultSet[i];
+ }
+
+ ///
+ /// The number of rows read.
+ ///
+ private int readCount = 0;
+
+ ///
+ /// See
+ ///
+ ///
+ /// True if there are more rows; otherwise, false.
+ ///
+ ///
+ public override bool Read()
+ {
+ return readCount++ < 1;
+ }
+
+ #endregion
+
+ }
+
+ private class BulkDataReaderSchemaTest : BulkDataReader
+ {
+
+ #region Properties
+
+ ///
+ /// Is the column nullable (i.e. optional)?
+ ///
+ public bool AllowDBNull { get; set; }
+
+ ///
+ /// The name of the column.
+ ///
+ public string ColumnName { get; set; }
+
+ ///
+ /// The size of the column which may be null if not applicable.
+ ///
+ public int? ColumnSize { get; set; }
+
+ ///
+ /// Is the column part of the primary key?
+ ///
+ public bool IsKey { get; set; }
+
+ ///
+ /// Are the column values unique (i.e. never duplicated)?
+ ///
+ public bool IsUnique { get; set; }
+
+ ///
+ /// The precision of the column which may be null if not applicable.
+ ///
+ public short? NumericPrecision { get; set; }
+
+ ///
+ /// The scale of the column which may be null if not applicable.
+ ///
+ public short? NumericScale { get; set; }
+
+ ///
+ /// The corresponding .
+ ///
+ public SqlDbType ProviderType { get; set; }
+
+ ///
+ /// The schema name of the UDT.
+ ///
+ public string UdtSchema { get; set; }
+
+ ///
+ /// The type name of the UDT.
+ ///
+ public string UdtType { get; set; }
+
+ ///
+ /// For XML columns the schema collection's database name. Otherwise, null.
+ ///
+ public string XmlSchemaCollectionDatabase { get; set; }
+
+ ///
+ /// For XML columns the schema collection's name. Otherwise, null.
+ ///
+ public string XmlSchemaCollectionName { get; set; }
+
+ ///
+ /// For XML columns the schema collection's schema name. Otherwise, null.
+ ///
+ public string XmlSchemaCollectionOwningSchema { get; set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Constructor.
+ ///
+ public BulkDataReaderSchemaTest()
+ {
+ }
+
+ #endregion
+
+ #region BulkDataReader
+
+ ///
+ /// See .
+ ///
+ ///
+ /// Returns .
+ ///
+ protected override string SchemaName
+ {
+ get { return BulkDataReaderTest.testSchemaName; }
+ }
+
+ ///
+ /// See .
+ ///
+ ///
+ /// Returns .
+ ///
+ protected override string TableName
+ {
+ get { return BulkDataReaderTest.testTableName; }
+ }
+
+ ///
+ /// See
+ ///
+ ///
+ /// Creates a schema row for the various values.
+ ///
+ protected override void AddSchemaTableRows()
+ {
+ AddSchemaTableRow(this.ColumnName,
+ this.ColumnSize,
+ this.NumericPrecision,
+ this.NumericScale,
+ this.IsUnique,
+ this.IsKey,
+ this.AllowDBNull,
+ this.ProviderType,
+ this.UdtSchema,
+ this.UdtType,
+ this.XmlSchemaCollectionDatabase,
+ this.XmlSchemaCollectionOwningSchema,
+ this.XmlSchemaCollectionName);
+ }
+
+ ///
+ /// See
+ ///
+ ///
+ /// The test stub is only for testing schema functionality and behaves as if it has no rows.
+ ///
+ ///
+ /// The zero-based column ordinal.
+ ///
+ ///
+ /// Never returns.
+ ///
+ ///
+ public override object GetValue(int i)
+ {
+ throw new InvalidOperationException("No data.");
+ }
+
+
+ ///
+ /// See
+ ///
+ ///
+ /// False.
+ ///
+ ///
+ public override bool Read()
+ {
+ return false;
+ }
+
+ #endregion
+
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Tests/Persistence/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/PetaPocoCachesTest.cs
new file mode 100644
index 0000000000..de32e0704b
--- /dev/null
+++ b/src/Umbraco.Tests/Persistence/PetaPocoCachesTest.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using NUnit.Framework;
+using Umbraco.Core.Models;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Services;
+using Umbraco.Tests.Services;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Tests.TestHelpers.Entities;
+
+namespace Umbraco.Tests.Persistence
+{
+ [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
+ [TestFixture, NUnit.Framework.Ignore]
+ public class PetaPocoCachesTest : BaseServiceTest
+ {
+
+#if DEBUG
+ ///
+ /// This tests the peta poco caches
+ ///
+ ///
+ /// This test WILL fail. This is because we cannot stop PetaPoco from creating more cached items for queries such as
+ /// ContentTypeRepository.GetAll(1,2,3,4);
+ /// when combined with other GetAll queries that pass in an array of Ids, each query generated for different length
+ /// arrays will produce a unique query which then gets added to the cache.
+ ///
+ /// This test confirms this, if you analyze the DIFFERENCE output below you can see why the cached queries grow.
+ ///
+ [Test]
+ public void Check_Peta_Poco_Caches()
+ {
+ var result = new List>>();
+
+ Database.PocoData.UseLongKeys = true;
+
+ for (int i = 0; i < 2; i++)
+ {
+ int id1, id2, id3;
+ string alias;
+ CreateStuff(out id1, out id2, out id3, out alias);
+ QueryStuff(id1, id2, id3, alias);
+
+ double totalBytes1;
+ IEnumerable keys;
+ Debug.Print(Database.PocoData.PrintDebugCacheReport(out totalBytes1, out keys));
+
+ result.Add(new Tuple>(totalBytes1, keys.Count(), keys));
+ }
+
+ for (int index = 0; index < result.Count; index++)
+ {
+ var tuple = result[index];
+ Debug.Print("Bytes: {0}, Delegates: {1}", tuple.Item1, tuple.Item2);
+ if (index != 0)
+ {
+ Debug.Print("----------------DIFFERENCE---------------------");
+ var diff = tuple.Item3.Except(result[index - 1].Item3);
+ foreach (var d in diff)
+ {
+ Debug.Print(d);
+ }
+ }
+
+ }
+
+ var allByteResults = result.Select(x => x.Item1).Distinct();
+ var totalKeys = result.Select(x => x.Item2).Distinct();
+
+ Assert.AreEqual(1, allByteResults.Count());
+ Assert.AreEqual(1, totalKeys.Count());
+ }
+
+ [Test]
+ public void Verify_Memory_Expires()
+ {
+ Database.PocoData.SlidingExpirationSeconds = 2;
+
+ var managedCache = new Database.ManagedCache();
+
+ int id1, id2, id3;
+ string alias;
+ CreateStuff(out id1, out id2, out id3, out alias);
+ QueryStuff(id1, id2, id3, alias);
+
+ var count1 = managedCache.GetCache().GetCount();
+ Debug.Print("Keys = " + count1);
+ Assert.Greater(count1, 0);
+
+ Thread.Sleep(10000);
+
+ var count2 = managedCache.GetCache().GetCount();
+ Debug.Print("Keys = " + count2);
+ Assert.Less(count2, count1);
+ }
+
+ private void QueryStuff(int id1, int id2, int id3, string alias1)
+ {
+ var contentService = ServiceContext.ContentService;
+
+ ServiceContext.TagService.GetTagsForEntity(id1);
+
+ ServiceContext.TagService.GetAllContentTags();
+
+ ServiceContext.TagService.GetTagsForEntity(id2);
+
+ ServiceContext.TagService.GetTagsForEntity(id3);
+
+ contentService.CountDescendants(id3);
+
+ contentService.CountChildren(id3);
+
+ contentService.Count(contentTypeAlias: alias1);
+
+ contentService.Count();
+
+ contentService.GetById(Guid.NewGuid());
+
+ contentService.GetByLevel(2);
+
+ contentService.GetChildren(id1);
+
+ contentService.GetDescendants(id2);
+
+ contentService.GetVersions(id3);
+
+ contentService.GetRootContent();
+
+ contentService.GetContentForExpiration();
+
+ contentService.GetContentForRelease();
+
+ contentService.GetContentInRecycleBin();
+
+ ((ContentService)contentService).GetPublishedDescendants(new Content("Test", -1, new ContentType(-1))
+ {
+ Id = id1,
+ Path = "-1," + id1
+ });
+
+ contentService.GetByVersion(Guid.NewGuid());
+ }
+
+ private void CreateStuff(out int id1, out int id2, out int id3, out string alias)
+ {
+ var contentService = ServiceContext.ContentService;
+
+ var ctAlias = "umbTextpage" + Guid.NewGuid().ToString("N");
+ alias = ctAlias;
+
+ for (int i = 0; i < 20; i++)
+ {
+ contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
+ }
+ var contentTypeService = ServiceContext.ContentTypeService;
+ var contentType = MockedContentTypes.CreateSimpleContentType(ctAlias, "test Doc Type");
+ contentTypeService.Save(contentType);
+ for (int i = 0; i < 20; i++)
+ {
+ contentService.CreateContentWithIdentity("Test", -1, ctAlias, 0);
+ }
+ var parent = contentService.CreateContentWithIdentity("Test", -1, ctAlias, 0);
+ id1 = parent.Id;
+
+ for (int i = 0; i < 20; i++)
+ {
+ contentService.CreateContentWithIdentity("Test", parent, ctAlias);
+ }
+ IContent current = parent;
+ for (int i = 0; i < 20; i++)
+ {
+ current = contentService.CreateContentWithIdentity("Test", current, ctAlias);
+ }
+ contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory" + Guid.NewGuid().ToString("N"), "Mandatory Doc Type", true);
+ contentType.PropertyGroups.First().PropertyTypes.Add(
+ new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
+ {
+ DataTypeDefinitionId = 1041
+ });
+ contentTypeService.Save(contentType);
+ var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
+ content1.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true);
+ contentService.Publish(content1);
+ id2 = content1.Id;
+
+ var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1);
+ content2.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true);
+ contentService.Publish(content2);
+ id3 = content2.Id;
+
+ contentService.MoveToRecycleBin(content1);
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs b/src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs
index 0a15994785..d06d422d83 100644
--- a/src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs
+++ b/src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs
@@ -1,201 +1,16 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
using System.Text.RegularExpressions;
-using System.Threading;
using NUnit.Framework;
using Umbraco.Core;
-using Umbraco.Core.Models;
+using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
-using Umbraco.Core.Services;
-using Umbraco.Tests.Services;
+using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Tests.TestHelpers;
-using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Persistence
{
- [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
- [TestFixture, NUnit.Framework.Ignore]
- public class PetaPocoCachesTest : BaseServiceTest
- {
- ///
- /// This tests the peta poco caches
- ///
- ///
- /// This test WILL fail. This is because we cannot stop PetaPoco from creating more cached items for queries such as
- /// ContentTypeRepository.GetAll(1,2,3,4);
- /// when combined with other GetAll queries that pass in an array of Ids, each query generated for different length
- /// arrays will produce a unique query which then gets added to the cache.
- ///
- /// This test confirms this, if you analyze the DIFFERENCE output below you can see why the cached queries grow.
- ///
- [Test]
- public void Check_Peta_Poco_Caches()
- {
- var result = new List>>();
-
- Database.PocoData.UseLongKeys = true;
-
- for (int i = 0; i < 2; i++)
- {
- int id1, id2, id3;
- string alias;
- CreateStuff(out id1, out id2, out id3, out alias);
- QueryStuff(id1, id2, id3, alias);
-
- double totalBytes1;
- IEnumerable keys;
- Debug.Print(Database.PocoData.PrintDebugCacheReport(out totalBytes1, out keys));
-
- result.Add(new Tuple>(totalBytes1, keys.Count(), keys));
- }
-
- for (int index = 0; index < result.Count; index++)
- {
- var tuple = result[index];
- Debug.Print("Bytes: {0}, Delegates: {1}", tuple.Item1, tuple.Item2);
- if (index != 0)
- {
- Debug.Print("----------------DIFFERENCE---------------------");
- var diff = tuple.Item3.Except(result[index - 1].Item3);
- foreach (var d in diff)
- {
- Debug.Print(d);
- }
- }
-
- }
-
- var allByteResults = result.Select(x => x.Item1).Distinct();
- var totalKeys = result.Select(x => x.Item2).Distinct();
-
- Assert.AreEqual(1, allByteResults.Count());
- Assert.AreEqual(1, totalKeys.Count());
- }
-
- [Test]
- public void Verify_Memory_Expires()
- {
- Database.PocoData.SlidingExpirationSeconds = 2;
-
- var managedCache = new Database.ManagedCache();
-
- int id1, id2, id3;
- string alias;
- CreateStuff(out id1, out id2, out id3, out alias);
- QueryStuff(id1, id2, id3, alias);
-
- var count1 = managedCache.GetCache().GetCount();
- Debug.Print("Keys = " + count1);
- Assert.Greater(count1, 0);
-
- Thread.Sleep(10000);
-
- var count2 = managedCache.GetCache().GetCount();
- Debug.Print("Keys = " + count2);
- Assert.Less(count2, count1);
- }
-
- private void QueryStuff(int id1, int id2, int id3, string alias1)
- {
- var contentService = ServiceContext.ContentService;
-
- ServiceContext.TagService.GetTagsForEntity(id1);
-
- ServiceContext.TagService.GetAllContentTags();
-
- ServiceContext.TagService.GetTagsForEntity(id2);
-
- ServiceContext.TagService.GetTagsForEntity(id3);
-
- contentService.CountDescendants(id3);
-
- contentService.CountChildren(id3);
-
- contentService.Count(contentTypeAlias: alias1);
-
- contentService.Count();
-
- contentService.GetById(Guid.NewGuid());
-
- contentService.GetByLevel(2);
-
- contentService.GetChildren(id1);
-
- contentService.GetDescendants(id2);
-
- contentService.GetVersions(id3);
-
- contentService.GetRootContent();
-
- contentService.GetContentForExpiration();
-
- contentService.GetContentForRelease();
-
- contentService.GetContentInRecycleBin();
-
- ((ContentService)contentService).GetPublishedDescendants(new Content("Test", -1, new ContentType(-1))
- {
- Id = id1,
- Path = "-1," + id1
- });
-
- contentService.GetByVersion(Guid.NewGuid());
- }
-
- private void CreateStuff(out int id1, out int id2, out int id3, out string alias)
- {
- var contentService = ServiceContext.ContentService;
-
- var ctAlias = "umbTextpage" + Guid.NewGuid().ToString("N");
- alias = ctAlias;
-
- for (int i = 0; i < 20; i++)
- {
- contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
- }
- var contentTypeService = ServiceContext.ContentTypeService;
- var contentType = MockedContentTypes.CreateSimpleContentType(ctAlias, "test Doc Type");
- contentTypeService.Save(contentType);
- for (int i = 0; i < 20; i++)
- {
- contentService.CreateContentWithIdentity("Test", -1, ctAlias, 0);
- }
- var parent = contentService.CreateContentWithIdentity("Test", -1, ctAlias, 0);
- id1 = parent.Id;
-
- for (int i = 0; i < 20; i++)
- {
- contentService.CreateContentWithIdentity("Test", parent, ctAlias);
- }
- IContent current = parent;
- for (int i = 0; i < 20; i++)
- {
- current = contentService.CreateContentWithIdentity("Test", current, ctAlias);
- }
- contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory" + Guid.NewGuid().ToString("N"), "Mandatory Doc Type", true);
- contentType.PropertyGroups.First().PropertyTypes.Add(
- new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
- {
- DataTypeDefinitionId = 1041
- });
- contentTypeService.Save(contentType);
- var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
- content1.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true);
- contentService.Publish(content1);
- id2 = content1.Id;
-
- var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1);
- content2.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true);
- contentService.Publish(content2);
- id3 = content2.Id;
-
- contentService.MoveToRecycleBin(content1);
- }
- }
-
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture]
public class PetaPocoExtensionsTest : BaseDatabaseFactoryTest
@@ -213,7 +28,7 @@ namespace Umbraco.Tests.Persistence
}
[Test]
- public void Can_Bulk_Insert()
+ public void Can_Bulk_Insert_One_By_One()
{
// Arrange
var db = DatabaseContext.Database;
@@ -234,13 +49,168 @@ namespace Umbraco.Tests.Persistence
// Act
using (ProfilingLogger.TraceDuration("starting insert", "finished insert"))
{
- db.BulkInsertRecords(servers);
+ using (var tr = db.GetTransaction())
+ {
+ db.BulkInsertRecords(servers, tr, SqlSyntax, useNativeSqlPlatformBulkInsert:false);
+ tr.Complete();
+ }
}
// Assert
Assert.That(db.ExecuteScalar("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(1000));
}
+ [Test]
+ public void Can_Bulk_Insert_One_By_One_Transaction_Rollback()
+ {
+ // Arrange
+ var db = DatabaseContext.Database;
+
+ var servers = new List();
+ for (var i = 0; i < 1000; i++)
+ {
+ servers.Add(new ServerRegistrationDto
+ {
+ ServerAddress = "address" + i,
+ ServerIdentity = "computer" + i,
+ DateRegistered = DateTime.Now,
+ IsActive = true,
+ DateAccessed = DateTime.Now
+ });
+ }
+
+ // Act
+ using (ProfilingLogger.TraceDuration("starting insert", "finished insert"))
+ {
+ using (var tr = db.GetTransaction())
+ {
+ db.BulkInsertRecords(servers, tr, SqlSyntax, useNativeSqlPlatformBulkInsert: false);
+ //don't call complete here - the trans will be rolled back
+ }
+ }
+
+ // Assert
+ Assert.That(db.ExecuteScalar("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(0));
+ }
+
+
+ [NUnit.Framework.Ignore("Ignored because you need to configure your own SQL Server to test thsi with")]
+ [Test]
+ public void Can_Bulk_Insert_Native_Sql_Server_Bulk_Inserts()
+ {
+ //create the db
+ var dbSqlServer = new UmbracoDatabase(
+ "server=.\\SQLExpress;database=YOURDB;user id=YOURUSER;password=YOURPASSWORD",
+ Constants.DatabaseProviders.SqlServer,
+ new DebugDiagnosticsLogger());
+
+ //drop the table
+ dbSqlServer.Execute("DROP TABLE [umbracoServer]");
+
+ //re-create it
+ dbSqlServer.Execute(@"CREATE TABLE [umbracoServer](
+ [id] [int] IDENTITY(1,1) NOT NULL,
+ [address] [nvarchar](500) NOT NULL,
+ [computerName] [nvarchar](255) NOT NULL,
+ [registeredDate] [datetime] NOT NULL CONSTRAINT [DF_umbracoServer_registeredDate] DEFAULT (getdate()),
+ [lastNotifiedDate] [datetime] NOT NULL,
+ [isActive] [bit] NOT NULL,
+ [isMaster] [bit] NOT NULL,
+ CONSTRAINT [PK_umbracoServer] PRIMARY KEY CLUSTERED
+(
+ [id] ASC
+)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
+)");
+ var data = new List();
+ for (var i = 0; i < 1000; i++)
+ {
+ data.Add(new ServerRegistrationDto
+ {
+ ServerAddress = "address" + i,
+ ServerIdentity = "computer" + i,
+ DateRegistered = DateTime.Now,
+ IsActive = true,
+ DateAccessed = DateTime.Now
+ });
+ }
+
+ var sqlServerSyntax = new SqlServerSyntaxProvider();
+ using (var tr = dbSqlServer.GetTransaction())
+ {
+ dbSqlServer.BulkInsertRecords(data, tr, sqlServerSyntax, useNativeSqlPlatformBulkInsert: true);
+ tr.Complete();
+ }
+
+ // Assert
+ Assert.That(dbSqlServer.ExecuteScalar("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(1000));
+ }
+
+ [Test]
+ public void Can_Bulk_Insert_Native_Sql_Bulk_Inserts()
+ {
+ // Arrange
+ var db = DatabaseContext.Database;
+
+ var servers = new List();
+ for (var i = 0; i < 1000; i++)
+ {
+ servers.Add(new ServerRegistrationDto
+ {
+ ServerAddress = "address" + i,
+ ServerIdentity = "computer" + i,
+ DateRegistered = DateTime.Now,
+ IsActive = true,
+ DateAccessed = DateTime.Now
+ });
+ }
+
+ // Act
+ using (ProfilingLogger.TraceDuration("starting insert", "finished insert"))
+ {
+ using (var tr = db.GetTransaction())
+ {
+ db.BulkInsertRecords(servers, tr, SqlSyntax, useNativeSqlPlatformBulkInsert: true);
+ tr.Complete();
+ }
+ }
+
+ // Assert
+ Assert.That(db.ExecuteScalar("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(1000));
+ }
+
+ [Test]
+ public void Can_Bulk_Insert_Native_Sql_Bulk_Inserts_Transaction_Rollback()
+ {
+ // Arrange
+ var db = DatabaseContext.Database;
+
+ var servers = new List();
+ for (var i = 0; i < 1000; i++)
+ {
+ servers.Add(new ServerRegistrationDto
+ {
+ ServerAddress = "address" + i,
+ ServerIdentity = "computer" + i,
+ DateRegistered = DateTime.Now,
+ IsActive = true,
+ DateAccessed = DateTime.Now
+ });
+ }
+
+ // Act
+ using (ProfilingLogger.TraceDuration("starting insert", "finished insert"))
+ {
+ using (var tr = db.GetTransaction())
+ {
+ db.BulkInsertRecords(servers, tr, SqlSyntax, useNativeSqlPlatformBulkInsert: true);
+ //don't call complete here - the trans will be rolled back
+ }
+ }
+
+ // Assert
+ Assert.That(db.ExecuteScalar("SELECT COUNT(*) FROM umbracoServer"), Is.EqualTo(0));
+ }
+
[Test]
public void Generate_Bulk_Import_Sql()
{
@@ -263,7 +233,9 @@ namespace Umbraco.Tests.Persistence
// Act
string[] sql;
- db.GenerateBulkInsertCommand(servers, db.Connection, out sql);
+ db.GenerateBulkInsertCommand(
+ Database.PocoData.ForType(typeof(ServerRegistrationDto)),
+ servers, out sql);
db.CloseSharedConnection();
// Assert
@@ -295,7 +267,7 @@ namespace Umbraco.Tests.Persistence
// Act
string[] sql;
- db.GenerateBulkInsertCommand(servers, db.Connection, out sql);
+ db.GenerateBulkInsertCommand(Database.PocoData.ForType(typeof(ServerRegistrationDto)), servers, out sql);
db.CloseSharedConnection();
// Assert
diff --git a/src/Umbraco.Tests/Persistence/SyntaxProvider/MySqlSyntaxProviderTests.cs b/src/Umbraco.Tests/Persistence/SyntaxProvider/MySqlSyntaxProviderTests.cs
index 8126aa5e36..e58506aa03 100644
--- a/src/Umbraco.Tests/Persistence/SyntaxProvider/MySqlSyntaxProviderTests.cs
+++ b/src/Umbraco.Tests/Persistence/SyntaxProvider/MySqlSyntaxProviderTests.cs
@@ -28,7 +28,7 @@ namespace Umbraco.Tests.Persistence.SyntaxProvider
public void Can_Generate_Create_Table_Statement()
{
var type = typeof(TagRelationshipDto);
- var definition = DefinitionFactory.GetTableDefinition(type);
+ var definition = DefinitionFactory.GetTableDefinition(SqlSyntaxContext.SqlSyntaxProvider, type);
string create = SqlSyntaxContext.SqlSyntaxProvider.Format(definition);
string primaryKey = SqlSyntaxContext.SqlSyntaxProvider.FormatPrimaryKey(definition);
diff --git a/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs
index e960f50799..fafddb8dfd 100644
--- a/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs
+++ b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs
@@ -51,7 +51,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine,
var sqlSyntax = new SqlCeSyntaxProvider();
var type = typeof (NodeDto);
- var definition = DefinitionFactory.GetTableDefinition(type);
+ var definition = DefinitionFactory.GetTableDefinition(sqlSyntax, type);
string create = sqlSyntax.Format(definition);
string primaryKey = sqlSyntax.FormatPrimaryKey(definition);
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 8659a73cd1..a606d401a0 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -176,7 +176,9 @@
+
+
From 1a6986cfcfac4610678077e07f39dcb5b5cea8dc Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 25 Oct 2016 12:34:06 +0200
Subject: [PATCH 23/48] U4-9111 Benchmark BulkCopy changes from U4-9107
---
src/Umbraco.Core/Properties/AssemblyInfo.cs | 1 +
src/Umbraco.Tests.Benchmarks/App.config | 48 ++++
.../BulkInsertBenchmarks.cs | 210 ++++++++++++++++++
src/Umbraco.Tests.Benchmarks/Program.cs | 19 ++
.../Properties/AssemblyInfo.cs | 36 +++
.../TraceEvent.ReleaseNotes.txt | 61 +++++
.../Umbraco.Tests.Benchmarks.csproj | 149 +++++++++++++
src/Umbraco.Tests.Benchmarks/packages.config | 25 +++
src/umbraco.sln | 9 +-
9 files changed, 557 insertions(+), 1 deletion(-)
create mode 100644 src/Umbraco.Tests.Benchmarks/App.config
create mode 100644 src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs
create mode 100644 src/Umbraco.Tests.Benchmarks/Program.cs
create mode 100644 src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs
create mode 100644 src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt
create mode 100644 src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj
create mode 100644 src/Umbraco.Tests.Benchmarks/packages.config
diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs
index d1ddadde37..dbc1ab6c93 100644
--- a/src/Umbraco.Core/Properties/AssemblyInfo.cs
+++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs
@@ -32,6 +32,7 @@ using System.Security.Permissions;
[assembly: InternalsVisibleTo("umbraco.editorControls")]
[assembly: InternalsVisibleTo("Umbraco.Tests")]
+[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")]
[assembly: InternalsVisibleTo("Umbraco.Core")]
[assembly: InternalsVisibleTo("Umbraco.Web")]
[assembly: InternalsVisibleTo("Umbraco.Web.UI")]
diff --git a/src/Umbraco.Tests.Benchmarks/App.config b/src/Umbraco.Tests.Benchmarks/App.config
new file mode 100644
index 0000000000..a988966f23
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/App.config
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs
new file mode 100644
index 0000000000..5212a4f524
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Collections.Generic;
+using System.Data.SqlServerCe;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Xml;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Columns;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.Diagnostics.Windows;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Loggers;
+using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Validators;
+using Umbraco.Core;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.Migrations.Initial;
+using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Tests.TestHelpers;
+using ILogger = Umbraco.Core.Logging.ILogger;
+
+namespace Umbraco.Tests.Benchmarks
+{
+ [Config(typeof(Config))]
+ public class BulkInsertBenchmarks
+ {
+ private class Config : ManualConfig
+ {
+ public Config()
+ {
+ Add(new MemoryDiagnoser());
+ //Add(ExecutionValidator.FailOnError);
+
+ //The 'quick and dirty' settings, so it runs a little quicker
+ // see benchmarkdotnet FAQ
+ Add(Job.Default
+ .WithLaunchCount(1) // benchmark process will be launched only once
+ .WithIterationTime(100) // 100ms per iteration
+ .WithWarmupCount(3) // 3 warmup iteration
+ .WithTargetCount(3)); // 3 target iteration
+ }
+ }
+
+ private static byte[] _initDbBytes = null;
+
+ [Setup]
+ public void Setup()
+ {
+ var logger = new DebugDiagnosticsLogger();
+ var path = TestHelper.CurrentAssemblyDirectory;
+
+ _sqlCeSyntax = new SqlCeSyntaxProvider();
+ _sqlServerSyntax = new SqlServerSyntaxProvider();
+
+ SetupSqlCe(path, logger);
+ SetupSqlServer(logger);
+
+
+ }
+
+ private void SetupSqlServer(ILogger logger)
+ {
+ //create the db
+ _dbSqlServer = new UmbracoDatabase(
+ "server=.\\SQLExpress;database=YOURDB;user id=YOURUSER;password=YOURPASS",
+ Constants.DatabaseProviders.SqlServer,
+ logger);
+
+ //drop the table
+ _dbSqlServer.Execute("DROP TABLE [umbracoServer]");
+
+ //re-create it
+ _dbSqlServer.Execute(@"CREATE TABLE [umbracoServer](
+ [id] [int] IDENTITY(1,1) NOT NULL,
+ [address] [nvarchar](500) NOT NULL,
+ [computerName] [nvarchar](255) NOT NULL,
+ [registeredDate] [datetime] NOT NULL CONSTRAINT [DF_umbracoServer_registeredDate] DEFAULT (getdate()),
+ [lastNotifiedDate] [datetime] NOT NULL,
+ [isActive] [bit] NOT NULL,
+ [isMaster] [bit] NOT NULL,
+ CONSTRAINT [PK_umbracoServer] PRIMARY KEY CLUSTERED
+(
+ [id] ASC
+)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
+)");
+ }
+
+ private void SetupSqlCe(string path, ILogger logger)
+ {
+ var dbName = string.Concat("Umb", Guid.NewGuid(), ".sdf");
+ AppDomain.CurrentDomain.SetData("DataDirectory", path);
+ var sqlCeConnectionString = $"Datasource=|DataDirectory|\\{dbName};Flush Interval=1;";
+
+ _dbFile = Path.Combine(path, dbName);
+
+ //only create the db one time
+ if (_initDbBytes == null)
+ {
+ using (var engine = new SqlCeEngine(sqlCeConnectionString))
+ {
+ engine.CreateDatabase();
+ }
+
+ //use the db to create the initial schema so we can reuse in each bench
+ using (_dbSqlCe = new UmbracoDatabase(
+ sqlCeConnectionString,
+ Constants.DatabaseProviders.SqlCe,
+ logger))
+ {
+ var creation = new DatabaseSchemaCreation(_dbSqlCe, logger, _sqlCeSyntax);
+ creation.InitializeDatabaseSchema();
+ }
+ _initDbBytes = File.ReadAllBytes(_dbFile);
+ }
+ else
+ {
+ File.WriteAllBytes(_dbFile, _initDbBytes);
+ }
+
+ //create the db
+ _dbSqlCe = new UmbracoDatabase(
+ sqlCeConnectionString,
+ Constants.DatabaseProviders.SqlCe,
+ logger);
+ }
+
+ private List GetData()
+ {
+ var data = new List();
+ for (var i = 0; i < 1000; i++)
+ {
+ data.Add(new ServerRegistrationDto
+ {
+ ServerAddress = "address" + Guid.NewGuid(),
+ ServerIdentity = "computer" + Guid.NewGuid(),
+ DateRegistered = DateTime.Now,
+ IsActive = true,
+ DateAccessed = DateTime.Now
+ });
+ }
+ return data;
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ _dbSqlCe.Dispose();
+ _dbSqlServer.Dispose();
+ File.Delete(_dbFile);
+ }
+
+ private string _dbFile;
+ private UmbracoDatabase _dbSqlCe;
+ private UmbracoDatabase _dbSqlServer;
+ private ISqlSyntaxProvider _sqlCeSyntax;
+ private ISqlSyntaxProvider _sqlServerSyntax;
+
+ ///
+ /// Tests updating the existing XML way
+ ///
+ [Benchmark(Baseline = true)]
+ public void SqlCeOneByOne()
+ {
+ using (var tr = _dbSqlCe.GetTransaction())
+ {
+ _dbSqlCe.BulkInsertRecords(GetData(), tr, _sqlCeSyntax, useNativeSqlPlatformBulkInsert: false);
+ tr.Complete();
+ }
+ }
+
+ ///
+ /// Tests updating with only the object graph
+ ///
+ [Benchmark]
+ public void SqlCeTableDirect()
+ {
+ using (var tr = _dbSqlCe.GetTransaction())
+ {
+ _dbSqlCe.BulkInsertRecords(GetData(), tr, _sqlCeSyntax, useNativeSqlPlatformBulkInsert: true);
+ tr.Complete();
+ }
+ }
+
+ [Benchmark]
+ public void SqlServerBulkInsertStatements()
+ {
+ using (var tr = _dbSqlServer.GetTransaction())
+ {
+ _dbSqlServer.BulkInsertRecords(GetData(), tr, _sqlServerSyntax, useNativeSqlPlatformBulkInsert: false);
+ tr.Complete();
+ }
+ }
+
+ [Benchmark]
+ public void SqlServerBulkCopy()
+ {
+ using (var tr = _dbSqlServer.GetTransaction())
+ {
+ _dbSqlServer.BulkInsertRecords(GetData(), tr, _sqlServerSyntax, useNativeSqlPlatformBulkInsert: true);
+ tr.Complete();
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests.Benchmarks/Program.cs b/src/Umbraco.Tests.Benchmarks/Program.cs
new file mode 100644
index 0000000000..37c1ccd853
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/Program.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Running;
+
+namespace Umbraco.Tests.Benchmarks
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ var summary = BenchmarkRunner.Run();
+
+ Console.ReadLine();
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..fb00b8d4e9
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Umbraco.Tests.Benchmarks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Umbraco.Tests.Benchmarks")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("86deb346-089f-4106-89c8-d852b9cf2a33")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt b/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt
new file mode 100644
index 0000000000..21fcb5d0ca
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt
@@ -0,0 +1,61 @@
+Version 1.0.0.3 - Initial release to NuGet, pre-release.
+
+ TraceEvent has been available from the site http://bcl.codeplex.com/wikipage?title=TraceEvent for some time now
+ this NuGet Version of the library supersedes that one. WHile the 'core' part of the library is unchanged,
+ we did change lesser used features, and change the namespace and DLL name, which will cause break. We anticipate
+ it will take an hour or so to 'port' to this version from the old one. Below are specific details on what
+ has changed to help in this port.
+
+ * The DLL has been renamed from TraceEvent.dll to Microsoft.Diagnostics.Tracing.TraceEvent.dll
+ * The name spaces for all classes have been changed. The easiest way to port is to simply place
+ the following using clauses at the top of any file that uses TraceEvent classes
+ using Microsoft.Diagnostics.Symbols;
+ using Microsoft.Diagnostics.Tracing;
+ using Microsoft.Diagnostics.Tracing.Etlx;
+ using Microsoft.Diagnostics.Tracing.Parsers.Clr;
+ using Microsoft.Diagnostics.Tracing.Parsers.Kernel;
+ using Microsoft.Diagnostics.Tracing.Session;
+ using Microsoft.Diagnostics.Tracing.Stacks;
+ * Any method with the name RelMSec in it has been changed to be RelativeMSec. The easiest port is to
+ simply globally rename RelMSec to RelativeMSec
+ * Any property in the Trace* classes that has the form Max*Index has been renamed to Count.
+ * A number of methods have been declared obsolete, these are mostly renames and the warning will tell you
+ how to update them.
+ * The following classes have been rename
+ SymPath -> SymbolPath
+ SymPathElement -> SymbolPathElement
+ SymbolReaderFlags -> SymbolReaderOptions
+ * TraceEventSession is now StopOnDispose (it will stop the session when TraceEventSesssion dies), by default
+ If you were relying on the kernel session living past the process that started it, you must now set
+ the StopOnDispose explicitly
+ * There used to be XmlAttrib extensions methods on StringBuilder for use in manifest generated TraceEventParsers
+ These have been moved to protected members of TraceEvent. The result is that in stead of writing
+ sb.XmlAttrib(...) you write XmlAttrib(sb, ...)
+ * References to Pdb in names have been replaced with 'Symbol' to conform to naming guidelines.
+
+ ***********************************************************************************************
+Version 1.0.0.4 - Initial stable release
+
+ Mostly this was insuring that the library was cleaned up in preparation
+ for release the TraceParserGen tool
+
+ Improved the docs, removed old code, fixed some naming convention stuff
+
+ * Additional changes from the PreRelease copy to the first Stable release
+
+ * The arguments to AddCallbackForProviderEvent were reversed!!!! (now provider than event)
+ * The arguments to Observe(string, string)!!!! (now provider than event)
+ * Event names for these APIs must include a / between the Task and Opcode names
+
+ * Many Events in KernelTraceEventParser were harmonized to be consistent with other conventions
+ * Events of the form PageFault* were typically renamed to Memory*
+ * The 'End' suffix was renamed to 'Stop' (its official name)
+ * PerfInfoSampleProf -> PerfInfoSample
+ * PerfInfoSampleProf -> PerfInfoSample
+ * ReadyThread -> DispatcherReadyThread
+ * StackWalkTraceData -> StackWalkStackTraceData
+ * FileIo -> FileIO
+ * DiskIo -> DiskIO
+
+ * Many Events in SymbolTraceEventParser were harmonized to be consistent with other conventions
+ * names with Symbol -> ImageID
diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj
new file mode 100644
index 0000000000..66033ba08e
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj
@@ -0,0 +1,149 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}
+ Exe
+ Properties
+ Umbraco.Tests.Benchmarks
+ Umbraco.Tests.Benchmarks
+ v4.5
+ 512
+
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ Always
+
+
+
+ ..\packages\BenchmarkDotNet.0.9.9\lib\net45\BenchmarkDotNet.dll
+ True
+
+
+ ..\packages\BenchmarkDotNet.Core.0.9.9\lib\net45\BenchmarkDotNet.Core.dll
+ True
+
+
+ ..\packages\BenchmarkDotNet.Diagnostics.Windows.0.9.9\lib\net45\BenchmarkDotNet.Diagnostics.Windows.dll
+ True
+
+
+ ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.9.9\lib\net45\BenchmarkDotNet.Toolchains.Roslyn.dll
+ True
+
+
+ ..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll
+ True
+
+
+ ..\packages\Microsoft.CodeAnalysis.CSharp.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.dll
+ True
+
+
+ ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll
+ True
+
+
+
+ ..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll
+ True
+
+
+
+ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll
+ True
+
+
+ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll
+ True
+
+
+
+ ..\packages\System.Reflection.Metadata.1.2.0\lib\portable-net45+win8\System.Reflection.Metadata.dll
+ True
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.0.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {31785bc3-256c-4613-b2f5-a1b0bdded8c1}
+ Umbraco.Core
+
+
+ {5d3b8245-ada6-453f-a008-50ed04bfe770}
+ Umbraco.Tests
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
+
+ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\amd64\*.* "$(TargetDir)amd64\" /Y /F /E /I /C /D
+xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" /Y /F /E /I /C /D
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests.Benchmarks/packages.config b/src/Umbraco.Tests.Benchmarks/packages.config
new file mode 100644
index 0000000000..c4d2ba1df2
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/packages.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/umbraco.sln b/src/umbraco.sln
index f9267c0412..8648f6ed5c 100644
--- a/src/umbraco.sln
+++ b/src/umbraco.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}"
EndProject
@@ -114,6 +114,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{5B03EF4E
..\build\NuSpecs\build\UmbracoCms.targets = ..\build\NuSpecs\build\UmbracoCms.targets
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{86DEB346-089F-4106-89C8-D852B9CF2A33}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -178,6 +180,10 @@ Global
{07FBC26B-2927-4A22-8D96-D644C667FECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -187,5 +193,6 @@ Global
{5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
{E3F9F378-AFE1-40A5-90BD-82833375DBFE} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D}
{5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D}
+ {86DEB346-089F-4106-89C8-D852B9CF2A33} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
EndGlobalSection
EndGlobal
From e330d40005eab2cc4eda67fe4f1224dd7f8c7e65 Mon Sep 17 00:00:00 2001
From: Stephan
Date: Tue, 25 Oct 2016 12:40:27 +0200
Subject: [PATCH 24/48] U4-9090 - Upgrade to ModelsBuilder 3.0.5 (reuse
compiled live dll on restart)
---
build/NuSpecs/UmbracoCms.nuspec | 2 +-
src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++--
src/Umbraco.Web.UI/config/Dashboard.config | 20 ++++++++++----------
src/Umbraco.Web.UI/packages.config | 2 +-
4 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index 54d12f6844..d30a139adc 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -17,7 +17,7 @@
-
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 234b3a8f09..6f2728a241 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -347,8 +347,8 @@
umbraco.providers
-
- ..\packages\Umbraco.ModelsBuilder.3.0.4\lib\Umbraco.ModelsBuilder.dll
+
+ ..\packages\Umbraco.ModelsBuilder.3.0.5\lib\Umbraco.ModelsBuilder.dll
True
diff --git a/src/Umbraco.Web.UI/config/Dashboard.config b/src/Umbraco.Web.UI/config/Dashboard.config
index 81bd9be6e8..c40218c8d2 100644
--- a/src/Umbraco.Web.UI/config/Dashboard.config
+++ b/src/Umbraco.Web.UI/config/Dashboard.config
@@ -99,16 +99,6 @@
-
-
- developer
-
-
-
- /App_Plugins/ModelsBuilder/modelsbuilder.htm
-
-
-
content
@@ -119,4 +109,14 @@
+
+
+ developer
+
+
+
+ /App_Plugins/ModelsBuilder/modelsbuilder.htm
+
+
+
diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config
index 50b4e59bdc..3850961ea8 100644
--- a/src/Umbraco.Web.UI/packages.config
+++ b/src/Umbraco.Web.UI/packages.config
@@ -35,6 +35,6 @@
-
+
\ No newline at end of file
From f1d5ccb1e33f0f25f2e9b16ef70f50efe3a4606e Mon Sep 17 00:00:00 2001
From: Mads Rasmussen
Date: Tue, 25 Oct 2016 15:40:31 +0200
Subject: [PATCH 25/48] Fixes: U4-9039 No progress indication when uploading
local package
---
.../views/install-local.controller.js | 3 ++
.../views/packager/views/install-local.html | 39 +++++++++++++++++--
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js
index e29df0b9ae..e5fba69a88 100644
--- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js
@@ -34,6 +34,9 @@
file: file
}).progress(function (evt) {
+ // set view state to uploading
+ vm.state = 'uploading';
+
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html
index 07fb21d00b..499e844588 100644
--- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html
+++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html
@@ -37,10 +37,8 @@
- or click here to choose files
-
- {{vm.zipFile.serverErrorMessage}}
-
+
@@ -53,6 +51,41 @@
+
+
+
+
+ ← Upload another package
+
+
+
+
+
+
+
+
+
+
+
+
+
Uploading package
+
+
+
+
+
+ {{ vm.zipFile.serverErrorMessage }}
+
+
+
+
+
+
+
+
+
+
From d7d4b26a663875c3071e8c14bc07bb896f9d7b25 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 25 Oct 2016 15:50:33 +0200
Subject: [PATCH 26/48] This changes the instruction fetch process to query for
the top records based on the MaxProcessingInstructionCount. This changes the
Fetch to be a Query so they are not all loaded into memory and instead uses a
db reader. This checks if any JSON blob contains more than the
MaxProcessingInstructionCount and if so, it breaks out of the reader, takes
the max amount to be processed and re-saves the remaining back to the same
record so that this single request does not over-process instructions (which
can take a long time and cause all sorts of problems)
---
.../Sync/DatabaseServerMessenger.cs | 168 +++++++++++++-----
1 file changed, 125 insertions(+), 43 deletions(-)
diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
index 4e46c0ab5c..e8d5287edc 100644
--- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
+++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
@@ -115,7 +115,11 @@ namespace Umbraco.Core.Sync
{
_released = true; // no more syncs
}
- _syncIdle.WaitOne(); // wait for pending sync
+
+ // Wait for pending sync this is waiting for _syncIdle.Set()
+ // to be called. Until that is called, the appdomain cannot shut down!
+ // so whatever is locking this currently should hurry up!
+ _syncIdle.WaitOne();
},
weight);
@@ -197,13 +201,15 @@ namespace Umbraco.Core.Sync
{
if (_syncing)
return;
-
+
+ //Don't continue if we are released
if (_released)
return;
if ((DateTime.UtcNow - _lastSync).TotalSeconds <= _options.ThrottleSeconds)
return;
+ //Set our flag and the lock to be in it's original state (i.e. it can be awaited)
_syncing = true;
_syncIdle.Reset();
_lastSync = DateTime.UtcNow;
@@ -215,6 +221,7 @@ namespace Umbraco.Core.Sync
{
ProcessDatabaseInstructions();
+ //Check for pruning throttling
if ((DateTime.UtcNow - _lastPruned).TotalSeconds <= _options.PruneThrottleSeconds)
return;
@@ -231,6 +238,7 @@ namespace Umbraco.Core.Sync
}
finally
{
+ //We must reset our flag and signal any waiting locks
_syncing = false;
_syncIdle.Set();
}
@@ -255,13 +263,14 @@ namespace Umbraco.Core.Sync
//
// FIXME not true if we're running on a background thread, assuming we can?
+
var sql = new Sql().Select("*")
.From(_appContext.DatabaseContext.SqlSyntax)
.Where(dto => dto.Id > _lastId)
.OrderBy(dto => dto.Id, _appContext.DatabaseContext.SqlSyntax);
- var dtos = _appContext.DatabaseContext.Database.Fetch(sql);
- if (dtos.Count <= 0) return;
+ //only retrieve the max (just in case there's tons)
+ var topSql = _appContext.DatabaseContext.SqlSyntax.SelectTop(sql, _options.MaxProcessingInstructionCount);
// only process instructions coming from a remote server, and ignore instructions coming from
// the local server as they've already been processed. We should NOT assume that the sequence of
@@ -269,7 +278,16 @@ namespace Umbraco.Core.Sync
var localIdentity = LocalIdentity;
var lastId = 0;
- foreach (var dto in dtos)
+
+ //this is used to determine if we need to exit the reader loop below because there are actually
+ // too many instructions to process. In which case we need to exit the reader so we can actually re-save
+ // the remaining instructions back to the same row (we cannot save something while inside a reader loop)
+ Tuple> maxInstructions = null;
+
+ //IMPORTANT! We are doing a Query here instead of a Fetch, this means that it will open a data reader
+ // which we are iterating over instead of loading everything into memory and iterating over that.
+ // When doing this we always must use a for loop so that the Enumerator is disposed and the reader is closed.
+ foreach (var dto in _appContext.DatabaseContext.Database.Query(topSql))
{
if (dto.OriginIdentity == localIdentity)
{
@@ -291,27 +309,73 @@ namespace Umbraco.Core.Sync
continue;
}
- // execute remote instructions & update lastId
- try
- {
- NotifyRefreshers(jsonA);
- lastId = dto.Id;
- }
- catch (Exception ex)
- {
- _logger.Error(
- string.Format("DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions ({0}: \"{1}\"). Instruction is being skipped/ignored", dto.Id, dto.Instructions), ex);
+ var instructionBatch = GetAllInstructions(jsonA);
- //we cannot throw here because this invalid instruction will just keep getting processed over and over and errors
- // will be thrown over and over. The only thing we can do is ignore and move on.
- lastId = dto.Id;
+ // Here we should check if there's too many instructions, if there is we should split them and re-save the instructions entry with
+ // the trimmed instructions. We then don't update the lastsynced value so that this row is re-processed again but with only the remaining
+ // instructions in it.
+ if (instructionBatch.Count > _options.MaxProcessingInstructionCount)
+ {
+ maxInstructions = new Tuple>(dto, instructionBatch);
+ break;
}
+
+ //process as per-normal
+ lastId = ProcessDatabaseInstructions(instructionBatch, dto);
+ }
+
+ //If this is not null this means we've found a row that has a ton of instructions in it and we'll need to process
+ // just a part of it and then re-save the remaining to the same row so that another request can deal with the data.
+ if (maxInstructions != null)
+ {
+ var remainingCount = maxInstructions.Item2.Count - _options.MaxProcessingInstructionCount;
+
+ _logger.Info(
+ "Max processing instruction count reached. This batch will be processed now but the remaining {0} will be processed by subsequent requests.", () => remainingCount);
+
+ var processingBatch = maxInstructions.Item2.GetRange(0, _options.MaxProcessingInstructionCount);
+ //NOTE: We are not persisting the lastId from the result of this method because we will need to re-process it
+ ProcessDatabaseInstructions(processingBatch, maxInstructions.Item1);
+
+ //Save the instruction blob back to the DB with the trimmed instruction count
+ var remaining = maxInstructions.Item2.GetRange(_options.MaxProcessingInstructionCount - 1, remainingCount);
+ maxInstructions.Item1.UtcStamp = DateTime.UtcNow;
+ //serialize the remaining instructions (leave the original identity as-is)
+ maxInstructions.Item1.Instructions = JsonConvert.SerializeObject(remaining, Formatting.None);
+ ApplicationContext.DatabaseContext.Database.Update(maxInstructions.Item1);
}
if (lastId > 0)
SaveLastSynced(lastId);
}
+ private int ProcessDatabaseInstructions(List instructionBatch, CacheInstructionDto dto)
+ {
+ // execute remote instructions & update lastId
+ try
+ {
+ NotifyRefreshers(instructionBatch);
+ return dto.Id;
+ }
+ //catch (ThreadAbortException ex)
+ //{
+ // //This will occur if the instructions processing is taking too long since this is occuring on a request thread.
+ // // Or possibly if IIS terminates the appdomain. In any case, we should deal with this differently perhaps...
+ //}
+ catch (Exception ex)
+ {
+ _logger.Error(
+ string.Format("DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions (id: {0}, instruction count: {1}). Instruction is being skipped/ignored", dto.Id, instructionBatch.Count), ex);
+
+ //we cannot throw here because this invalid instruction will just keep getting processed over and over and errors
+ // will be thrown over and over. The only thing we can do is ignore and move on.
+ return dto.Id;
+ }
+
+ ////if this is returned it will not be saved
+ //return -1;
+ }
+
///
/// Remove old instructions from the database
///
@@ -459,8 +523,14 @@ namespace Umbraco.Core.Sync
return jsonRefresher;
}
- private static void NotifyRefreshers(IEnumerable jsonArray)
+ ///
+ /// Parses out the individual instructions to be processed
+ ///
+ ///
+ ///
+ private static List GetAllInstructions(IEnumerable jsonArray)
{
+ var result = new List();
foreach (var jsonItem in jsonArray)
{
// could be a JObject in which case we can convert to a RefreshInstruction,
@@ -469,33 +539,45 @@ namespace Umbraco.Core.Sync
if (jsonObj != null)
{
var instruction = jsonObj.ToObject();
- switch (instruction.RefreshType)
- {
- case RefreshMethodType.RefreshAll:
- RefreshAll(instruction.RefresherId);
- break;
- case RefreshMethodType.RefreshByGuid:
- RefreshByGuid(instruction.RefresherId, instruction.GuidId);
- break;
- case RefreshMethodType.RefreshById:
- RefreshById(instruction.RefresherId, instruction.IntId);
- break;
- case RefreshMethodType.RefreshByIds:
- RefreshByIds(instruction.RefresherId, instruction.JsonIds);
- break;
- case RefreshMethodType.RefreshByJson:
- RefreshByJson(instruction.RefresherId, instruction.JsonPayload);
- break;
- case RefreshMethodType.RemoveById:
- RemoveById(instruction.RefresherId, instruction.IntId);
- break;
- }
-
+ result.Add(instruction);
}
else
{
- var jsonInnerArray = (JArray) jsonItem;
- NotifyRefreshers(jsonInnerArray); // recurse
+ var jsonInnerArray = (JArray)jsonItem;
+ result.AddRange(GetAllInstructions(jsonInnerArray)); // recurse
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// executes the instructions against the cache refresher instances
+ ///
+ ///
+ private static void NotifyRefreshers(IEnumerable instructions)
+ {
+ foreach (var instruction in instructions)
+ {
+ switch (instruction.RefreshType)
+ {
+ case RefreshMethodType.RefreshAll:
+ RefreshAll(instruction.RefresherId);
+ break;
+ case RefreshMethodType.RefreshByGuid:
+ RefreshByGuid(instruction.RefresherId, instruction.GuidId);
+ break;
+ case RefreshMethodType.RefreshById:
+ RefreshById(instruction.RefresherId, instruction.IntId);
+ break;
+ case RefreshMethodType.RefreshByIds:
+ RefreshByIds(instruction.RefresherId, instruction.JsonIds);
+ break;
+ case RefreshMethodType.RefreshByJson:
+ RefreshByJson(instruction.RefresherId, instruction.JsonPayload);
+ break;
+ case RefreshMethodType.RemoveById:
+ RemoveById(instruction.RefresherId, instruction.IntId);
+ break;
}
}
}
From 68e871d175e3009ffb71a756386514ba01e3ef2f Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 25 Oct 2016 16:01:55 +0200
Subject: [PATCH 27/48] Ensures that any given JSON blob written to the
instructions can only contain the maximum number of instructions to process
---
.../Sync/DatabaseServerMessenger.cs | 30 +++++++++----------
.../BatchedDatabaseServerMessenger.cs | 22 +++++++++++---
2 files changed, 33 insertions(+), 19 deletions(-)
diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
index e8d5287edc..171635b8ab 100644
--- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
+++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
@@ -28,8 +28,7 @@ namespace Umbraco.Core.Sync
//
public class DatabaseServerMessenger : ServerMessengerBase
{
- private readonly ApplicationContext _appContext;
- private readonly DatabaseServerMessengerOptions _options;
+ private readonly ApplicationContext _appContext;
private readonly ManualResetEvent _syncIdle;
private readonly object _locko = new object();
private readonly ILogger _logger;
@@ -41,6 +40,7 @@ namespace Umbraco.Core.Sync
private bool _released;
private readonly ProfilingLogger _profilingLogger;
+ protected DatabaseServerMessengerOptions Options { get; private set; }
protected ApplicationContext ApplicationContext { get { return _appContext; } }
public DatabaseServerMessenger(ApplicationContext appContext, bool distributedEnabled, DatabaseServerMessengerOptions options)
@@ -50,7 +50,7 @@ namespace Umbraco.Core.Sync
if (options == null) throw new ArgumentNullException("options");
_appContext = appContext;
- _options = options;
+ Options = options;
_lastPruned = _lastSync = DateTime.UtcNow;
_syncIdle = new ManualResetEvent(true);
_profilingLogger = appContext.ProfilingLogger;
@@ -159,13 +159,13 @@ namespace Umbraco.Core.Sync
{
//check for how many instructions there are to process
var count = _appContext.DatabaseContext.Database.ExecuteScalar("SELECT COUNT(*) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId});
- if (count > _options.MaxProcessingInstructionCount)
+ if (count > Options.MaxProcessingInstructionCount)
{
//too many instructions, proceed to cold boot
_logger.Warn("The instruction count ({0}) exceeds the specified MaxProcessingInstructionCount ({1})."
+ " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id"
+ " to the latest found in the database and maintain cache updates based on that Id.",
- () => count, () => _options.MaxProcessingInstructionCount);
+ () => count, () => Options.MaxProcessingInstructionCount);
coldboot = true;
}
@@ -183,8 +183,8 @@ namespace Umbraco.Core.Sync
SaveLastSynced(maxId);
// execute initializing callbacks
- if (_options.InitializingCallbacks != null)
- foreach (var callback in _options.InitializingCallbacks)
+ if (Options.InitializingCallbacks != null)
+ foreach (var callback in Options.InitializingCallbacks)
callback();
}
@@ -206,7 +206,7 @@ namespace Umbraco.Core.Sync
if (_released)
return;
- if ((DateTime.UtcNow - _lastSync).TotalSeconds <= _options.ThrottleSeconds)
+ if ((DateTime.UtcNow - _lastSync).TotalSeconds <= Options.ThrottleSeconds)
return;
//Set our flag and the lock to be in it's original state (i.e. it can be awaited)
@@ -222,7 +222,7 @@ namespace Umbraco.Core.Sync
ProcessDatabaseInstructions();
//Check for pruning throttling
- if ((DateTime.UtcNow - _lastPruned).TotalSeconds <= _options.PruneThrottleSeconds)
+ if ((DateTime.UtcNow - _lastPruned).TotalSeconds <= Options.PruneThrottleSeconds)
return;
_lastPruned = _lastSync;
@@ -270,7 +270,7 @@ namespace Umbraco.Core.Sync
.OrderBy(dto => dto.Id, _appContext.DatabaseContext.SqlSyntax);
//only retrieve the max (just in case there's tons)
- var topSql = _appContext.DatabaseContext.SqlSyntax.SelectTop(sql, _options.MaxProcessingInstructionCount);
+ var topSql = _appContext.DatabaseContext.SqlSyntax.SelectTop(sql, Options.MaxProcessingInstructionCount);
// only process instructions coming from a remote server, and ignore instructions coming from
// the local server as they've already been processed. We should NOT assume that the sequence of
@@ -314,7 +314,7 @@ namespace Umbraco.Core.Sync
// Here we should check if there's too many instructions, if there is we should split them and re-save the instructions entry with
// the trimmed instructions. We then don't update the lastsynced value so that this row is re-processed again but with only the remaining
// instructions in it.
- if (instructionBatch.Count > _options.MaxProcessingInstructionCount)
+ if (instructionBatch.Count > Options.MaxProcessingInstructionCount)
{
maxInstructions = new Tuple>(dto, instructionBatch);
break;
@@ -328,17 +328,17 @@ namespace Umbraco.Core.Sync
// just a part of it and then re-save the remaining to the same row so that another request can deal with the data.
if (maxInstructions != null)
{
- var remainingCount = maxInstructions.Item2.Count - _options.MaxProcessingInstructionCount;
+ var remainingCount = maxInstructions.Item2.Count - Options.MaxProcessingInstructionCount;
_logger.Info(
"Max processing instruction count reached. This batch will be processed now but the remaining {0} will be processed by subsequent requests.", () => remainingCount);
- var processingBatch = maxInstructions.Item2.GetRange(0, _options.MaxProcessingInstructionCount);
+ var processingBatch = maxInstructions.Item2.GetRange(0, Options.MaxProcessingInstructionCount);
//NOTE: We are not persisting the lastId from the result of this method because we will need to re-process it
ProcessDatabaseInstructions(processingBatch, maxInstructions.Item1);
//Save the instruction blob back to the DB with the trimmed instruction count
- var remaining = maxInstructions.Item2.GetRange(_options.MaxProcessingInstructionCount - 1, remainingCount);
+ var remaining = maxInstructions.Item2.GetRange(Options.MaxProcessingInstructionCount - 1, remainingCount);
maxInstructions.Item1.UtcStamp = DateTime.UtcNow;
//serialize the remaining instructions (leave the original identity as-is)
maxInstructions.Item1.Instructions = JsonConvert.SerializeObject(remaining, Formatting.None);
@@ -386,7 +386,7 @@ namespace Umbraco.Core.Sync
///
private void PruneOldInstructions()
{
- var pruneDate = DateTime.UtcNow.AddDays(-_options.DaysToRetainInstructions);
+ var pruneDate = DateTime.UtcNow.AddDays(-Options.DaysToRetainInstructions);
// using 2 queries is faster than convoluted joins
diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs
index 730efd380b..f06a7a332f 100644
--- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs
+++ b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs
@@ -84,11 +84,16 @@ namespace Umbraco.Web
var instructions = batch.SelectMany(x => x.Instructions).ToArray();
batch.Clear();
- if (instructions.Length == 0) return;
- WriteInstructions(instructions);
+
+ //Write the instructions but only create JSON blobs with a max instruction count equal to MaxProcessingInstructionCount
+ foreach (var instructionsBatch in instructions.InGroupsOf(Options.MaxProcessingInstructionCount))
+ {
+ WriteInstructions(instructionsBatch);
+ }
+
}
- private void WriteInstructions(RefreshInstruction[] instructions)
+ private void WriteInstructions(IEnumerable instructions)
{
var dto = new CacheInstructionDto
{
@@ -136,9 +141,18 @@ namespace Umbraco.Web
// batch if we can, else write to DB immediately
if (batch == null)
- WriteInstructions(instructions.ToArray());
+ {
+ //only write the json blob with a maximum count of the MaxProcessingInstructionCount
+ foreach (var maxBatch in instructions.InGroupsOf(Options.MaxProcessingInstructionCount))
+ {
+ WriteInstructions(maxBatch);
+ }
+ }
else
+ {
batch.Add(new RefreshInstructionEnvelope(servers, refresher, instructions));
+ }
+
}
}
}
\ No newline at end of file
From 876d78b4adf1d7587ece149e50879c461764ad6f Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 25 Oct 2016 16:23:49 +0200
Subject: [PATCH 28/48] During instruction processing, always check if the app
is shutting down and if so then exit, if it's in the middle of a batch it
will exit even if it's processed a few and will not save the last synced id
meaning the batch will get processed again .Reverts the change to process
only a portion of the instructions json blob and save back to the db - that
won't work in LB scenarios.
---
.../Sync/DatabaseServerMessenger.cs | 83 ++++++++++---------
1 file changed, 42 insertions(+), 41 deletions(-)
diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
index 171635b8ab..b6dda18cb7 100644
--- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
+++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
@@ -278,17 +278,19 @@ namespace Umbraco.Core.Sync
var localIdentity = LocalIdentity;
var lastId = 0;
-
- //this is used to determine if we need to exit the reader loop below because there are actually
- // too many instructions to process. In which case we need to exit the reader so we can actually re-save
- // the remaining instructions back to the same row (we cannot save something while inside a reader loop)
- Tuple> maxInstructions = null;
-
+
//IMPORTANT! We are doing a Query here instead of a Fetch, this means that it will open a data reader
// which we are iterating over instead of loading everything into memory and iterating over that.
// When doing this we always must use a for loop so that the Enumerator is disposed and the reader is closed.
foreach (var dto in _appContext.DatabaseContext.Database.Query(topSql))
{
+ //If this flag gets set it means we're shutting down! In this case, we need to exit asap and cannot
+ // continue processing anything otherwise we'll hold up the app domain shutdown
+ if (_released)
+ {
+ break;
+ }
+
if (dto.OriginIdentity == localIdentity)
{
// just skip that local one but update lastId nevertheless
@@ -311,51 +313,39 @@ namespace Umbraco.Core.Sync
var instructionBatch = GetAllInstructions(jsonA);
- // Here we should check if there's too many instructions, if there is we should split them and re-save the instructions entry with
- // the trimmed instructions. We then don't update the lastsynced value so that this row is re-processed again but with only the remaining
- // instructions in it.
- if (instructionBatch.Count > Options.MaxProcessingInstructionCount)
- {
- maxInstructions = new Tuple>(dto, instructionBatch);
- break;
- }
-
//process as per-normal
- lastId = ProcessDatabaseInstructions(instructionBatch, dto);
- }
+ var success = ProcessDatabaseInstructions(instructionBatch, dto, ref lastId);
- //If this is not null this means we've found a row that has a ton of instructions in it and we'll need to process
- // just a part of it and then re-save the remaining to the same row so that another request can deal with the data.
- if (maxInstructions != null)
- {
- var remainingCount = maxInstructions.Item2.Count - Options.MaxProcessingInstructionCount;
-
- _logger.Info(
- "Max processing instruction count reached. This batch will be processed now but the remaining {0} will be processed by subsequent requests.", () => remainingCount);
-
- var processingBatch = maxInstructions.Item2.GetRange(0, Options.MaxProcessingInstructionCount);
- //NOTE: We are not persisting the lastId from the result of this method because we will need to re-process it
- ProcessDatabaseInstructions(processingBatch, maxInstructions.Item1);
-
- //Save the instruction blob back to the DB with the trimmed instruction count
- var remaining = maxInstructions.Item2.GetRange(Options.MaxProcessingInstructionCount - 1, remainingCount);
- maxInstructions.Item1.UtcStamp = DateTime.UtcNow;
- //serialize the remaining instructions (leave the original identity as-is)
- maxInstructions.Item1.Instructions = JsonConvert.SerializeObject(remaining, Formatting.None);
- ApplicationContext.DatabaseContext.Database.Update(maxInstructions.Item1);
+ //if they couldn't be all processed (i.e. we're shutting down) then exit
+ if (success == false)
+ break;
}
if (lastId > 0)
SaveLastSynced(lastId);
}
- private int ProcessDatabaseInstructions(List instructionBatch, CacheInstructionDto dto)
+ ///
+ /// Processes the instruction batch and checks for errors
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// returns true if all instructions in the batch were processed, otherwise false if they could not be due to the app being shut down
+ ///
+ private bool ProcessDatabaseInstructions(IReadOnlyCollection instructionBatch, CacheInstructionDto dto, ref int lastId)
{
// execute remote instructions & update lastId
try
{
- NotifyRefreshers(instructionBatch);
- return dto.Id;
+ var result = NotifyRefreshers(instructionBatch);
+ if (result)
+ {
+ //if all instructions we're processed, set the last id
+ lastId = dto.Id;
+ }
+ return result;
}
//catch (ThreadAbortException ex)
//{
@@ -369,7 +359,8 @@ namespace Umbraco.Core.Sync
//we cannot throw here because this invalid instruction will just keep getting processed over and over and errors
// will be thrown over and over. The only thing we can do is ignore and move on.
- return dto.Id;
+ lastId = dto.Id;
+ return false;
}
////if this is returned it will not be saved
@@ -554,10 +545,19 @@ namespace Umbraco.Core.Sync
/// executes the instructions against the cache refresher instances
///
///
- private static void NotifyRefreshers(IEnumerable instructions)
+ ///
+ /// Returns true if all instructions were processed, otherwise false if the processing was interupted (i.e. app shutdown)
+ ///
+ private bool NotifyRefreshers(IEnumerable instructions)
{
foreach (var instruction in instructions)
{
+ //Check if the app is shutting down, we need to exit if this happens.
+ if (_released)
+ {
+ return false;
+ }
+
switch (instruction.RefreshType)
{
case RefreshMethodType.RefreshAll:
@@ -580,6 +580,7 @@ namespace Umbraco.Core.Sync
break;
}
}
+ return true;
}
private static void RefreshAll(Guid uniqueIdentifier)
From f153332ad5e49425b18b07b4431e4486e6663391 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 25 Oct 2016 16:56:55 +0200
Subject: [PATCH 29/48] updates logging levels for MainDom
---
src/Umbraco.Core/MainDom.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs
index 6f4a539194..49d4b9b36a 100644
--- a/src/Umbraco.Core/MainDom.cs
+++ b/src/Umbraco.Core/MainDom.cs
@@ -118,7 +118,7 @@ namespace Umbraco.Core
// in any case...
_isMainDom = false;
_asyncLocker.Dispose();
- _logger.Debug("Released MainDom.");
+ _logger.Info("Released MainDom.");
}
}
@@ -131,11 +131,11 @@ namespace Umbraco.Core
// the handler is not installed so that would be the hosting environment
if (_signaled)
{
- _logger.Debug("Cannot acquire MainDom (signaled).");
+ _logger.Info("Cannot acquire MainDom (signaled).");
return false;
}
- _logger.Debug("Acquiring MainDom...");
+ _logger.Info("Acquiring MainDom...");
// signal other instances that we want the lock, then wait one the lock,
// which may timeout, and this is accepted - see comments below
@@ -162,7 +162,7 @@ namespace Umbraco.Core
HostingEnvironment.RegisterObject(this);
- _logger.Debug("Acquired MainDom.");
+ _logger.Info("Acquired MainDom.");
return true;
}
}
From db7c20d99a932943d5b058aff5478b015769a73c Mon Sep 17 00:00:00 2001
From: Claus
Date: Wed, 26 Oct 2016 11:32:25 +0200
Subject: [PATCH 30/48] fixing unit test failing on da-DK culture sorting.
---
.../Persistence/Repositories/ContentTypeRepositoryTest.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs
index 7d8d7659c8..84cdef73e1 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs
@@ -539,7 +539,7 @@ namespace Umbraco.Tests.Persistence.Repositories
using (var repository = CreateRepository(unitOfWork))
{
var contentType = repository.Get(NodeDto.NodeIdSeed + 1);
- var child1 = MockedContentTypes.CreateSimpleContentType("aabc", "aabc", contentType, randomizeAliases: true);
+ var child1 = MockedContentTypes.CreateSimpleContentType("abc", "abc", contentType, randomizeAliases: true);
repository.AddOrUpdate(child1);
var child3 = MockedContentTypes.CreateSimpleContentType("zyx", "zyx", contentType, randomizeAliases: true);
repository.AddOrUpdate(child3);
@@ -553,7 +553,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
Assert.That(contentTypes.Count(), Is.EqualTo(3));
Assert.AreEqual("a123", contentTypes.ElementAt(0).Name);
- Assert.AreEqual("aabc", contentTypes.ElementAt(1).Name);
+ Assert.AreEqual("abc", contentTypes.ElementAt(1).Name);
Assert.AreEqual("zyx", contentTypes.ElementAt(2).Name);
}
From 8b411c3d4662140b41e2aa099fd27780ffd01180 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Wed, 26 Oct 2016 11:37:59 +0200
Subject: [PATCH 31/48] Adds checks to see if the app is shutting down while
processing instruction batches, if so it will exit instruction processing.
Also set a timeout on the wait lock so that if the app is shutting down it
will after 5 seconds.
---
.../Sync/DatabaseServerMessenger.cs | 41 ++++++++++++++-----
1 file changed, 30 insertions(+), 11 deletions(-)
diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
index b6dda18cb7..ccd0a7b71d 100644
--- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
+++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs
@@ -116,10 +116,13 @@ namespace Umbraco.Core.Sync
_released = true; // no more syncs
}
- // Wait for pending sync this is waiting for _syncIdle.Set()
- // to be called. Until that is called, the appdomain cannot shut down!
- // so whatever is locking this currently should hurry up!
- _syncIdle.WaitOne();
+ //only wait 5 seconds
+ var result =_syncIdle.WaitOne(5000);
+ if (result == false)
+ {
+ //a timeout occurred :/
+ _logger.Warn("The wait lock timed out, application is shutting down. The current instruction batch will be re-processed.");
+ }
},
weight);
@@ -222,7 +225,7 @@ namespace Umbraco.Core.Sync
ProcessDatabaseInstructions();
//Check for pruning throttling
- if ((DateTime.UtcNow - _lastPruned).TotalSeconds <= Options.PruneThrottleSeconds)
+ if ((_released || (DateTime.UtcNow - _lastPruned).TotalSeconds <= Options.PruneThrottleSeconds))
return;
_lastPruned = _lastSync;
@@ -238,8 +241,12 @@ namespace Umbraco.Core.Sync
}
finally
{
- //We must reset our flag and signal any waiting locks
- _syncing = false;
+ lock (_locko)
+ {
+ //We must reset our flag and signal any waiting locks
+ _syncing = false;
+ }
+
_syncIdle.Set();
}
}
@@ -279,10 +286,10 @@ namespace Umbraco.Core.Sync
var lastId = 0;
- //IMPORTANT! We are doing a Query here instead of a Fetch, this means that it will open a data reader
- // which we are iterating over instead of loading everything into memory and iterating over that.
- // When doing this we always must use a for loop so that the Enumerator is disposed and the reader is closed.
- foreach (var dto in _appContext.DatabaseContext.Database.Query(topSql))
+ //It would have been nice to do this in a Query instead of Fetch using a data reader to save
+ // some memory however we cannot do thta because inside of this loop the cache refreshers are also
+ // performing some lookups which cannot be done with an active reader open
+ foreach (var dto in _appContext.DatabaseContext.Database.Fetch(topSql))
{
//If this flag gets set it means we're shutting down! In this case, we need to exit asap and cannot
// continue processing anything otherwise we'll hold up the app domain shutdown
@@ -318,7 +325,11 @@ namespace Umbraco.Core.Sync
//if they couldn't be all processed (i.e. we're shutting down) then exit
if (success == false)
+ {
+ _logger.Info("The current batch of instructions was not processed, app is shutting down");
break;
+ }
+
}
if (lastId > 0)
@@ -550,6 +561,8 @@ namespace Umbraco.Core.Sync
///
private bool NotifyRefreshers(IEnumerable instructions)
{
+ var processed = new HashSet