Я создал службу WCF, которая обрабатывает MSMQ, давайте назовем службу QueueService.Контракт выглядит следующим образом:
// Each call to the service will be dispatched separately, not grouped into sessions.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class QueueServiceContract : IQueueServiceContract
{
[OperationBehavior(TransactionScopeRequired = true)]
public void QueueItems(List<Item> items) // really should be called 'HandleQueueItems
{
// Early in the processing I do:
Transaction qTransaction = Transaction.Current;
...
// I then check if the destination database is available.
if(DB.IsAvailable)
... process the data
else
qTransaction.Rollback;
...
}
IQueueServiceContract выглядит следующим образом:
// The contract will be session-less. Each post to the queue from the client will create a single message on the queue.
[ServiceContract(SessionMode = SessionMode.NotAllowed, Namespace = "MyWebService")]
public interface IQueueServiceContract
{
[OperationContract(IsOneWay = true)]
void QueueItems(List<Item> items);
}
Соответствующие части App.config для службы очереди выглядят следующим образом.
<services>
<service name="QueueService.QueueServiceContract">
<endpoint address="net.msmq://localhost/private/MyQueueServiceQueue" binding="netMsmqBinding" contract="QueueService.IQueueServiceContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
...
<netMsmqBinding>
<binding exactlyOnce="true" maxRetryCycles="1000" receiveRetryCount="1"
retryCycleDelay="00:10:00" timeToLive="7.00:00:00" useActiveDirectory="false">
</binding>
</netMsmqBinding>
Это все отлично работает.Когда БД недоступна, откат приводит к тому, что запись очереди помещается в очередь повторных попыток, которую я настроил для повторения каждые 10 минут в течение 7 дней.Все в нем работает и работает в течение 6 месяцев или около того.
Теперь я добавляю логирование в сервис.QueueService собирается поместить записи журнала в другую очередь, которую мы назовем: LogQueue.Требование заключается в том, что независимо от того, выполняется ли откат qTransaction, в LogQueue должно быть отправлено сообщение с указанием статуса запроса.
В файле app.config QueueService я добавил:
<client>
<endpoint address="net.msmq://localhost/private/MyLogQueue"
binding="netMsmqBinding" bindingConfiguration="NetMsmqBinding_ILogContract"
contract="LogServiceReference.ILogContract" name="NetMsmqBinding_ILogContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
...
<binding name="NetMsmqBinding_ILogContract" timeToLive="7.00:00:00">
<security mode="None" />
</binding>
В файле logService app.config у меня есть:
<service name="LogService.LogContract">
<endpoint address="net.msmq://localhost/private/MyLogQueue" binding="netMsmqBinding" contract="LogService.ILogContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
...
<netMsmqBinding>
<binding exactlyOnce="true" maxRetryCycles="1000" receiveRetryCount="1" retryCycleDelay="00:10:00" timeToLive="7.00:00:00" useActiveDirectory="false">
</binding>
</netMsmqBinding>
...
Затем, в конце метода QueueItems, я делаю следующее:
LogContractClient proxy = new LogContractClient();
proxy.LogTransaction(myLoggingInformation); // This queues myLoggingInformation to the LogQueue.
Все это тоже отлично работает... пока ... база данных недоступна и транзакция откатывается.
Откат произойдет до вызова proxy.LogTransaction, и я получу:
System.ServiceModel.CommunicationException: 'An error occurred while sending to the queue: The transaction specified cannot be enlisted. (-1072824232, 0xc00e0058).Ensure that MSMQ is installed and running. If you are sending to a local queue, ensure the queue exists with the required access mode and authorization.'
Если я перемещу proxy.LogTransaction до qTransaction.Rollback, запись журнала никогда не будет помещена в LogQueue.
Моя теория работы заключается в том, что WCF рассматривает операции в двух очередях: чтение из очереди QueueService и запись в LogQueue, как одна транзакция.Итак, если я пытаюсь записать в LogQueue после отката, транзакция уже закончилась, но если я записываю в LogQueue перед вызовом отката, запись в очередь также откатывается.
Есть ли способ, которым яможет сохранить возможность отката транзакции queueService, не откатывая одновременно транзакцию LogService?