问题
I am trying to create a DbSyncForeignKeyConstraint to a table with a composite Primary Key but, I keep getting errors. Here is some sample code to demonstrate what I am doing:
EXAMPLE TABLES:
USE [TempTest]
GO
CREATE TABLE [dbo].[Users](
[UserId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
[UserId] ASC
)
WITH
(
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[UsersRoles](
[UserId] [uniqueidentifier] NOT NULL,
[RoleId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_UsersRoles] PRIMARY KEY CLUSTERED
(
[UserId] ASC,
[RoleId] ASC
)
WITH
(
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO
and here is my C# code:
DbSyncTableDescription tableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("UsersRoles", (SqlConnection)this.dbProvider.Connection);
Collection<string> parentkeys = new Collection<string>();
parentkeys.Add("UserId");
Collection<string> childkeys = new Collection<string>();
childkeys.Add("RoleId");
childkeys.Add("UserId");
tableDesc.Constraints.Add(new DbSyncForeignKeyConstraint("FK_UsersRoles_Users", "Users", "UsersRoles", parentkeys, childkeys));
The code above would produce this error:
The definition of referring columns (such as number of columns or data types) in referential relationships must match the referred columns.
If I comment out the line with childkeys.Add("RoleId")
, then I get this error:
"The referenced table must have a primary or candidate key."
So I am a little unclear as to how I should do this. The constraint should really only be about the UserId column, in this example. But, since the UsersRoles table has a composite primary key, should I include that in the constraint? If I leave out the "RoleId" in the constraint definition, I get an error...if I include it, I get a different error.
Does anyone have a suggestion about how I should proceed? (PS: I have code that creates other foreign keys between tables with normal non-composite primary keys, and that code works without error).
EDIT WITH ADDITIONAL INFORMATION:
OK. The problem doesn't seem to be with the DBSyncForeignKeyConstraint creation. The problem seems to be with the GetDescriptionForTable functionality. It doesn't seem to be picking up the UserId in the as the primary key. In my project, I am actually working against a database which uses the aspnet_Membership schema. So, if I script the aspnet_Users table, it looks like this:
'
CREATE TABLE [dbo].[aspnet_Users](
[ApplicationId] [uniqueidentifier] NOT NULL,
[UserId] [uniqueidentifier] NOT NULL,
[UserName] nvarchar NOT NULL,
[LoweredUserName] nvarchar NOT NULL,
[MobileAlias] nvarchar NULL,
[IsAnonymous] [bit] NOT NULL,
[LastActivityDate] [datetime] NOT NULL,
PRIMARY KEY NONCLUSTERED
(
[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[aspnet_Users] WITH CHECK ADD FOREIGN KEY([ApplicationId])
REFERENCES [dbo].[aspnet_Applications] ([ApplicationId])
GO
ALTER TABLE [dbo].[aspnet_Users] ADD DEFAULT (newid()) FOR [UserId]
GO
ALTER TABLE [dbo].[aspnet_Users] ADD DEFAULT (NULL) FOR [MobileAlias]
GO
ALTER TABLE [dbo] [aspnet_Users] ADD DEFAULT ((0)) FOR [IsAnonymous] GO'
But, if I examine the results from calling "GetScopeDescription", I see that the columns "ApplicationId" and "LoweredUserName" are listed as PKColumns and that UserId is listed among the NonPkColumns. I have no idea what is causing this. But, at least I understand why the error is telling me that the column "UserId" is not among the primary keys. It is in the database but, it isn't there in the results of GetScopDescription. So, I know what direction to start searching in, at least.
回答1:
not sure how you're provisioning code looks like, but this works perfectly fine
var sqlConn = new SqlConnection(@"Data Source=(local)\SQLExpress; Initial Catalog=Test; Integrated Security=True");
var sqlConn2 = new SqlConnection(@"Data Source=(local)\SQLExpress; Initial Catalog=Test2; Integrated Security=True");
var parent = SqlSyncDescriptionBuilder.GetDescriptionForTable("Users", sqlConn);
var child = SqlSyncDescriptionBuilder.GetDescriptionForTable("UsersRoles", sqlConn);
var parentPKColumns = new Collection<string> {"UserId"};
var childFKColumns = new Collection<string> {"UserId"};
child.Constraints.Add(new DbSyncForeignKeyConstraint("FK_UsersRoles_Users", "Users", "UsersRoles", parentPKColumns, childFKColumns));
var fKTestScope= new DbSyncScopeDescription("FKTestScope");
fKTestScope.Tables.Add(parent);
fKTestScope.Tables.Add(child);
var scopeProvisioning = new SqlSyncScopeProvisioning(sqlConn2, fKTestScope);
scopeProvisioning.Apply();
回答2:
OK, this seems to be a pretty serious problem with the scope provisioning functionality. If I go to my source database that has been provisioned, and look at the data in the config_data column of the scope_config table, I see, in the xml, that the aspnet_Users table is described in an incorrect manner:
<Adapter Name="[aspnet_Users]" GlobalName="[aspnet_Users]" TrackingTable="[aspnet_Users_tracking]" SelChngProc="[aspnet_Users_selectchanges]" SelRowProc="[aspnet_Users_selectrow]" InsProc="[aspnet_Users_insert]" UpdProc="[aspnet_Users_update]" DelProc="[aspnet_Users_delete]" InsMetaProc="[aspnet_Users_insertmetadata]" UpdMetaProc="[aspnet_Users_updatemetadata]" DelMetaProc="[aspnet_Users_deletemetadata]" BulkTableType="[aspnet_Users_BulkType]" BulkInsProc="[aspnet_Users_bulkinsert]" BulkUpdProc="[aspnet_Users_bulkupdate]" BulkDelProc="[aspnet_Users_bulkdelete]" InsTrig="[aspnet_Users_insert_trigger]" UpdTrig="[aspnet_Users_update_trigger]" DelTrig="[aspnet_Users_delete_trigger]">
<Col name="ApplicationId" type="uniqueidentifier" param="@P_1" pk="true" />
<Col name="UserId" type="uniqueidentifier" param="@P_2" />
<Col name="UserName" type="nvarchar" size="256" param="@P_3" />
<Col name="LoweredUserName" type="nvarchar" size="256" param="@P_4" pk="true" />
<Col name="MobileAlias" type="nvarchar" size="16" null="true" param="@P_5" />
<Col name="IsAnonymous" type="bit" param="@P_6" />
<Col name="LastActivityDate" type="datetime" param="@P_7" />
You can see that the ApplicationId and the LoweredUserName columns are marked as pks, when they are not. And, the UserId column is not marked as a pk, when it should be. This incorrect data causes the tracking table that is created for the aspnet_Users table to be structured incorrectly so, simply fixing the xml in the scope_config table is not enough to fix the problem. So, it would appear that I have stumbled upon a pretty large problem that will take some work to correct. I don't think I am doing anything to create this error, as I am not setting any parameters when performing the server configuration.
I have not been able to find a description of this problem anywhere else, which I find really strange but, I will keep looking.
Ugh!
I will include an image from the explorer in my SQL Server Management Studio.
Thank you, JuneT, for your willingness to help.
回答3:
this looks like due to DataReader's GetSchemaTable which is used internally by Sync Fx to grab the table structure (see: http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataproviders/thread/c1c113e2-32dc-4826-b6e0-17dff29c1baf)
as a workaround, just set the PK yourself.
e.g.,
downloadOnlyScopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("aspnet_Users", (System.Data.SqlClient.SqlConnection)provider.Connection));
//untag wrong PK information
foreach(var pkColumn in downloadOnlyScopeDesc.Tables["aspnet_Users"].PkColumns)
{
downloadOnlyScopeDesc.Tables["aspnet_Users"].Columns[pkColumn.QuotedName].IsPrimaryKey = false;
}
//tag the correct PK
downloadOnlyScopeDesc.Tables["aspnet_Users"].Columns["UserId"].IsPrimaryKey = true;
来源:https://stackoverflow.com/questions/12501310/how-do-i-create-a-dbsyncforeignkeyconstraint-to-a-table-with-a-composite-primary