Согласен, должно быть что-то проще, чем ваш нынешний подход. Но до тех пор, пока мы не сможем использовать шаблоны T4 для генерации прокси-классов или пока вы не захотите свернуть свои собственные (не рекомендуется IMO, YMMV), я думаю, что то, что вы уже попробовали, - ваш лучший шанс.
В настоящее время я использую гибрид обоих подходов. Я обернул все вызовы WCF в серию классов доступа к данным, которые переводят не очень полезную модель, управляемую событиями, которую предоставляет MS, в гораздо более простой подход обратного вызова. Внутри этих классов доступа к данным я также обернул каждый вызов статическим методом PreProcessCall (), который обрабатывает увеличение счетчика ожидающих вызовов и маршалинг вызова в фоновый поток; и я обернул каждый обратный вызов статическим методом PostProcessCall (), который уменьшает счетчик ожидающих вызовов и маршалирует обратный вызов в потоке пользовательского интерфейса.
public static void PreProcessCall(Action action)
{
Logger.LogDebugMessage("Pre-processing call for {0}", (new StackTrace()).GetFrame(1).GetMethod().Name);
Interlocked.Increment(ref pendingCalls);
ThreadPool.QueueUserWorkItem(o =>
{
try
{
action();
}
catch (System.Exception ex)
{
DataPublisher.GetPublisher().RaiseDataProcessExceptionOccurred(ex, ex.Message);
UpdatePendingCalls();
}
});
}
private static void UpdatePendingCalls()
{
Interlocked.Decrement(ref pendingCalls);
Debug.Assert(pendingCalls >= 0, "The number of pending calls should never go below zero.");
if (pendingCalls <= 0)
{
lock (pendingCallNotifications)
{
while (pendingCallNotifications.Count > 0)
{
Action callback = pendingCallNotifications.Dequeue();
Globals.Dispatcher.BeginInvoke(callback);
}
}
}
}
public static void PostProcessCall(Action action)
{
Logger.LogDebugMessage("Post-processing call for {0}", (new StackTrace()).GetFrame(1).GetMethod().Name);
UpdatePendingCalls();
Globals.Dispatcher.BeginInvoke(() =>
{
try
{
action();
}
catch (System.Exception ex)
{
DataPublisher.GetPublisher().RaiseDataProcessExceptionOccurred(ex, ex.Message);
}
});
}
Таким образом, типичный вызов выглядит примерно так:
public void SendMessage(string message, OperationCallback callback)
{
DataConnectionManager.PreProcessCall(() =>
{
Logger.LogDebugMessage("SendChatMessage");
notificationClient.SendChatMessageAsync(roomViewModel.SessionId, message, callback);
});
}
void RoomService_SendChatMessageCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
OperationCallback callback = (OperationCallback)e.UserState;
DataConnectionManager.PostProcessCall(() =>
{
if (callback != null)
{
callback(e.Error);
}
Logger.LogDebugMessage("SendChatMessageCompleted.");
});
}
Как я уже сказал, в основном то, что вы уже пробовали.