new table designer features (#1420)

* support graph table types

* more fixes

* refresh view upon edit

* support temporal tables

* temporal and memory table

* primary key and default constraint

* bug fixes

* dispose table designer

* vbump dacfx

* update string
This commit is contained in:
Alan Ren
2022-03-08 18:52:51 -08:00
committed by GitHub
parent 3e5bf00cc5
commit 7a326b2487
17 changed files with 1896 additions and 89 deletions

View File

@@ -14,6 +14,17 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
public const string ForeignKeys = "foreignKeys";
public const string CheckConstraints = "checkConstraints";
public const string Indexes = "indexes";
public const string EdgeConstraints = "edgeConstraints";
public const string GraphTableType = "graphTableType";
public const string IsSystemVersioningEnabled = "isSystemVersioningEnabled";
public const string AutoCreateHistoryTable = "autoCreateHistoryTable";
public const string NewHistoryTableTable = "newHistoryTableName";
public const string ExistingHistoryTableName = "existingHistoryTable";
public const string IsMemoryOptimized = "isMemoryOptimized";
public const string Durability = "durability";
public const string PrimaryKeyName = "primaryKeyName";
public const string PrimaryKeyIsClustered = "primaryKeyIsClustered";
public const string PrimaryKeyColumns = "primaryKeyColumns";
}
public static class TableColumnPropertyNames
@@ -29,6 +40,10 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
public const string IsIdentity = "isIdentity";
public const string IdentityIncrement = "identityIncrement";
public const string IdentitySeed = "identitySeed";
public const string CanBeDeleted = "canBeDeleted";
public const string GeneratedAlwaysAs = "generatedAlwaysAs";
public const string IsHidden = "isHidden";
public const string DefaultConstraintName = "defaultConstraintName";
}
public static class ForeignKeyPropertyNames
@@ -70,4 +85,19 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
public const string Column = "column";
public const string Ascending = "ascending";
}
public static class EdgeConstraintPropertyNames
{
public const string Name = "name";
public const string Enabled = "enabled";
public const string Clauses = "clauses";
public const string OnDeleteAction = "onDeleteAction";
public const string ClausesDisplayValue = "clausesDisplayValue";
}
public static class EdgeConstraintClausePropertyNames
{
public const string FromTable = "fromTable";
public const string ToTable = "toTable";
}
}

View File

@@ -14,7 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
/// <summary>
/// The column names to be displayed
/// </summary>
public string[] Columns { get; set; }
public List<string> Columns { get; set; } = new List<string>();
/// <summary>
/// The object type display name of the objects in this table
@@ -30,5 +30,25 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
/// The object list.
/// </summary>
public List<T> Data { get; set; } = new List<T>();
/// <summary>
/// Whether new rows can be added.
/// </summary>
public bool CanAddRows { get; set; } = true;
/// <summary>
/// Whether rows can be deleted.
/// </summary>
public bool CanRemoveRows { get; set; } = true;
/// <summary>
/// Whether a confirmation should be shown when a row is about to be removed.
/// </summary>
public bool ShowRemoveRowConfirmation { get; set; } = false;
/// <summary>
/// The confirmation message to be displayed when a row is about to be removed.
/// </summary>
public string RemoveRowConfirmationMessage { get; set; }
}
}

View File

@@ -19,6 +19,8 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
{
public TableViewModel ViewModel { get; set; }
public TableDesignerView View { get; set; }
public bool IsValid { get; set; }
public TableDesignerValidationError[] Errors { get; set; }

View File

@@ -11,6 +11,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
{
public TableInfo NewTableInfo;
public TableViewModel ViewModel;
public TableDesignerView View;
}
/// <summary>

View File

@@ -16,9 +16,13 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
public List<DesignerDataPropertyInfo> AdditionalTableProperties { get; set; } = new List<DesignerDataPropertyInfo>();
public BuiltinTableOptions ColumnTableOptions { get; set; } = new BuiltinTableOptions();
public BuiltinTableOptions ForeignKeyTableOptions { get; set; } = new BuiltinTableOptions();
public BuiltinTableOptions ForeignKeyColumnMappingTableOptions { get; set; } = new BuiltinTableOptions();
public BuiltinTableOptions CheckConstraintTableOptions { get; set; } = new BuiltinTableOptions();
public BuiltinTableOptions IndexTableOptions { get; set; } = new BuiltinTableOptions();
public BuiltinTableOptions IndexColumnSpecificationTableOptions { get; set; } = new BuiltinTableOptions();
public List<DesignerDataPropertyInfo> AdditionalPrimaryKeyProperties { get; set; } = new List<DesignerDataPropertyInfo>();
public BuiltinTableOptions PrimaryKeyColumnSpecificationTableOptions = new BuiltinTableOptions();
public List<DesignerTabView> AdditionalTabs { get; } = new List<DesignerTabView>();
}
public class BuiltinTableOptions
@@ -29,6 +33,12 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
public bool CanRemoveRows { get; set; } = true;
public List<DesignerDataPropertyInfo> AdditionalProperties { get; set; } = new List<DesignerDataPropertyInfo>();
public string RemoveRowConfirmationMessage { get; set; }
public bool ShowRemoveRowConfirmation { get; set;} = false;
public bool ShowRemoveRowConfirmation { get; set; } = false;
}
public class DesignerTabView
{
public string Title { get; set; }
public List<DesignerDataPropertyInfo> Components { get; } = new List<DesignerDataPropertyInfo>();
}
}

View File

@@ -0,0 +1,28 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
{
/// <summary>
/// The view model of edge constraint.
/// </summary>
public class EdgeConstraintViewModel : ObjectViewModelBase
{
public CheckBoxProperties Enabled { get; set; } = new CheckBoxProperties();
public DropdownProperties OnDeleteAction { get; set; } = new DropdownProperties();
public TableComponentProperties<EdgeConstraintClause> Clauses { get; set; } = new TableComponentProperties<EdgeConstraintClause>();
public InputBoxProperties ClausesDisplayValue { get; set; } = new InputBoxProperties();
}
public class EdgeConstraintClause
{
public DropdownProperties FromTable { get; set; } = new DropdownProperties();
public DropdownProperties ToTable { get; set; } = new DropdownProperties();
}
}

