Вопросы надежности WCF
Пытаюсь проверить надежность моих wcf сервисов. Я вызываю службу wcf в цикле со стороны клиента. Служба отдыха wcf (webhttpbinding) выполняет некоторую обработку данных и вставляет записи в базу данных. Вся операция выполняется в рамках транзакции.
Я обнаружил, что из примерно 60 сообщений (в 60 раз превышающих количество обращений к службе из цикла) только 40 проходят в базу данных, если я устанавливаю свой InstanceContextMode в PerCall. Там нет ошибок, нет исключений. Сообщения просто удаляются.
Если я установлю InstanceContextMode в Single, тогда я увижу все сообщения, попадающие в БД. Является ли InstanceContextMode.Percall потерями ожидаемого поведения? Кроме того, у меня нет набора параллелизма. Любые разъяснения будут очень полезны. Добавил код. Используя MySQL в качестве базы данных...
РЕДАКТИРОВАТЬ Мой плохой - я только что заметил, что я получаю исключение на стороне сервера - {"Обнаружен тупик при попытке получить блокировку; попробуйте перезапустить транзакцию"}
Это связано с тем, что во время выполнения транзакции в тех же записях вызывается другая транзакция. Исправить это путем перезапуска транзакции, если она провалилась один раз.
Сервис
[WebInvoke(UriTemplate = "employee", Method = "POST")]
public long AddNewEmployee(EmployeeEntity newEmployee)
{
EmployeeRepository eRep = new EmployeeRepository();
return eRep.AddNewEmployee(newEventEntity);
}
Конструктор класса репозитория инициализирует контекст объекта
public EmployeeRepository()
{
bd = new EmployeeEntity();
}
Код - Сервисный звонок
//bd is the object context
//there are two tables
internal long AddNewEmployee(EmployeeEntity newEmployee)
{
bool tSuccess = false;
using (TransactionScope transaction = new TransactionScope())
{
try
{
//get existing employees
var existingEmployees = from employee
in bd.employees select employee;
List<employee> returnedEmployees = new List<employee>();
//Do some processing
returnedEmployees = DoSomeprocessing(existingEmployees);
//Insert returned employees as updates
foreach (employee e in returnedEmployees)
{
bd.employees.AddObject(e);
}
bd.SaveChanges();
returnedEmployees.Clear();
//add to second table
bd.otherdetails.AddObject(newEmployee);
bd.SaveChanges();
//Transaction Complete
transaction.Complete();
tSuccess = true;
}
catch (Exception e)
{
//return something meaningful
return -1;
}
}
if (tSuccess)
{
//End Transaction
bd.AcceptAllChanges();
return 200;
}
else
{
return -1;
}
}
Клиентская сторона просто вызывает сервис в цикле
1 ответ
Я настоятельно рекомендую добавить глобальную обработку исключений для любого WCF. Это помогло мне сэкономить много часов отладки и поймать любые необработанные исключения. Это немного больше, чем global.ascx в ASP.NET
Шаг 1 - Реализация IErroHander и IServiceBehavior
Обратите внимание, внутри HandleError я использую Enterprise Library для обработки исключения. Вы также можете использовать свою собственную реализацию здесь.
public class ErrorHandler : IErrorHandler, IServiceBehavior
{
public bool HandleError(Exception error)
{
// Returning true indicates you performed your behavior.
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// Log Exception
ExceptionPolicy.HandleException(error, "ExceptionPolicy");
// Shield the unknown exception
FaultException faultException = new FaultException(
"Server error encountered. All details have been logged.");
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
}
private IErrorHandler errorHandler = null;
public ErrorHandler()
{
}
public ErrorHandler(IErrorHandler errorHandler)
{
this.errorHandler = errorHandler;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
if (cd != null)
{
cd.ErrorHandlers.Add(new ErrorHandler());
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
if (cd != null)
{
cd.ErrorHandlers.Add(new ErrorHandler());
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
}
Шаг 2 - Создать элемент ошибки
public class ErrorHandlerElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ErrorHandler();
}
public override Type BehaviorType
{
get
{
return typeof(ErrorHandler);
}
}
}
Шаг 3 - Добавить элемент в web.config
<serviceBehaviors>
<behavior>
<ErrorHandler />
</behavior>
</serviceBehaviors>