#c# #.net #wcf #prism
#c# #.net #wcf #призма
Вопрос:
В настоящее время я пишу .Сетевое приложение на c #, состоящее из двух основных компонентов:
- DataGenerator — компонент, генерирующий много данных
- Viewer — приложение WPF, способное визуализировать данные, создаваемые генератором
В настоящее время эти два компонента являются двумя отдельными проектами в моем решении. Кроме того, я использую платформу PRISM 4.0 для создания модулей из этих компонентов.
По сути, DataGenerator генерирует много данных и отправляет события с помощью EventAggregator из PRISM, а средство просмотра подписывается на эти события и показывает данные, готовые для пользователя.
Теперь мои требования немного изменились, и теперь два компонента будут запускаться в своем собственном приложении (но на одном компьютере). Я все равно хотел бы, чтобы все коммуникации управлялись событиями, и я также хотел бы использовать PRISM framework.
Моей первой мыслью было использовать WCF для связи между этими двумя приложениями. Однако есть одна вещь, которая немного усложняет жизнь:
- DataGenerator не имеет абсолютно никаких знаний о средстве просмотра (и никаких зависимостей)
- DataGenerator все равно должен работать нормально, если у нас не открыт viewer или если мы закрываем приложение viewer.
- в настоящее время из генератора данных (с использованием EventAggregator) поступает много событий: достаточно ли эффективен WCF для обработки большого количества событий за очень короткий промежуток времени?
В основном данные, которые несут все эти события, представляют собой очень простые строки, целые и логические значения. Может ли быть более легкий способ сделать это без WCF?
Наконец, было бы неплохо, если бы DataGenerator мог отправлять эти события, и, возможно, на них подписывается более одного приложения (или ни одного).
Любые предложения и подсказки приветствуются.
Спасибо! Кристиан
РЕДАКТИРОВАТЬ 1
Сейчас я создаю два простых консольных приложения (одно из которых размещает службу и отправляет сообщения, другое получает сообщения), используя WCF и обратные вызовы (как было предложено). Я добавлю рабочий код, как только у меня это заработает.
РЕДАКТИРОВАТЬ 2
Хорошо — удалось запустить простую программу! 🙂 Спасибо за вашу помощь, ребята! Вот код и изображение того, где какие классы находятся:
Давайте начнем с отправителя:
В моем приложении отправитель содержит интерфейсы служб и их реализации.
IMessageCallback — это интерфейс обратного вызова:
namespace WCFSender
{
interface IMessageCallback
{
[OperationContract(IsOneWay = true)]
void OnMessageAdded(string message, DateTime timestamp);
}
}
ISimpleService — это контракт на обслуживание:
namespace WCFSender
{
[ServiceContract(CallbackContract = typeof(IMessageCallback))]
public interface ISimpleService
{
[OperationContract]
void SendMessage(string message);
[OperationContract]
bool Subscribe();
[OperationContract]
bool Unsubscribe();
}
}
SimpleService — это реализация ISimpleService:
public class SimpleService : ISimpleService
{
private static readonly List<IMessageCallback> subscribers = new List<IMessageCallback>();
public void SendMessage(string message)
{
subscribers.ForEach(delegate(IMessageCallback callback)
{
if (((ICommunicationObject)callback).State == CommunicationState.Opened)
{
callback.OnMessageAdded(message, DateTime.Now);
}
else
{
subscribers.Remove(callback);
}
});
}
public bool Subscribe()
{
try
{
IMessageCallback callback = OperationContext.Current.GetCallbackChannel<IMessageCallback>();
if (!subscribers.Contains(callback))
subscribers.Add(callback);
return true;
}
catch
{
return false;
}
}
public bool Unsubscribe()
{
try
{
IMessageCallback callback = OperationContext.Current.GetCallbackChannel<IMessageCallback>();
if (!subscribers.Contains(callback))
subscribers.Remove(callback);
return true;
}
catch
{
return false;
}
}
}
In Program.cs (on the sender side), the Service is hosted and the messages are being send:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class Program : SimpleServiceReference.ISimpleServiceCallback, IDisposable
{
private SimpleServiceClient client;
static void Main(string[] args)
{
ServiceHost myService = new ServiceHost(typeof(SimpleService));
myService.Open();
Program p = new Program();
p.start();
Console.ReadLine();
}
public void start()
{
InstanceContext context = new InstanceContext(this);
client = new SimpleServiceReference.SimpleServiceClient(context, "WSDualHttpBinding_ISimpleService");
for (int i = 0; i < 100; i )
{
client.SendMessage("message " i);
Console.WriteLine("sending message" i);
Thread.Sleep(600);
}
}
public void OnMessageAdded(string message, DateTime timestamp)
{
throw new NotImplementedException();
}
public void Dispose()
{
client.Close();
}
}
Furthermore, note that the service reference has been added to the Sender project!
Lets now get to the Receiver side:
As has already been done in the Sender, I added the Service Reference to the project.
There is only one class, Program.cs:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class Program : SimpleServiceReference.ISimpleServiceCallback, IDisposable
{
private SimpleServiceClient client;
static void Main(string[] args)
{
Program p = new Program();
p.start();
Console.ReadLine();
p.Dispose();
}
public void start()
{
InstanceContext context = new InstanceContext(this);
client = new SimpleServiceReference.SimpleServiceClient(context, "WSDualHttpBinding_ISimpleService");
client.Subscribe();
}
public void OnMessageAdded(string message, DateTime timestamp)
{
Console.WriteLine(message " " timestamp.ToString());
}
public void Dispose()
{
client.Unsubscribe();
client.Close();
}
}
Последнее, что осталось, это файлы app.config. На стороне клиента app.config создается автоматически путем добавления ссылки на службу. На стороне сервера я немного изменил конфигурацию, однако ее части также автоматически генерируются путем добавления ссылки на службу. Обратите внимание, что вам необходимо внести изменения перед добавлением ссылки на службу:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_ISimpleService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" />
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8732/Design_Time_Addresses/WCFSender/SimpleService/"
binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_ISimpleService"
contract="SimpleServiceReference.ISimpleService" name="WSDualHttpBinding_ISimpleService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
<behaviors>
<serviceBehaviors>
<behavior name="MessageBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WCFSender.SimpleService" behaviorConfiguration="MessageBehavior">
<endpoint address="" binding="wsDualHttpBinding" contract="WCFSender.ISimpleService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8732/Design_Time_Addresses/WCFSender/SimpleService/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
ВАЖНО:
Мне удалось реализовать эти два очень простых приложения, используя учебные пособия. Приведенный выше код работает для меня и, надеюсь, поможет другим понять обратный вызов WCF. Это не очень хорошо написанный код, и его не следует использовать полностью! Это просто упрощенный пример приложения.
Ответ №1:
Не беспокойтесь о производительности, wcf может достигать очень высокой пропускной способности при правильной настройке. Используйте обратные вызовы для ваших событий: http://www.switchonthecode.com/tutorials/wcf-tutorial-events-and-callbacks
Комментарии:
1. Итак, вы думаете, что WCF может справиться со случаем, когда неизвестное количество приложений прослушивает события?
2. @Christian: используйте Microsoft StreamInsight 1.2
3. @Артур Мустафин — вау, только что прочитал, что такое StreamInsight. Это, кажется, еще более эффективный способ. Однако я думаю, что в моем случае будет достаточно простых обратных вызовов WCF.
Ответ №2:
Используйте WCF с обратными вызовами, он очень эффективен при правильной настройке.
Вот несколько тестов: http://msdn.microsoft.com/en-us/library/bb310550.aspx
Вот хороший пример использования обратных вызовов: http://idunno.org/archive/2008/05/29/wcf-callbacks-a-beginners-guide.aspx
Ответ №3:
Используйте Microsoft StreamInsight 1.2. Варианты использования описывают, что он может встраиваться в приложение, службу WCF или в оба.
Прочитайте статью MSDN о StreamInsight 1.2:
Microsoft® StreamInsight — это сложная технология обработки событий Microsoft, помогающая компаниям создавать приложения, управляемые событиями, и получать лучшую информацию путем сопоставления потоков событий из нескольких источников с почти нулевой задержкой.
Microsoft StreamInsight ™ — это мощная платформа, которую можно использовать для разработки и развертывания сложных приложений обработки событий (CEP). Его высокопроизводительная архитектура потоковой обработки и платформа разработки на базе Microsoft .NET Framework позволяют быстро внедрять надежные и высокоэффективные приложения для обработки событий. Источники потоков событий обычно включают данные из производственных приложений, приложений для торговли финансовыми инструментами, веб-аналитики и операционной аналитики. Используя StreamInsight, вы можете разрабатывать приложения CEP, которые извлекают непосредственную выгоду для бизнеса из этих необработанных данных за счет снижения затрат на извлечение, анализ и сопоставление данных; и позволяя вам отслеживать, управлять и извлекать данные для условий, возможностей и дефектов практически мгновенно.
Используя StreamInsight для разработки приложений CEP, вы можете достичь следующих тактических и стратегических целей для своего бизнеса:
Отслеживайте свои данные из нескольких источников на предмет значимых шаблонов, тенденций, исключений и возможностей.
Анализируйте и сопоставляйте данные постепенно, пока данные находятся в процессе обработки, то есть без их предварительного сохранения, что обеспечивает очень низкую задержку. Агрегируйте, казалось бы, не связанные события из нескольких источников и выполняйте очень сложные анализы с течением времени.
Управляйте своим бизнесом, выполняя аналитику событий с низкой задержкой и инициируя действия реагирования, определенные в ключевых показателях эффективности вашего бизнеса (KPI).
Быстро реагируйте на области возможностей или угроз, включив определения ключевых показателей эффективности в логику приложения CEP, тем самым повышая операционную эффективность и вашу способность быстро реагировать на бизнес-возможности.
Разрабатывайте события для новых бизнес-показателей эффективности.
Переходите к бизнес-модели с прогнозом, извлекая исторические данные для постоянного уточнения и улучшения ваших определений ключевых показателей эффективности.
Дополнительную информацию и примеры можно найти в CodePlex:
Комментарии:
1. Спасибо за подсказку. Я обязательно посмотрю на примеры CodePlex, чтобы увидеть, много ли накладных расходов на внедрение этого в мое приложение. Также я должен посмотреть, достаточно ли легка платформа, потому что она должна быть как можно более легкой
2. Текст, заполненный маркетинговым жаргоном, лучше было бы заменить с трудом заработанным списком плюсов и минусов. По крайней мере, поместите такой текст в кавычки (я это сделал), потому что я сомневаюсь, что вы написали это сами. 🙂