View File

@@ -29,5 +29,13 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
public InputBoxProperties IdentitySeed { get; set; } = new InputBoxProperties();
public InputBoxProperties IdentityIncrement { get; set; } = new InputBoxProperties();
public DropdownProperties GeneratedAlwaysAs { get; set; } = new DropdownProperties();
public CheckBoxProperties IsHidden { get; set; } = new CheckBoxProperties();
public InputBoxProperties DefaultConstraintName { get; set; } = new InputBoxProperties();
public bool CanBeDeleted { get; set; } = true;
}
}

View File

@@ -14,12 +14,34 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
public InputBoxProperties Description { get; set; } = new InputBoxProperties();
public DropdownProperties GraphTableType { get; set; } = new DropdownProperties();
public CheckBoxProperties IsMemoryOptimized { get; set; } = new CheckBoxProperties();
public DropdownProperties Durability { get; set; } = new DropdownProperties();
public CheckBoxProperties IsSystemVersioningEnabled { get; set; } = new CheckBoxProperties();
public DropdownProperties ExistingHistoryTable { get; set; } = new DropdownProperties();
public CheckBoxProperties AutoCreateHistoryTable { get; set; } = new CheckBoxProperties();
public InputBoxProperties NewHistoryTableName { get; set; } = new InputBoxProperties();
public InputBoxProperties PrimaryKeyName { get; set; } = new InputBoxProperties();
public CheckBoxProperties PrimaryKeyIsClustered { get; set; } = new CheckBoxProperties();
public TableComponentProperties<IndexedColumnSpecification> PrimaryKeyColumns { get; set; } = new TableComponentProperties<IndexedColumnSpecification>();
public TableComponentProperties<TableColumnViewModel> Columns { get; set; } = new TableComponentProperties<TableColumnViewModel>();
public TableComponentProperties<ForeignKeyViewModel> ForeignKeys { get; set; } = new TableComponentProperties<ForeignKeyViewModel>();
public TableComponentProperties<CheckConstraintViewModel> CheckConstraints { get; set; } = new TableComponentProperties<CheckConstraintViewModel>();
public TableComponentProperties<EdgeConstraintViewModel> EdgeConstraints { get; set; } = new TableComponentProperties<EdgeConstraintViewModel>();
public TableComponentProperties<IndexViewModel> Indexes { get; set; } = new TableComponentProperties<IndexViewModel>();
public InputBoxProperties Script { get; set; } = new InputBoxProperties();

View File

@@ -0,0 +1,93 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Data.Tools.Sql.DesignServices.TableDesigner;
namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
public abstract class EnumUtil<T> where T : struct, IConvertible
{
protected Dictionary<string, T> Mapping = new Dictionary<string, T>();
public List<string> DisplayNames
{
get
{
return this.Mapping.Keys.ToList();
}
}
public string GetName(T enumValue)
{
foreach (var key in this.Mapping.Keys)
{
if (this.Mapping[key].Equals(enumValue))
{
return key;
}
}
throw new KeyNotFoundException(SR.UnknownEnumString(enumValue.ToString()));
}
public T GetValue(string displayName)
{
if (this.Mapping.ContainsKey(displayName))
{
return this.Mapping[displayName];
}
else
{
throw new KeyNotFoundException(SR.UnknownEnumString(displayName));
}
}
}
public class SqlForeignKeyActionUtil : EnumUtil<SqlForeignKeyAction>
{
public static SqlForeignKeyActionUtil Instance { get; } = new SqlForeignKeyActionUtil();
public SqlForeignKeyActionUtil()
{
this.Mapping.Add(SR.SqlForeignKeyAction_NoAction, SqlForeignKeyAction.NoAction);
this.Mapping.Add(SR.SqlForeignKeyAction_Cascade, SqlForeignKeyAction.Cascade);
this.Mapping.Add(SR.SqlForeignKeyAction_SetNull, SqlForeignKeyAction.SetNull);
this.Mapping.Add(SR.SqlForeignKeyAction_SetDefault, SqlForeignKeyAction.SetDefault);
}
public List<string> EdgeConstraintOnDeleteActionNames
{
get
{
return new List<string> { SR.SqlForeignKeyAction_NoAction, SR.SqlForeignKeyAction_Cascade };
}
}
}
public class SqlTableDurabilityUtil : EnumUtil<TableDurability>
{
public static SqlTableDurabilityUtil Instance { get; } = new SqlTableDurabilityUtil();
public SqlTableDurabilityUtil()
{
this.Mapping.Add(SR.SqlTableDurability_SchemaAndData, TableDurability.SchemaAndData);
this.Mapping.Add(SR.SqlTableDurability_SchemaOnly, TableDurability.SchemaOnly);
}
}
public class ColumnGeneratedAlwaysAsTypeUtil : EnumUtil<ColumnGeneratedAlwaysAsType>
{
public static ColumnGeneratedAlwaysAsTypeUtil Instance { get; } = new ColumnGeneratedAlwaysAsTypeUtil();
public ColumnGeneratedAlwaysAsTypeUtil()
{
this.Mapping.Add(SR.GeneratedAlwaysColumnType_None, ColumnGeneratedAlwaysAsType.None);
this.Mapping.Add(SR.GeneratedAlwaysColumnType_RowStart, ColumnGeneratedAlwaysAsType.GeneratedAlwaysAsRowStart);
this.Mapping.Add(SR.GeneratedAlwaysColumnType_RowEnd, ColumnGeneratedAlwaysAsType.GeneratedAlwaysAsRowEnd);
}
}
}

View File

