SQL Server CE Merge replication:0x80004005与0x80040e37
结论
Hr=80004005 是 Web Synchronization 返回的通用失败码,仅凭这个代码无法判断根因。0x80040E37 对应 DB_E_NOTABLE,表示同步时引用的表或视图不存在。这里的“表”可能是 SQL Server CE 中的业务表,也可能是发布端对象、筛选视图、触发器依赖对象、复制生成的对象,或初始化快照中的目标对象。
从现象看,问题更可能出在发布端的 Merge replication 元数据、快照或对象依赖关系上:
- 已初始化的两台设备仍能同步。
- 新建的空
.sdf在初始化 bidirectional publication 时失败。 - download-only publication 始终正常。
- 删除并重建 publication 后曾短暂恢复。
- SSMS 和自定义程序得到相同结果。
因此,设备程序可以暂时放在次要排查位置。现有设备能够同步也不能证明 publication 完整,因为这些设备通常不会重新执行快照中的建表和初始化脚本。
先保留故障现场,找到返回 DB_E_NOTABLE 的那条 SQL,再判断是否需要重建 publication。ACK:Error for DistributorSessionID = 1752 本身无法定位具体对象。
保留可复现的故障现场
先不要删除全部 publication。选择一个失败的 bidirectional publication,换用新的订阅者名称和新的 .sdf 文件,再次执行初始化。需要记录:
- 精确到秒的开始时间和失败时间。
- publication、article 和 subscriber 名称。
- IIS 日志中的
DistributorSessionID。 - SQL CE 异常集合中的所有错误,而非只有最外层
Message。 - 同一时间段内 Snapshot Agent 和 Merge Agent 的详细输出。
已有设备能继续同步,而新设备无法初始化,这个差异有助于缩小排查范围。重建 publication 会删除相关元数据和快照,使故障现场无法继续分析。
输出完整的 SQL CE 错误集合
SqlCeException.Message 经常只显示最后一层错误。应遍历 Errors 集合,输出 HResult、NativeError、Source 和所有数值参数。
using System;
using System.Data.SqlServerCe;
public static class ReplicationTest
{
public static void Synchronize()
{
var replication = new SqlCeReplication
{
InternetUrl =
"https://iis-server.example.com/sqlce/sqlcesa35.dll",
InternetLogin = "web-sync-user",
InternetPassword = "password",
Publisher = "SQLSERVER",
PublisherDatabase = "ApplicationDatabase",
Publication = "BidirectionalPublication",
PublisherSecurityMode = SecurityType.NTAuthentication,
Subscriber = "DiagnosticSubscriber03",
SubscriberConnectionString =
@"Data Source=C:\Temp\diagnostic.sdf;Password=password"
};
try
{
replication.AddSubscription(AddOption.CreateDatabase);
replication.Synchronize();
}
catch (SqlCeException ex)
{
Console.WriteLine(ex.ToString());
foreach (SqlCeError error in ex.Errors)
{
Console.WriteLine(
"HResult=0x{0:X8}; NativeError={1}; Source={2}; Message={3}",
error.HResult,
error.NativeError,
error.Source,
error.Message);
if (error.NumericErrorParameters != null)
{
for (int i = 0;
i < error.NumericErrorParameters.Length;
i++)
{
Console.WriteLine(
"NumericErrorParameters[{0}]={1}",
i,
error.NumericErrorParameters[i]);
}
}
}
}
finally
{
replication.Dispose();
}
}
}
请将示例中的地址、身份验证方式和密码替换为实际配置。正式日志中不要记录密码或完整的连接凭据。
如果订阅已经存在,不要再次调用 AddSubscription(AddOption.CreateDatabase)。可以根据测试目标使用新的数据库,或对已有订阅直接调用 Synchronize()。
获取服务器端实际失败的 SQL
IIS 文件中的以下内容只是会话失败通知:
Hr=80004005 ACK:Error for DistributorSessionID = 1752
DistributorSessionID 只能用于关联日志,不能说明根因。检查同一目录中、同一时间生成的其他日志和 SQL 文件,并保留原始文件。查找以下内容:
- SQL Server 错误号,例如
208。 Invalid object name。- 完整的
CREATE TABLE、ALTER TABLE、INSERT或存储过程调用。 - 出错对象的两段式名称,例如
dbo.TableName。 - 执行失败的触发器、视图或筛选过程。
还可以在 SQL Server Profiler 中执行一次范围尽量小的跟踪,关注:
Errors and Warnings / ExceptionErrors and Warnings / User Error MessageTSQL / SQL:BatchCompletedStored Procedures / RPC:Completed
用数据库名、登录名、应用程序名和测试时间进行过滤。生产环境中不要长时间捕获全部 SQL,也不要无条件记录大字段。只需取得第一次失败时的服务器错误及其对应语句。
Replication Monitor 显示同步“已启动”,只能证明请求进入了复制流程,不能证明快照已完整应用。
核对 publication 和 article 元数据
对每个失败的 publication 执行以下存储过程,并保存结果:
EXEC sys.sp_helpmergepublication
@publication = N'BidirectionalPublication';
EXEC sys.sp_helpmergearticle
@publication = N'BidirectionalPublication';
EXEC sys.sp_helpmergefilter
@publication = N'BidirectionalPublication';
逐项核对:
- article 指向的源对象是否仍然存在。
source_owner、destination_owner和对象名是否正确。- 表改名、迁移 schema 或恢复数据库后,article 是否仍在沿用旧配置。
- row filter、join filter 引用的表和列是否存在。
- 初始化脚本、
creation_script或自定义同步对象是否仍引用旧表名。 - bidirectional article 的
upload_options、冲突解析和权限设置是否符合预期。 - 复制所需的
ROWGUIDCOL、主键和 identity range 配置是否完整。
可用以下查询确认三个源表的 schema:
SELECT
s.name AS schema_name,
t.name AS table_name,
t.object_id
FROM sys.tables AS t
JOIN sys.schemas AS s
ON s.schema_id = t.schema_id
WHERE t.name IN
(
N'BidirectionalTable1',
N'BidirectionalTable2',
N'BidirectionalTable3'
);
不要只核对表名。dbo.Sample 与其他 schema 下的 Sample 是不同对象。数据库迁移、更改所有者或从备份恢复后,article 元数据可能仍保留旧的对象关系。
检查触发器和其他依赖对象
Bidirectional article 会经过上传和服务器端 DML 路径,download-only article 则不一定会触发这些代码。可以先检查三个双向表上的用户触发器、约束、视图和存储过程。
SELECT
OBJECT_SCHEMA_NAME(tr.parent_id) AS table_schema,
OBJECT_NAME(tr.parent_id) AS table_name,
SCHEMA_NAME(tr.schema_id) AS trigger_schema,
tr.name AS trigger_name,
tr.is_disabled,
sm.definition
FROM sys.triggers AS tr
JOIN sys.sql_modules AS sm
ON sm.object_id = tr.object_id
WHERE tr.parent_id IN
(
OBJECT_ID(N'dbo.BidirectionalTable1'),
OBJECT_ID(N'dbo.BidirectionalTable2'),
OBJECT_ID(N'dbo.BidirectionalTable3')
);
然后检查能够静态解析的未绑定依赖:
SELECT
OBJECT_SCHEMA_NAME(d.referencing_id) AS referencing_schema,
OBJECT_NAME(d.referencing_id) AS referencing_object,
d.referenced_database_name,
d.referenced_schema_name,
d.referenced_entity_name
FROM sys.sql_expression_dependencies AS d
WHERE d.referenced_id IS NULL
AND d.referenced_entity_name IS NOT NULL
ORDER BY
referencing_schema,
referencing_object;
查询结果中可能包含跨数据库引用或其他合法的延迟解析对象,需要逐项核实,不能把每一行都视为故障。动态 SQL 中的对象名通常不会完整出现在依赖视图中,因此还要搜索模块定义和触发器代码。
验证初始化快照
既有订阅能够继续同步,而新订阅初始化失败时,应重点检查初始化快照:
- 在 Replication Monitor 中查看对应 Snapshot Agent 的最后一次运行记录。
- 确认快照生成时间晚于最近一次表结构、筛选条件或 article 属性变更。
- 检查快照目录中该 publication 的
.sch、.bcp和相关脚本是否齐全。 - 核对 Snapshot Agent、SQL Server Agent 和 IIS 应用程序池身份是否有权读取快照共享目录。
- 确认磁盘空间充足,快照文件未被清理程序、防病毒软件或人工部署删除。
- 重新生成受影响 publication 的快照,再用一个全新的 subscriber 验证。
如果重新生成快照后恢复正常,仍需检查 Snapshot Agent 的详细日志。后续处理取决于具体原因,需要分清旧快照是否包含过期对象名,以及快照文件是否不完整或无法读取。
检查组件版本和实际加载的 DLL
记录 SQL Server CE 3.5 SP2、桌面测试工具、设备程序和 IIS Server Tools 的完整文件版本。安装列表不能准确反映程序实际使用的 CU,因为程序可能从应用目录加载另一份 System.Data.SqlServerCe.dll。
建议记录:
- 设备上
System.Data.SqlServerCe.dll的文件版本。 - WinForms 工具实际加载的程序集路径及版本。
- IIS 上 SQL Server CE Server Tools 的文件版本。
- IIS 应用程序池位数和所用 ISAPI 路径。
- SQL Server 的精确 build。
如果所有新订阅都在同一个服务器步骤失败,而旧订阅正常,版本差异通常不是排查重点。不过,仍需排除混装 CU、位数不匹配,以及应用目录中的旧 DLL 被优先加载等问题。
修复顺序
取得失败 SQL 和对象名后,按对象范围处理:
- 修复缺失、已改名或 schema 不一致的表、视图及触发器依赖。
- 修正 article、filter 或初始化脚本中的旧对象名。
- 为受影响的 publication 重新生成快照。
- 使用新的 subscriber 和新的
.sdf完成初始化及双向上传测试。 - 验证成功后,再逐台重新初始化设备。
- 确认复制元数据已无法修复后,才删除并重建 publication。
不要同时删除全部 publication、subscription 和设备数据库。这种做法或许能恢复服务,却无法确认具体是哪项操作消除了故障,也无法避免相同的对象依赖或部署流程再次破坏快照。
现有日志不足以确定唯一根因。排查时需要拿到 0x80040E37 出现时执行的完整 SQL,以及该语句实际引用的对象。有了这两项信息,才能判断问题是缺失对象、schema 错误、依赖失效、快照陈旧,还是复制元数据不一致。
备注:内容仅供参考。
