如何在具有 Sql 服务器生产者的 CodeFluent 实体中的多对多关系 table 上声明聚簇键
How can I declare a Clustered key on a many-to-many relationship table in CodeFluent Entities with Sql Server Producer
我有一个 CodeFluent 实体模型,例如:
<cf:project defaultNamespace="S5T" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfmy="http://www.softfluent.com/codefluent/producers.mysql/2012/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfasp="http://www.softfluent.com/codefluent/producers.aspnet/2011/1" xmlns:cfaz="http://www.softfluent.com/codefluent/producers.sqlazure/2011/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" defaultKeyPropertyTypeName="long" maxParameterNameLength="62" defaultConcurrencyMode="None" persistencePropertyNameFormat="{1}" defaultMethodAllowDynamicSort="false" defaultProducerProductionFlags="Default, Overwrite, RemoveDates" defaultMethodDistinct="false" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false" productionFlags="Default, Overwrite, RemoveDates">
<cf:import path="Default.Surface.cfp" />
<cf:producer name="SQL Server" typeName="CodeFluent.Producers.SqlServer.SqlServerProducer, CodeFluent.Producers.SqlServer">
<cf:configuration produceViews="true" targetDirectory="..\Model7Bom\Persistence" connectionString="Server=MY-MACHINE\SQLEXPRESS;Database=model7;Integrated Security=true;Application Name=S5T;Password=MyPassword;User ID=MyUser" cfx:targetProject="..\Model7Bom\Model7Bom.vbproj" cfx:targetProjectLayout="Update, DontRemove" />
</cf:producer>
<cf:entity name="User" namespace="S5T">
<cf:property name="Id" key="true" cfps:hint="CLUSTERED" />
<cf:property name="Name" />
<cf:property name="Roles" typeName="{0}.RoleCollection" relationPropertyName="Users" />
</cf:entity>
<cf:entity name="Role" namespace="S5T">
<cf:property name="Id" key="true" cfps:hint="CLUSTERED" />
<cf:property name="Name" />
<cf:property name="Users" typeName="{0}.UserCollection" relationPropertyName="Roles" />
</cf:entity>
</cf:project>
我可以在 cfps:hint="CLUSTERED" 的两个实体上成功装饰 cf:属性 name="Id"。这让我 Sql 服务器生产者正确输出
CONSTRAINT [PK_Use_Id_Use] PRIMARY KEY CLUSTERED
CONSTRAINT [PK_Rol_Id_Rol] PRIMARY KEY CLUSTERED
与默认 NONCLUSTERED 相反。
如何使用模型生成的第三个 TABLE 来适应多对多关系?
默认情况下,table 创建生成的片段如下:
CREATE TABLE [dbo].[Role_Users_User_Roles] (
[Id] [bigint] NOT NULL,
[Id2] [bigint] NOT NULL,
CONSTRAINT [PK_Roe_Id_Id2_Roe] PRIMARY KEY NONCLUSTERED
(
[Id],
[Id2]
) ON [PRIMARY]
)
但是,如果我用 cfps:hint="CLUSTERED" 装饰两个属性,如:
cf:属性 name="Roles" typeName="{0}.RoleCollection" relationPropertyName="Users" cfps:hint="CLUSTERED" /
cf:属性 name="Users" typeName="{0}.UserCollection" relationPropertyName="Roles" cfps:hint="CLUSTERED" /
我在 TABLE [dbo].[Role_Users_User_Roles] 中得到了使用 PRIMARY KEY CLUSTERED 为 PK 生成的片段,但是,此外,我得到了不正确脚本的意外效果generated 用于添加关系(generated filename ...relations_add.sql),如:
ALTER TABLE [dbo].[Role_Users_User_Roles] WITH NOCHECK ADD CONSTRAINT [FK_Roe_Id_Id_Rol] FOREIGN KEY (
[Id]
) REFERENCES [dbo].[Role](
[Id]
) CLUSTERED
连同来自 Sql 服务器的错误:
错误 3 SQL80001:'CLUSTERED' 附近的语法不正确。
并且 CodeFluent Producer 错误:
CodeFluentRuntimeDatabaseException: CF4116: 在第 2 行执行文件...路径..._relations_add.sql 语句
我需要生成的三个 table 中的所有三个 PK,但不需要生成关系的语法错误的副作用。
不支持out-of-the-box。在 a 关系上声明的提示很少使用,更多的是作为外键提示而不是列提示。您可以使用多个选项来执行此操作。
最简单的方法是使用 post-generation .SQL 脚本手动添加集群设置。此处对此进行了描述:How to: Execute custom T-SQL scripts with the Microsoft SQL Server producer.
您还可以使用 Patch Producer 在文件创建后从文件中删除 CLUSTERED 单词:Patch Producer
否则,这是另一个解决方案,涉及我在此处作为示例编写的方面。您可以将以下片段 XML 保存为文件,并将其作为模型中的一个方面进行引用。
此方面会将 CLUSTERED 提示添加到从具有 CLUSTERED 键的实体推断出的所有多对多 table 的主键。它将在创建 table 脚本和 运行 之前添加提示,并在之后将其删除(因此它不会在 relations_add
脚本中结束)。
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1">
<!-- assembly references -->
<?code @reference name="CodeFluent.Producers.SqlServer.dll" ?>
<?code @reference name="CodeFluent.Runtime.Database.dll" ?>
<!-- namespace includes -->
<?code @namespace name="System" ?>
<?code @namespace name="System.Collections.Generic" ?>
<?code @namespace name="CodeFluent.Model.Code" ?>
<?code @namespace name="CodeFluent.Model.Persistence" ?>
<?code @namespace name="CodeFluent.Model.Code" ?>
<!-- add global code to listen to inference steps -->
<?code
Project.StepChanging += (sender1, e1) =>
{
if (e1.Step == ImportStep.End) // hook before production begins (end of inference pipeline)
{
var modifiedTables = ProjectHandler.AddClusteredHint(Project);
// get sql server producer and hook on production events
var sqlProducer = Project.Producers.GetProducerInstance<CodeFluent.Producers.SqlServer.SqlServerProducer>();
sqlProducer.Production += (sender, e) =>
{
// determine what SQL file has been created
// we want to remove hints once the table_diffs has been created, before relations_add is created
string script = e.GetDictionaryValue("filetype", null);
if (script == "TablesDiffsScript")
{
ProjectHandler.RemoveClusteredHint(modifiedTables);
}
};
}
};
?>
<!-- add member code to handle inference modification -->
<?code @member
public class ProjectHandler
{
public static IList<Table> AddClusteredHint(Project project)
{
var list = new List<Table>();
foreach (var table in project.Database.Tables)
{
// we're only interested by tables inferred from M:M relations
if (table.Relation == null || table.Relation.RelationType != RelationType.ManyToMany)
continue;
// check this table definition is ok for us
if (table.RelationKeyColumns.Count < 1 || table.RelationRelatedKeyColumns.Count < 1)
continue;
// check clustered is declared on both sides
string keyHint = GetSqlServerProducerHint(table.RelationKeyColumns[0].Property);
string relatedKeyHint = GetSqlServerProducerHint(table.RelationKeyColumns[0].Property);
if (keyHint.IndexOf("clustered", StringComparison.OrdinalIgnoreCase) < 0 ||
relatedKeyHint.IndexOf("clustered", StringComparison.OrdinalIgnoreCase) < 0)
continue;
// force hint now, we only need to do this on one of the keys, not all
table.PrimaryKey.Elements[0].SetAttribute("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri, "clustered");
// remember this table
list.Add(table);
}
return list;
}
public static void RemoveClusteredHint(IEnumerable<Table> list)
{
foreach (var table in list)
{
table.PrimaryKey.Elements[0].RemoveAttribute("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri);
}
}
// helper method to read XML element's hint attribute in the SQL Server Producer namespace
private static string GetSqlServerProducerHint(Node node)
{
if (node == null)
return null;
return node.GetAttributeValue<string>("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri, null);
}
}
?>
</cf:project>
我有一个 CodeFluent 实体模型,例如:
<cf:project defaultNamespace="S5T" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfmy="http://www.softfluent.com/codefluent/producers.mysql/2012/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfasp="http://www.softfluent.com/codefluent/producers.aspnet/2011/1" xmlns:cfaz="http://www.softfluent.com/codefluent/producers.sqlazure/2011/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" defaultKeyPropertyTypeName="long" maxParameterNameLength="62" defaultConcurrencyMode="None" persistencePropertyNameFormat="{1}" defaultMethodAllowDynamicSort="false" defaultProducerProductionFlags="Default, Overwrite, RemoveDates" defaultMethodDistinct="false" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false" productionFlags="Default, Overwrite, RemoveDates">
<cf:import path="Default.Surface.cfp" />
<cf:producer name="SQL Server" typeName="CodeFluent.Producers.SqlServer.SqlServerProducer, CodeFluent.Producers.SqlServer">
<cf:configuration produceViews="true" targetDirectory="..\Model7Bom\Persistence" connectionString="Server=MY-MACHINE\SQLEXPRESS;Database=model7;Integrated Security=true;Application Name=S5T;Password=MyPassword;User ID=MyUser" cfx:targetProject="..\Model7Bom\Model7Bom.vbproj" cfx:targetProjectLayout="Update, DontRemove" />
</cf:producer>
<cf:entity name="User" namespace="S5T">
<cf:property name="Id" key="true" cfps:hint="CLUSTERED" />
<cf:property name="Name" />
<cf:property name="Roles" typeName="{0}.RoleCollection" relationPropertyName="Users" />
</cf:entity>
<cf:entity name="Role" namespace="S5T">
<cf:property name="Id" key="true" cfps:hint="CLUSTERED" />
<cf:property name="Name" />
<cf:property name="Users" typeName="{0}.UserCollection" relationPropertyName="Roles" />
</cf:entity>
</cf:project>
我可以在 cfps:hint="CLUSTERED" 的两个实体上成功装饰 cf:属性 name="Id"。这让我 Sql 服务器生产者正确输出
CONSTRAINT [PK_Use_Id_Use] PRIMARY KEY CLUSTERED
CONSTRAINT [PK_Rol_Id_Rol] PRIMARY KEY CLUSTERED
与默认 NONCLUSTERED 相反。
如何使用模型生成的第三个 TABLE 来适应多对多关系?
默认情况下,table 创建生成的片段如下:
CREATE TABLE [dbo].[Role_Users_User_Roles] (
[Id] [bigint] NOT NULL,
[Id2] [bigint] NOT NULL,
CONSTRAINT [PK_Roe_Id_Id2_Roe] PRIMARY KEY NONCLUSTERED
(
[Id],
[Id2]
) ON [PRIMARY]
)
但是,如果我用 cfps:hint="CLUSTERED" 装饰两个属性,如:
cf:属性 name="Roles" typeName="{0}.RoleCollection" relationPropertyName="Users" cfps:hint="CLUSTERED" /
cf:属性 name="Users" typeName="{0}.UserCollection" relationPropertyName="Roles" cfps:hint="CLUSTERED" /
我在 TABLE [dbo].[Role_Users_User_Roles] 中得到了使用 PRIMARY KEY CLUSTERED 为 PK 生成的片段,但是,此外,我得到了不正确脚本的意外效果generated 用于添加关系(generated filename ...relations_add.sql),如:
ALTER TABLE [dbo].[Role_Users_User_Roles] WITH NOCHECK ADD CONSTRAINT [FK_Roe_Id_Id_Rol] FOREIGN KEY (
[Id]
) REFERENCES [dbo].[Role](
[Id]
) CLUSTERED
连同来自 Sql 服务器的错误:
错误 3 SQL80001:'CLUSTERED' 附近的语法不正确。
并且 CodeFluent Producer 错误:
CodeFluentRuntimeDatabaseException: CF4116: 在第 2 行执行文件...路径..._relations_add.sql 语句
我需要生成的三个 table 中的所有三个 PK,但不需要生成关系的语法错误的副作用。
不支持out-of-the-box。在 a 关系上声明的提示很少使用,更多的是作为外键提示而不是列提示。您可以使用多个选项来执行此操作。
最简单的方法是使用 post-generation .SQL 脚本手动添加集群设置。此处对此进行了描述:How to: Execute custom T-SQL scripts with the Microsoft SQL Server producer.
您还可以使用 Patch Producer 在文件创建后从文件中删除 CLUSTERED 单词:Patch Producer
否则,这是另一个解决方案,涉及我在此处作为示例编写的方面。您可以将以下片段 XML 保存为文件,并将其作为模型中的一个方面进行引用。
此方面会将 CLUSTERED 提示添加到从具有 CLUSTERED 键的实体推断出的所有多对多 table 的主键。它将在创建 table 脚本和 运行 之前添加提示,并在之后将其删除(因此它不会在 relations_add
脚本中结束)。
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1">
<!-- assembly references -->
<?code @reference name="CodeFluent.Producers.SqlServer.dll" ?>
<?code @reference name="CodeFluent.Runtime.Database.dll" ?>
<!-- namespace includes -->
<?code @namespace name="System" ?>
<?code @namespace name="System.Collections.Generic" ?>
<?code @namespace name="CodeFluent.Model.Code" ?>
<?code @namespace name="CodeFluent.Model.Persistence" ?>
<?code @namespace name="CodeFluent.Model.Code" ?>
<!-- add global code to listen to inference steps -->
<?code
Project.StepChanging += (sender1, e1) =>
{
if (e1.Step == ImportStep.End) // hook before production begins (end of inference pipeline)
{
var modifiedTables = ProjectHandler.AddClusteredHint(Project);
// get sql server producer and hook on production events
var sqlProducer = Project.Producers.GetProducerInstance<CodeFluent.Producers.SqlServer.SqlServerProducer>();
sqlProducer.Production += (sender, e) =>
{
// determine what SQL file has been created
// we want to remove hints once the table_diffs has been created, before relations_add is created
string script = e.GetDictionaryValue("filetype", null);
if (script == "TablesDiffsScript")
{
ProjectHandler.RemoveClusteredHint(modifiedTables);
}
};
}
};
?>
<!-- add member code to handle inference modification -->
<?code @member
public class ProjectHandler
{
public static IList<Table> AddClusteredHint(Project project)
{
var list = new List<Table>();
foreach (var table in project.Database.Tables)
{
// we're only interested by tables inferred from M:M relations
if (table.Relation == null || table.Relation.RelationType != RelationType.ManyToMany)
continue;
// check this table definition is ok for us
if (table.RelationKeyColumns.Count < 1 || table.RelationRelatedKeyColumns.Count < 1)
continue;
// check clustered is declared on both sides
string keyHint = GetSqlServerProducerHint(table.RelationKeyColumns[0].Property);
string relatedKeyHint = GetSqlServerProducerHint(table.RelationKeyColumns[0].Property);
if (keyHint.IndexOf("clustered", StringComparison.OrdinalIgnoreCase) < 0 ||
relatedKeyHint.IndexOf("clustered", StringComparison.OrdinalIgnoreCase) < 0)
continue;
// force hint now, we only need to do this on one of the keys, not all
table.PrimaryKey.Elements[0].SetAttribute("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri, "clustered");
// remember this table
list.Add(table);
}
return list;
}
public static void RemoveClusteredHint(IEnumerable<Table> list)
{
foreach (var table in list)
{
table.PrimaryKey.Elements[0].RemoveAttribute("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri);
}
}
// helper method to read XML element's hint attribute in the SQL Server Producer namespace
private static string GetSqlServerProducerHint(Node node)
{
if (node == null)
return null;
return node.GetAttributeValue<string>("hint", CodeFluent.Producers.SqlServer.Constants.SqlServerProducerNamespaceUri, null);
}
}
?>
</cf:project>