@@ -1,56 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Data.Tools.Sql.DesignServices.TableDesigner;
namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
public static class SqlForeignKeyActionUtil
{
private static Dictionary<string, SqlForeignKeyAction> mapping = new Dictionary<string, SqlForeignKeyAction>();
static SqlForeignKeyActionUtil()
{
mapping.Add(SR.SqlForeignKeyAction_NoAction, SqlForeignKeyAction.NoAction);
mapping.Add(SR.SqlForeignKeyAction_Cascade, SqlForeignKeyAction.Cascade);
mapping.Add(SR.SqlForeignKeyAction_SetNull, SqlForeignKeyAction.SetNull);
mapping.Add(SR.SqlForeignKeyAction_SetDefault, SqlForeignKeyAction.SetDefault);
}
public static List<string> ActionNames
{
get
{
return mapping.Keys.ToList();
}
}
public static string GetName(SqlForeignKeyAction action)
{
foreach (var key in mapping.Keys)
{
if (mapping[key] == action)
{
return key;
}
}
throw new NotSupportedException(SR.UnKnownSqlForeignKeyAction(action.ToString()));
}
public static SqlForeignKeyAction GetValue(string displayName)
{
if (mapping.ContainsKey(displayName))
{
return mapping[displayName];
}
else
{
throw new KeyNotFoundException(SR.UnKnownSqlForeignKeyAction(displayName));
}
}
}
}

View File

