我有以下精简的 DTO:
[DataContract]
public class ChartDefinitionBase
{
[DataMember]
public string Id { get; private set; }
}
...以及以下精简的 Mongo 服务定义:
public class MongoChartService : IChartService
{
private readonly IMongoCollection<ChartDefinitionBase> _collection;
private const string _connectionStringKey = "MongoChartRepository";
internal MongoChartService()
{
// Exception occurs here.
BsonClassMap.RegisterClassMap<ChartDefinitionBase>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Id).SetIdGenerator(StringObjectIdGenerator.Instance);
});
var connectionString = ConfigurationManager.ConnectionStrings[_connectionStringKey].ConnectionString;
var settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
var client = new MongoClient(settings);
var database = client.GetDatabase(ConfigurationManager.ConnectionStrings[_connectionStringKey].ProviderName);
_collection = database.GetCollection<ChartDefinitionBase>("Charts");
}
public void Create(ChartDefinitionBase instance)
{
_collection.InsertOne(instance);
}
public IEnumerable<ChartDefinitionBase> GetAllCharts()
{
var charts = _collection.Find(_ => true).ToList();
return charts;
}
}
然后我有一个客户端库,它有一个名为
MongoChartService
的 ChartServiceClient
的 WCF 服务引用。当我直接创建一个
MongoChartService
的实例并注入(inject)一个ChartDefinitionBase
的实例(完全实现并且没有子类)时,我就可以完成一次到数据库的往返(创建、读取、删除)。如果我创建 ChartServiceClient
的一个实例并尝试使用精简的 DTO 重复相同的步骤,则在调用 ServiceModel.FaultException
时会得到一个 GetAllCharts
,其中 ExceptionDetail
“已添加具有相同键的项目。”这是带有注释的示例单元测试。 [TestMethod, TestCategory("MongoService")]
public void ChartServiceClient_CRD_ExecutesSuccessfully()
{
SetupHost();
using (var client = new ChartServiceClient())
{
client.Create(_dto); // Create method succeeds. Single entry in dB with Mongo-generated ID.
ChartDefinitionBase dto = null;
while (dto == null)
{
var dtos = client.GetAllCharts(); // Exception occurs here.
dto = dtos.SingleOrDefault(d => d.Id == _dto.Id);
}
client.Delete(_dto);
while (dto != null)
{
var dtos = client.GetAllCharts();
dto = dtos.SingleOrDefault(d => d.Id == _dto.Id);
}
}
}
堆栈跟踪如下:
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at QRPad.Spc.DataLayer.Charts.Service.Client.ServiceReference.IChartService.GetAllCharts()
at QRPad.Spc.DataLayer.Charts.Service.Client.ServiceReference.ChartServiceClient.GetAllCharts()
编辑: 请注意,异常似乎发生在调用
BsonClassMap.RegisterClassMap
时。这个方法似乎同时被 Create
和 GetAllCharts()
调用。任何人都知道发生了什么以及如何解决这个问题?
最佳答案
问题似乎是由于在 BsonClassMap.RegisterClassMap
的构造函数中放置了对 MongoChartService
的调用。直接使用 MongoChartService
时,构造函数只调用一次。使用 ChartServiceClient
时,MongoChartService
构造函数在 Create
上调用一次,在 GetAllCharts
上调用一次;然而,由于 ChartDefinitionBase
是第一次注册的,第二次尝试注册它会产生异常。
当我在 BsonIdAttribute
上使用 Id
或将调用移动到其他地方的 BsonClassMap.RegisterClassMap
时,问题就解决了,例如在对客户端的调用之上:
[TestMethod, TestCategory("MongoService")]
public void ChartServiceClient_CRD_ExecutesSuccessfully()
{
SetupHost();
BsonClassMap.RegisterClassMap<ChartDefinitionBase>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Id).SetIdGenerator(StringObjectIdGenerator.Instance);
});
using (var client = new ChartServiceClient())
{
client.Create(_dto);
ChartDefinitionBase dto = null;
while (dto == null)
{
var dtos = client.GetAllCharts();
dto = dtos.SingleOrDefault(d => d.Id == _dto.Id);
}
client.Delete(_dto);
while (dto != null)
{
var dtos = client.GetAllCharts();
dto = dtos.SingleOrDefault(d => d.Id == _dto.Id);
}
}
}
MongoDb documentation 对此进行了说明,尽管我没有意识到服务构造函数会被多次调用:
关于c# - 为什么 WCF/Mongo 会抛出异常 : An item with the same key has already been added,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37078116/