@@ -87,9 +87,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
await requestContext.SendResult(new TableDesignerInfo()
{
ViewModel = viewModel,
View = view,
ColumnTypes = tableDesigner.DataTypes.ToList(),
Schemas = tableDesigner.Schemas.ToList()
View = view
});
});
}
@@ -98,6 +96,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
return this.HandleRequest<ProcessTableDesignerEditResponse>(requestContext, async () =>
{
var refreshViewRequired = false;
DesignerPathUtils.Validate(requestParams.TableChangeInfo.Path, requestParams.TableChangeInfo.Type);
switch (requestParams.TableChangeInfo.Type)
{
@@ -108,7 +107,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
this.HandleRemoveItemRequest(requestParams);
break;
case DesignerEditType.Update:
this.HandleUpdateItemRequest(requestParams);
refreshViewRequired = this.HandleUpdateItemRequest(requestParams);
break;
default:
break;
@@ -119,7 +118,8 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
ViewModel = this.GetTableViewModel(requestParams.TableInfo),
IsValid = errors.Count == 0,
Errors = errors.ToArray()
Errors = errors.ToArray(),
View = refreshViewRequired ? this.GetDesignerViewInfo(requestParams.TableInfo) : null
});
});
}
@@ -146,7 +146,8 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
await requestContext.SendResult(new PublishTableChangesResponse()
{
NewTableInfo = tableInfo,
ViewModel = this.GetTableViewModel(tableInfo)
ViewModel = this.GetTableViewModel(tableInfo),
View = GetDesignerViewInfo(tableInfo)
});
});
}
@@ -179,6 +180,8 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
return this.HandleRequest<DisposeTableDesignerResponse>(requestContext, async () =>
{
var td = this.GetTableDesigner(tableInfo);
td.Dispose();
this.idTableMap.Remove(tableInfo.Id);
await requestContext.SendResult(new DisposeTableDesignerResponse());
});
@@ -206,6 +209,16 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
case TablePropertyNames.Indexes:
table.Indexes.AddNew();
break;
case TablePropertyNames.EdgeConstraints:
table.EdgeConstraints.AddNew();
break;
case TablePropertyNames.PrimaryKeyColumns:
if (table.PrimaryKey == null)
{
table.CreatePrimaryKey();
}
table.PrimaryKey.AddNewColumnSpecification();
break;
default:
break;
}
@@ -237,6 +250,16 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
break;
}
break;
case TablePropertyNames.EdgeConstraints:
switch (propertyNameL2)
{
case EdgeConstraintPropertyNames.Clauses:
table.EdgeConstraints.Items[indexL1].AddNewClause();
break;
default:
break;
}
break;
default:
break;
}
@@ -266,6 +289,12 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
case TablePropertyNames.Indexes:
table.Indexes.RemoveAt(objIndex);
break;
case TablePropertyNames.EdgeConstraints:
table.EdgeConstraints.RemoveAt(objIndex);
break;
case TablePropertyNames.PrimaryKeyColumns:
table.PrimaryKey.RemoveColumnSpecification(objIndex);
break;
default:
break;
}
@@ -298,15 +327,27 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
break;
}
break;
case TablePropertyNames.EdgeConstraints:
switch (propertyNameL2)
{
case EdgeConstraintPropertyNames.Clauses:
table.EdgeConstraints.Items[indexL1].RemoveClause(indexL2);
break;
default:
break;
}
break;
default:
break;
}
}
}
private void HandleUpdateItemRequest(ProcessTableDesignerEditRequestParams requestParams)
private bool HandleUpdateItemRequest(ProcessTableDesignerEditRequestParams requestParams)
{
var table = this.GetTableDesigner(requestParams.TableInfo).TableViewModel;
var refreshView = false;
var tableDesigner = this.GetTableDesigner(requestParams.TableInfo);
var table = tableDesigner.TableViewModel;
var path = requestParams.TableChangeInfo.Path;
var newValue = requestParams.TableChangeInfo.Value;
if (path.Length == 1)
@@ -323,6 +364,55 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
case TablePropertyNames.Schema:
table.Schema = GetStringValue(newValue);
break;
case TablePropertyNames.GraphTableType:
var wasEdgeTable = table.IsEdge;
table.IsEdge = false;
table.IsNode = false;
var newType = GetStringValue(newValue);
if (newType == SR.TableDesignerGraphTableTypeNode)
{
table.IsNode = true;
}
else if (newType == SR.TableDesignerGraphTableTypeEdge)
{
table.IsEdge = true;
}
refreshView = (wasEdgeTable || table.IsEdge) && tableDesigner.IsEdgeConstraintSupported;
break;
case TablePropertyNames.IsSystemVersioningEnabled:
table.IsSystemVersioningEnabled = GetBooleanValue(newValue);
refreshView = true;
break;
case TablePropertyNames.AutoCreateHistoryTable:
table.AutoCreateHistoryTable = GetBooleanValue(newValue);
refreshView = true;
break;
case TablePropertyNames.NewHistoryTableTable:
table.NewHistoryTableName = GetStringValue(newValue);
break;
case TablePropertyNames.ExistingHistoryTableName:
table.ExistingHistoryTable = GetStringValue(newValue);
break;
case TablePropertyNames.IsMemoryOptimized:
table.IsMemoryOptimized = GetBooleanValue(newValue);
refreshView = true;
break;
case TablePropertyNames.Durability:
table.Durability = SqlTableDurabilityUtil.Instance.GetValue(GetStringValue(newValue));
break;
case TablePropertyNames.PrimaryKeyName:
if (table.PrimaryKey != null)
{
table.PrimaryKey.Name = GetStringValue(newValue);
}
break;
case TablePropertyNames.PrimaryKeyIsClustered:
if (table.PrimaryKey != null)
{
table.PrimaryKey.IsClustered = GetBooleanValue(newValue);
}
break;
default:
break;
}
@@ -371,6 +461,15 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
case TableColumnPropertyNames.Type:
column.DataType = GetStringValue(newValue);
break;
case TableColumnPropertyNames.GeneratedAlwaysAs:
column.GeneratedAlwaysAs = ColumnGeneratedAlwaysAsTypeUtil.Instance.GetValue(GetStringValue(newValue));
break;
case TableColumnPropertyNames.IsHidden:
column.IsHidden = GetBooleanValue(newValue);
break;
case TableColumnPropertyNames.DefaultConstraintName:
column.DefaultConstraintName = GetStringValue(newValue);
break;
default:
break;
}
@@ -406,10 +505,10 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
foreignKey.Name = GetStringValue(newValue);
break;
case ForeignKeyPropertyNames.OnDeleteAction:
foreignKey.OnDeleteAction = SqlForeignKeyActionUtil.GetValue(GetStringValue(newValue));
foreignKey.OnDeleteAction = SqlForeignKeyActionUtil.Instance.GetValue(GetStringValue(newValue));
break;
case ForeignKeyPropertyNames.OnUpdateAction:
foreignKey.OnUpdateAction = SqlForeignKeyActionUtil.GetValue(GetStringValue(newValue));
foreignKey.OnUpdateAction = SqlForeignKeyActionUtil.Instance.GetValue(GetStringValue(newValue));
break;
case ForeignKeyPropertyNames.ForeignTable:
foreignKey.ForeignTable = GetStringValue(newValue);
@@ -438,6 +537,36 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
break;
}
break;
case TablePropertyNames.EdgeConstraints:
var constraint = table.EdgeConstraints.Items[indexL1];
switch (propertyNameL2)
{
case EdgeConstraintPropertyNames.Enabled:
constraint.Enabled = GetBooleanValue(newValue);
break;
case EdgeConstraintPropertyNames.Name:
constraint.Name = GetStringValue(newValue);
break;
case EdgeConstraintPropertyNames.OnDeleteAction:
constraint.OnDeleteAction = SqlForeignKeyActionUtil.Instance.GetValue(GetStringValue(newValue));
break;
default:
break;
}
break;
case TablePropertyNames.PrimaryKeyColumns:
switch (propertyNameL2)
{
case IndexColumnSpecificationPropertyNames.Column:
table.PrimaryKey.UpdateColumnName(indexL1, GetStringValue(newValue));
break;
case IndexColumnSpecificationPropertyNames.Ascending:
table.PrimaryKey.UpdateIsAscending(indexL1, GetBooleanValue(newValue));
break;
default:
break;
}
break;
default:
break;
}
@@ -493,10 +622,32 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
break;
}
break;
case TablePropertyNames.EdgeConstraints:
var constraint = table.EdgeConstraints.Items[indexL1];
switch (propertyNameL2)
{
case EdgeConstraintPropertyNames.Clauses:
switch (propertyNameL3)
{
case EdgeConstraintClausePropertyNames.FromTable:
constraint.UpdateFromTable(indexL2, GetStringValue(newValue));
break;
case EdgeConstraintClausePropertyNames.ToTable:
constraint.UpdateToTable(indexL2, GetStringValue(newValue));
break;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
}
return refreshView;
}
private int GetInt32Value(object value)
@@ -521,8 +672,48 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
var tableViewModel = new TableViewModel();
tableViewModel.Name.Value = table.Name;
tableViewModel.Schema.Value = table.Schema;
tableViewModel.Schema.Values = tableDesigner.Schemas.ToList();
tableViewModel.Description.Value = table.Description;
tableViewModel.Description.Enabled = false; // TODO: https://github.com/microsoft/azuredatastudio/issues/18247
var primaryKey = table.PrimaryKey;
tableViewModel.PrimaryKeyName.Enabled = primaryKey != null;
tableViewModel.PrimaryKeyIsClustered.Enabled = primaryKey != null;
if (primaryKey != null)
{
tableViewModel.PrimaryKeyName.Value = primaryKey.Name;
tableViewModel.PrimaryKeyIsClustered.Checked = primaryKey.IsClustered;
foreach (var cs in primaryKey.Columns)
{
var columnSpecVM = new IndexedColumnSpecification();
columnSpecVM.Ascending.Checked = cs.IsAscending;
columnSpecVM.Column.Value = cs.Column;
columnSpecVM.Column.Values = tableDesigner.GetColumnsForTable(table.FullName).ToList();
tableViewModel.PrimaryKeyColumns.Data.Add(columnSpecVM);
}
}
// Graph table related properties
tableViewModel.GraphTableType.Enabled = table.CanEditGraphTableType;
tableViewModel.GraphTableType.Values = new List<string>() { SR.TableDesignerGraphTableTypeNone, SR.TableDesignerGraphTableTypeEdge, SR.TableDesignerGraphTableTypeNode };
tableViewModel.GraphTableType.Value = (table.IsEdge || table.IsNode) ? (table.IsEdge ? SR.TableDesignerGraphTableTypeEdge : SR.TableDesignerGraphTableTypeNode) : SR.TableDesignerGraphTableTypeNone;
// Memory-optimized related properties
tableViewModel.IsMemoryOptimized.Checked = table.IsMemoryOptimized;
tableViewModel.IsMemoryOptimized.Enabled = table.CanEditIsMemoryOptimized;
tableViewModel.Durability.Enabled = table.CanEditDurability;
tableViewModel.Durability.Value = SqlTableDurabilityUtil.Instance.GetName(table.Durability);
tableViewModel.Durability.Values = SqlTableDurabilityUtil.Instance.DisplayNames;
// Temporal related properties
var isTemporalTable = table.SystemVersioningHistoryTable != null;
tableViewModel.IsSystemVersioningEnabled.Enabled = table.CanEditIsSystemVersioningEnabled;
tableViewModel.IsSystemVersioningEnabled.Checked = isTemporalTable;
tableViewModel.AutoCreateHistoryTable.Enabled = table.CanEditAutoCreateHistoryTable;
tableViewModel.AutoCreateHistoryTable.Checked = table.AutoCreateHistoryTable;
tableViewModel.NewHistoryTableName.Enabled = table.CanEditNewHistoryTableName;
tableViewModel.NewHistoryTableName.Value = table.NewHistoryTableName;
tableViewModel.ExistingHistoryTable.Enabled = table.CanEditExistingHistoryTable;
tableViewModel.ExistingHistoryTable.Value = table.SystemVersioningHistoryTable;
tableViewModel.ExistingHistoryTable.Values = table.ExistingHistoryTablePropertyOptionalValues.ToList();
foreach (var column in table.Columns.Items)
{
@@ -543,12 +734,21 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
columnViewModel.IsPrimaryKey.Enabled = true; // To be consistent with SSDT, any column can be a primary key.
columnViewModel.Type.Value = column.DataType;
columnViewModel.Type.Enabled = column.CanEditDataType;
columnViewModel.Type.Values = tableDesigner.DataTypes.ToList();
columnViewModel.IsIdentity.Enabled = column.CanEditIsIdentity;
columnViewModel.IsIdentity.Checked = column.IsIdentity;
columnViewModel.IdentitySeed.Enabled = column.CanEditIdentityValues;
columnViewModel.IdentitySeed.Value = column.IdentitySeed?.ToString();
columnViewModel.IdentityIncrement.Enabled = column.CanEditIdentityValues;
columnViewModel.IdentityIncrement.Value = column.IdentityIncrement?.ToString();
columnViewModel.CanBeDeleted = column.CanBeDeleted;
columnViewModel.GeneratedAlwaysAs.Value = ColumnGeneratedAlwaysAsTypeUtil.Instance.GetName(column.GeneratedAlwaysAs);
columnViewModel.GeneratedAlwaysAs.Values = ColumnGeneratedAlwaysAsTypeUtil.Instance.DisplayNames;
columnViewModel.GeneratedAlwaysAs.Enabled = tableDesigner.IsTemporalTableSupported;
columnViewModel.IsHidden.Checked = column.IsHidden;
columnViewModel.IsHidden.Enabled = column.CanEditIsHidden;
columnViewModel.DefaultConstraintName.Enabled = column.CanEditDefaultConstraintName;
columnViewModel.DefaultConstraintName.Value = column.DefaultConstraintName;
tableViewModel.Columns.Data.Add(columnViewModel);
}
@@ -557,10 +757,10 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
var foreignKeyViewModel = new ForeignKeyViewModel();
foreignKeyViewModel.Name.Value = foreignKey.Name;
foreignKeyViewModel.Enabled.Checked = foreignKey.Enabled;
foreignKeyViewModel.OnDeleteAction.Value = SqlForeignKeyActionUtil.GetName(foreignKey.OnDeleteAction);
foreignKeyViewModel.OnDeleteAction.Values = SqlForeignKeyActionUtil.ActionNames;
foreignKeyViewModel.OnUpdateAction.Value = SqlForeignKeyActionUtil.GetName(foreignKey.OnUpdateAction);
foreignKeyViewModel.OnUpdateAction.Values = SqlForeignKeyActionUtil.ActionNames;
foreignKeyViewModel.OnDeleteAction.Value = SqlForeignKeyActionUtil.Instance.GetName(foreignKey.OnDeleteAction);
foreignKeyViewModel.OnDeleteAction.Values = SqlForeignKeyActionUtil.Instance.DisplayNames;
foreignKeyViewModel.OnUpdateAction.Value = SqlForeignKeyActionUtil.Instance.GetName(foreignKey.OnUpdateAction);
foreignKeyViewModel.OnUpdateAction.Values = SqlForeignKeyActionUtil.Instance.DisplayNames;
foreignKeyViewModel.ForeignTable.Value = foreignKey.ForeignTable;
foreignKeyViewModel.ForeignTable.Values = tableDesigner.AllTables.ToList();
foreignKeyViewModel.IsNotForReplication.Checked = foreignKey.IsNotForReplication;
@@ -591,13 +791,14 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
var indexVM = new IndexViewModel();
indexVM.Name.Value = index.Name;
indexVM.Name.Enabled = tableInfo.IsNewTable; // renaming an index is not supported, it will cause a new index to be created.
indexVM.IsClustered.Checked = index.IsClustered;
indexVM.Enabled.Checked = index.Enabled;
indexVM.IsUnique.Checked = index.IsUnique;
foreach (var columnSpec in index.Columns)
{
var columnSpecVM = new IndexedColumnSpecification();
columnSpecVM.Ascending.Checked = columnSpec.isAscending;
columnSpecVM.Ascending.Checked = columnSpec.IsAscending;
columnSpecVM.Column.Value = columnSpec.Column;
columnSpecVM.Column.Values = tableDesigner.GetColumnsForTable(table.FullName).ToList();
indexVM.Columns.Data.Add(columnSpecVM);
@@ -606,6 +807,27 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
indexVM.ColumnsDisplayValue.Enabled = false;
tableViewModel.Indexes.Data.Add(indexVM);
}
foreach (var constraint in table.EdgeConstraints.Items)
{
var constraintVM = new EdgeConstraintViewModel();
constraintVM.Name.Value = constraint.Name;
constraintVM.Enabled.Checked = constraint.Enabled;
constraintVM.OnDeleteAction.Value = SqlForeignKeyActionUtil.Instance.GetName(constraint.OnDeleteAction);
constraintVM.OnDeleteAction.Values = SqlForeignKeyActionUtil.Instance.EdgeConstraintOnDeleteActionNames;
constraintVM.ClausesDisplayValue.Value = constraint.ClausesDisplayValue;
constraintVM.ClausesDisplayValue.Enabled = false;
foreach (var clause in constraint.Clauses)
{
var clauseVM = new EdgeConstraintClause();
clauseVM.FromTable.Value = clause.FromTable;
clauseVM.FromTable.Values = tableDesigner.AllNodeTables.ToList();
clauseVM.ToTable.Value = clause.ToTable;
clauseVM.ToTable.Values = tableDesigner.AllNodeTables.ToList();
constraintVM.Clauses.Data.Add(clauseVM);
}
tableViewModel.EdgeConstraints.Data.Add(constraintVM);
}
tableViewModel.Script.Enabled = false;
tableViewModel.Script.Value = tableDesigner.Script;
return tableViewModel;
@@ -613,14 +835,46 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
private TableDesignerView GetDesignerViewInfo(TableInfo tableInfo)
{
var tableDesigner = this.GetTableDesigner(tableInfo);
var view = new TableDesignerView();
this.SetPrimaryKeyViewInfo(view);
this.SetColumnsViewInfo(view);
this.SetForeignKeysViewInfo(view);
this.SetCheckConstraintsViewInfo(view);
this.SetIndexesViewInfo(view);
this.SetGraphTableViewInfo(view, tableDesigner);
this.SetEdgeConstraintsViewInfo(view, tableDesigner);
this.SetTemporalTableViewInfo(view, tableDesigner);
this.SetMemoryOptimizedTableViewInfo(view, tableDesigner);
return view;
}
private void SetPrimaryKeyViewInfo(TableDesignerView view)
{
view.AdditionalPrimaryKeyProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.PrimaryKeyIsClustered,
ComponentType = DesignerComponentType.Checkbox,
Description = SR.IndexIsClusteredPropertyDescription,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.TableDesignerIndexIsClusteredPropertyTitle
}
});
view.PrimaryKeyColumnSpecificationTableOptions.AdditionalProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = IndexColumnSpecificationPropertyNames.Ascending,
Description = SR.IndexColumnIsAscendingPropertyDescription,
ComponentType = DesignerComponentType.Checkbox,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.IndexColumnIsAscendingPropertyTitle
}
});
view.PrimaryKeyColumnSpecificationTableOptions.PropertiesToDisplay.Add(IndexColumnSpecificationPropertyNames.Column);
view.PrimaryKeyColumnSpecificationTableOptions.PropertiesToDisplay.Add(IndexColumnSpecificationPropertyNames.Ascending);
}
private void SetColumnsViewInfo(TableDesignerView view)
{
view.ColumnTableOptions.AdditionalProperties.AddRange(new DesignerDataPropertyInfo[] {
@@ -656,6 +910,16 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
Title = SR.TableColumnIdentityIncrementPropertyTitle
}
},
new DesignerDataPropertyInfo()
{
PropertyName = TableColumnPropertyNames.DefaultConstraintName,
Description = SR.TableColumnDefaultConstraintNamePropertyDescription,
ComponentType = DesignerComponentType.Input,
ComponentProperties = new InputBoxProperties()
{
Title = SR.TableColumnDefaultConstraintNamePropertyTitle
}
}
});
view.ColumnTableOptions.CanAddRows = true;
@@ -774,6 +1038,261 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
view.IndexColumnSpecificationTableOptions.CanRemoveRows = true;
}
private void SetGraphTableViewInfo(TableDesignerView view, Dac.TableDesigner tableDesigner)
{
if (tableDesigner.IsGraphTableSupported && (tableDesigner.IsNewTable || tableDesigner.TableViewModel.IsEdge || tableDesigner.TableViewModel.IsNode))
{
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.GraphTableType,
ComponentType = DesignerComponentType.Dropdown,
Description = SR.TableDesignerGraphTableTypeDescription,
Group = SR.TableDesignerGraphTableGroupTitle,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerGraphTableTypeTitle
}
});
}
}
private void SetEdgeConstraintsViewInfo(TableDesignerView view, Dac.TableDesigner tableDesigner)
{
if (!(tableDesigner.TableViewModel.IsEdge && tableDesigner.IsEdgeConstraintSupported))
{
return;
}
var tab = new DesignerTabView()
{
Title = SR.TableDesignerEdgeConstraintsTabTitle
};
var constraintsTableProperties = new TableComponentProperties<EdgeConstraintViewModel>()
{
Title = SR.TableDesignerEdgeConstraintsTabTitle,
ObjectTypeDisplayName = SR.TableDesignerEdgeConstraintObjectType
};
constraintsTableProperties.Columns.AddRange(new string[] { EdgeConstraintPropertyNames.Name, EdgeConstraintPropertyNames.ClausesDisplayValue });
constraintsTableProperties.ItemProperties.AddRange(new DesignerDataPropertyInfo[] {
new DesignerDataPropertyInfo()
{
PropertyName = EdgeConstraintPropertyNames.Name,
Description = SR.TableDesignerEdgeConstraintNamePropertyDescription,
ComponentType = DesignerComponentType.Input,
ComponentProperties = new InputBoxProperties()
{
Width = 200,
Title = SR.TableDesignerEdgeConstraintNamePropertyTitle
}
},
new DesignerDataPropertyInfo()
{
PropertyName = EdgeConstraintPropertyNames.ClausesDisplayValue,
ComponentType = DesignerComponentType.Input,
ShowInPropertiesView = false,
ComponentProperties = new InputBoxProperties()
{
Width = 300,
Title = SR.TableDesignerEdgeConstraintClausesPropertyTitle
}
},
new DesignerDataPropertyInfo()
{
PropertyName = EdgeConstraintPropertyNames.Enabled,
Description = SR.TableDesignerEdgeConstraintIsEnabledPropertyDescription,
ComponentType = DesignerComponentType.Checkbox,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.TableDesignerEdgeConstraintIsEnabledPropertyTitle
}
},
new DesignerDataPropertyInfo()
{
PropertyName = EdgeConstraintPropertyNames.OnDeleteAction,
Description = SR.TableDesignerEdgeConstraintOnDeleteActionPropertyDescription,
ComponentType = DesignerComponentType.Dropdown,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerEdgeConstraintOnDeleteActionPropertyTitle
}
},
new DesignerDataPropertyInfo()
{
PropertyName = EdgeConstraintPropertyNames.Clauses,
Description = SR.TableDesignerEdgeConstraintClausesPropertyDescription,
ComponentType = DesignerComponentType.Table,
ComponentProperties = new TableComponentProperties<EdgeConstraintClause>()
{
Title = SR.TableDesignerEdgeConstraintClausesPropertyTitle,
ObjectTypeDisplayName = SR.TableDesignerEdgeConstraintClauseObjectType,
Columns = new List<string> () { EdgeConstraintClausePropertyNames.FromTable, EdgeConstraintClausePropertyNames.ToTable},
ItemProperties = new List<DesignerDataPropertyInfo>()
{
new DesignerDataPropertyInfo()
{
PropertyName = EdgeConstraintClausePropertyNames.FromTable,
ComponentType = DesignerComponentType.Dropdown,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerEdgeConstraintClauseFromTablePropertyName,
Width = 150
}
},
new DesignerDataPropertyInfo()
{
PropertyName = EdgeConstraintClausePropertyNames.ToTable,
ComponentType = DesignerComponentType.Dropdown,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerEdgeConstraintClauseToTablePropertyName,
Width = 150
}
}
}
}
}
});
tab.Components.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.EdgeConstraints,
ComponentType = DesignerComponentType.Table,
ComponentProperties = constraintsTableProperties,
ShowInPropertiesView = false
});
view.AdditionalTabs.Add(tab);
}
private void SetTemporalTableViewInfo(TableDesignerView view, Dac.TableDesigner tableDesigner)
{
if (!tableDesigner.IsTemporalTableSupported)
{
return;
}
var table = tableDesigner.TableViewModel;
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.IsSystemVersioningEnabled,
ComponentType = DesignerComponentType.Checkbox,
Description = SR.TableDesignerIsSystemVersioningEnabledDescription,
Group = SR.TableDesignerSystemVersioningGroupTitle,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.TableDesignerIsSystemVersioningEnabledTitle
}
});
if (table.OriginalHistoryTable == null && table.SystemVersioningHistoryTable != null)
{
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.AutoCreateHistoryTable,
ComponentType = DesignerComponentType.Checkbox,
Description = SR.TableDesignerAutoCreateHistoryTableDescription,
Group = SR.TableDesignerSystemVersioningGroupTitle,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.TableDesignerAutoCreateHistoryTableTitle
}
});
if (table.AutoCreateHistoryTable)
{
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.NewHistoryTableTable,
ComponentType = DesignerComponentType.Input,
Description = SR.TableDesignerNewHistoryTableDescription,
Group = SR.TableDesignerSystemVersioningGroupTitle,
ComponentProperties = new InputBoxProperties()
{
Title = SR.TableDesignerNewHistoryTableTitle
}
});
}
else
{
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.ExistingHistoryTableName,
ComponentType = DesignerComponentType.Dropdown,
Description = SR.TableDesignerHistoryTableDescription,
Group = SR.TableDesignerSystemVersioningGroupTitle,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerHistoryTableTitle
}
});
}
}
else if (table.SystemVersioningHistoryTable != null)
{
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.ExistingHistoryTableName,
ComponentType = DesignerComponentType.Dropdown,
Description = SR.TableDesignerHistoryTableDescription,
Group = SR.TableDesignerSystemVersioningGroupTitle,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerHistoryTableTitle
}
});
}
view.ColumnTableOptions.AdditionalProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TableColumnPropertyNames.GeneratedAlwaysAs,
ComponentType = DesignerComponentType.Dropdown,
Description = SR.TableDesignerColumnGeneratedAlwaysAsDescription,
Group = SR.TableDesignerSystemVersioningGroupTitle,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerColumnGeneratedAlwaysAsTitle
}
});
view.ColumnTableOptions.AdditionalProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TableColumnPropertyNames.IsHidden,
ComponentType = DesignerComponentType.Checkbox,
Description = SR.TableDesignerColumnIsHiddenDescription,
Group = SR.TableDesignerSystemVersioningGroupTitle,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.TableDesignerColumnIsHiddenTitle
}
});
}
private void SetMemoryOptimizedTableViewInfo(TableDesignerView view, Dac.TableDesigner tableDesigner)
{
if (!tableDesigner.IsMemoryOptimizedTableSupported)
{
return;
}
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.IsMemoryOptimized,
ComponentType = DesignerComponentType.Checkbox,
Description = SR.TableDesignerIsMemoryOptimizedDescription,
Group = SR.TableDesignerMemoryOptimizedGroupTitle,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.TableDesignerIsMemoryOptimizedTitle
}
});
if (tableDesigner.TableViewModel.IsMemoryOptimized)
{
view.AdditionalTableProperties.Add(new DesignerDataPropertyInfo()
{
PropertyName = TablePropertyNames.Durability,
ComponentType = DesignerComponentType.Dropdown,
Description = SR.TableDesignerDurabilityDescription,
Group = SR.TableDesignerMemoryOptimizedGroupTitle,
ComponentProperties = new DropdownProperties()
{
Title = SR.TableDesignerDurabilityTitle
}
});
}
}
private Dac.TableDesigner CreateTableDesigner(TableInfo tableInfo)
{
var connectinStringbuilder = new SqlConnectionStringBuilder(tableInfo.ConnectionString);

View File

@@ -6,6 +6,7 @@
using System.Collections.Generic;
using Microsoft.Data.Tools.Sql.DesignServices.TableDesigner;
using ValidationError = Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts.TableDesignerValidationError;
using System.Linq;
namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
public static class TableDesignerValidator
@@ -17,7 +18,16 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
new ColumnCanOnlyAppearOnceInIndexRule(),
new NoDuplicateColumnNameRule(),
new NoDuplicateConstraintNameRule(),
new NoDuplicateIndexNameRule()
new NoDuplicateIndexNameRule(),
new EdgeConstraintMustHaveClausesRule(),
new EdgeConstraintNoRepeatingClausesRule(),
new MemoryOptimizedTableMustHaveNonClusteredPrimaryKeyRule(),
new TemporalTableMustHavePeriodColumns(),
new PeriodColumnsRule(),
new ColumnsInPrimaryKeyCannotBeNullableRule(),
new OnlyDurableMemoryOptimizedTableCanBeSystemVersionedRule(),
new TemporalTableMustHavePrimaryKeyRule(),
new TableMustHaveAtLeastOneColumnRule()
};
/// <summary>
@@ -72,7 +82,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
{
errors.Add(new ValidationError()
{
Message = string.Format("Foreign key '{0}' does not have any column mapping specified.", foreignKey.Name),
Message = string.Format("Foreign key '{0}' does not have any columns specified.", foreignKey.Name),
PropertyPath = new object[] { TablePropertyNames.ForeignKeys, i }
});
}
@@ -198,6 +208,23 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
existingNames.Add(checkConstraint.Name);
}
}
for (int i = 0; i < table.EdgeConstraints.Items.Count; i++)
{
var edgeConstraint = table.EdgeConstraints.Items[i];
if (existingNames.Contains(edgeConstraint.Name))
{
errors.Add(new ValidationError()
{
Message = string.Format("The name '{0}' is already used by another constraint. Row number: {1}.", edgeConstraint.Name, i + 1),
PropertyPath = new object[] { TablePropertyNames.EdgeConstraints, i, EdgeConstraintPropertyNames.Name }
});
}
else
{
existingNames.Add(edgeConstraint.Name);
}
}
return errors;
}
}
@@ -253,4 +280,183 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
return errors;
}
}
public class EdgeConstraintMustHaveClausesRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
for (int i = 0; i < table.EdgeConstraints.Items.Count; i++)
{
var edgeConstraint = table.EdgeConstraints.Items[i];
if (edgeConstraint.Clauses.Count == 0)
{
errors.Add(new ValidationError()
{
Message = string.Format("Edge constraint '{0}' does not have any clauses specified.", edgeConstraint.Name),
PropertyPath = new object[] { TablePropertyNames.EdgeConstraints, i }
});
}
}
return errors;
}
}
public class EdgeConstraintNoRepeatingClausesRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
for (int i = 0; i < table.EdgeConstraints.Items.Count; i++)
{
var edgeConstraint = table.EdgeConstraints.Items[i];
var existingPairs = new HashSet<string>();
for (int j = 0; j < edgeConstraint.Clauses.Count; j++)
{
var clause = edgeConstraint.Clauses[j];
var pair = string.Format("{0} - {1}", clause.FromTable, clause.ToTable);
if (existingPairs.Contains(pair))
{
errors.Add(new ValidationError()
{
Message = string.Format("The pair '{0}' is already defined by another clause in the edge constraint. Row number: {1}.", pair, j + 1),
PropertyPath = new object[] { TablePropertyNames.EdgeConstraints, i, EdgeConstraintPropertyNames.Clauses, j, EdgeConstraintClausePropertyNames.FromTable }
});
}
else
{
existingPairs.Add(pair);
}
}
}
return errors;
}
}
public class MemoryOptimizedTableMustHaveNonClusteredPrimaryKeyRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
if (table.IsMemoryOptimized && (table.PrimaryKey == null || table.PrimaryKey.IsClustered))
{
errors.Add(new ValidationError()
{
Message = "Memory-optimized table must have non-clustered primary key.",
PropertyPath = new object[] { TablePropertyNames.PrimaryKeyIsClustered }
});
}
return errors;
}
}
public class TemporalTableMustHavePrimaryKeyRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
if (table.SystemVersioningHistoryTable != null && table.PrimaryKey == null)
{
errors.Add(new ValidationError()
{
Message = "System versioned table must have primary key."
});
}
return errors;
}
}
public class TemporalTableMustHavePeriodColumns : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
if (table.SystemVersioningHistoryTable != null && !table.PeriodColumnsDefined)
{
errors.Add(new ValidationError()
{
Message = "System versioned table must have the period columns defined."
});
}
return errors;
}
}
public class PeriodColumnsRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
var rowStart = table.Columns.Items.Where(c => c.GeneratedAlwaysAs == ColumnGeneratedAlwaysAsType.GeneratedAlwaysAsRowStart);
var rowEnd = table.Columns.Items.Where(c => c.GeneratedAlwaysAs == ColumnGeneratedAlwaysAsType.GeneratedAlwaysAsRowEnd);
if (rowStart.Count() > 1 || rowEnd.Count() > 1)
{
errors.Add(new ValidationError()
{
Message = "Period columns (Generated Always As Row Start/End) can only be defined once."
});
}
else if (rowEnd.Count() != rowStart.Count())
{
errors.Add(new ValidationError()
{
Message = "Period columns (Generated Always As Row Start/End) must be defined as pair. If one is defined, the other must also be defined"
});
}
return errors;
}
}
public class ColumnsInPrimaryKeyCannotBeNullableRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
for (int i = 0; i < table.Columns.Items.Count; i++)
{
var column = table.Columns.Items[i];
if (column.IsPrimaryKey && column.IsNullable)
{
errors.Add(new ValidationError()
{
Message = "Columns in primary key cannot be nullable.",
PropertyPath = new object[] { TablePropertyNames.Columns, i }
});
}
}
return errors;
}
}
public class OnlyDurableMemoryOptimizedTableCanBeSystemVersionedRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
if (table.Durability == TableDurability.SchemaOnly && table.IsMemoryOptimized && table.IsSystemVersioningEnabled)
{
errors.Add(new ValidationError()
{
Message = "Only durable (DURABILITY = SCHEMA_AND_DATA) memory-optimized tables can be system-versioned."
});
}
return errors;
}
}
public class TableMustHaveAtLeastOneColumnRule : ITableDesignerValidationRule
{
public List<ValidationError> Run(TableViewModel table)
{
var errors = new List<ValidationError>();
if (!table.IsEdge && table.Columns.Items.Count == 0)
{
errors.Add(new ValidationError()
{
Message = "A table must have at least one column defined."
});
}
return errors;
}
}
}