Hallo,
ich habe vor einiger Zeit zwecks meiner Ausbildung ein Praktikum angefangen und arbeite deswegen in meinem zweiten Ausbildungsjahr komplett in einer Firma als Praktikant. Dort wurde mir die Aufgabe zugeteilt, die bisherige Schnittstelle, die mit DCOM arbeitet zu ersetzen. Hier setzt WCF (Windows Communication Foundation) an. Sie ist der "Nachfolger" von diversen Diensten und stellt ein recht mächtiges Framework zur Verfügung. Damit kann man recht "einfach" Server-Client-Applikationen realisieren...
In diesem Tutorial möchte ich eine einfache Möglichkeit zeigen, wie man von einem Client einen Server anfunkt und dieser dann eine gewisse Aufgabe ausführt. Damit der Client während der Wartezeit nicht einfriert läuft das ganze asynchron. Zur Übersichtlichkeit wurde beim Client das MVVM-Pattern verwendet (näheres dazu findet sich hier)
Ein WCF-Projekt ist nach dem ABC (Address, Binding, Contract) aufgebaut...ohne das läuft nichts. Wenn man weiß, was für Funktionen man hat/braucht kann man auch von hinten anfangen und nach CBA das ganze aufbauen
C -> Contract
nach Möglichkeit durch ein Interface (Schnittstelle) zu lösen, welche druch eine Klasse aufgerufen wird (siehe CdatenVertrag/Ivertrag/IvertragAsynchron)
B -> Binding
Angabe des Transportprotokolls (zb. TCP oder Http) in meinem Projekt habe ich TCP gewählt (NetTcpBinding). Bei Microsoft findet man auch noch mehr. Je nach Einsatzzweck wählt man hier das passende aus oder kreiert sich selbst eines...
A -> Address
Beinhaltet Angabe zum Protokoll, IpAdresse, Portnummer und Pfad zur Anwendung
Binding//IpAdresse:Portnummer/PfadzurAnwendung -> in meinem Projekt: net.tcp://localhost:2121/IvertragAsynchron (anstatt localhost kann man auch eine Ipadresse eintragen)
Damit es am Anfang keine Probleme gibt sollte man setzen.... (siehe im Code zb. Bei Server -> Program Zeile 28)
Folgender Aufbau:
Man startet mit einer leeren Projektmappe und fügt dieser folgende Projekte hinzu:
Client -> WPF-Application
HilfeTools -> Class Library ("Klassenbibliothek") (wird für das MVVM benötigt)
Server -> Console-Application
Vertrag -> Class Library
Am besten fängt man mit dem Server an...
Dem Serverprojekt fügt man zwei Klassen hinzu (bzw eine, da eine ja schon standardmäßig angelegt wird) "Cservice" und "Program" oder wie man sie auch immer nennen möchte...
Die Klasse Cservice führt das aus, was der Server machen soll...zum Beispiel rechnet es Primzahlen aus oder gibt einfach nur nen String "Hallo Welt" zurück
Die Klasse Program baut eine Verbindung zur "Außenwelt" auf...
Program:
Spoiler anzeigen
Hier ließt der Server die Ip-Adresse des Rechners aus, auf dem er gestartet wird und öffnet einen Port. Die Portnummer wird im Debugverzeichnis des Servers als .txt Datei hinterlegt. So kann man recht einfach die Portnummer anpassen/ändern. Nachdem der Server diese Portnummer ausgelesen hat, baut er aus der Ip-Adresse und dem Port sowie dem zugehörigen Vertrag (dazu später mehr) eine Verbindung auf... Dann wartet der Server auf eine Anfrage vom Client...
Cservice:
Spoiler anzeigen
Hier werden einfach Primzahlen berechnet...(hier jetzt mal bis 100.000). Das Ergebnis wird in einen String geschrieben und an den Client dann zurück geschickt... return new CdatenVertrag -> Content = ergebnis
Das war es auch schon zum Server...zu beachten sind die References (Verweise) und Usings
Jetzt zum Client...
Dem Client verpasst man eine nicht allzu aufwändige Oberfläche mit WPF/XAML...
Textbox1 = Eingabefeld für die Ipadresse -> Name: hostNameFeld
Textbox2 = Eingabefeld für die Portnummer -> Name: portnummerFeld
Textbox3 = Ausgabefeld des Ergebnisses welches man vom Server erhält -> Name: Ausgabe
Button = zum Verbindung aufbauen/zum Starten des Clients...
Den Controlls gibt man halbwegs sinnvolle Namen, sodass man damit später auch was anfangen kann...
Im XAML-Code sind noch 6 Zeilen wichtig:
Spoiler anzeigen
genaueres ließt man hier
Ebenfalls wichtig im XAML-Code sind die folgenden Dinge:
in der Textbox für die IP-Adresse und die Portnummer
bei der Textbox der Portnummer lautet der Code entsprechend
beim Button
beim Ausgabefeld
so wird der entsprechende Inhalt gebunden.
Spoiler anzeigen
Das ist der vollständige Code meines Fensters...Ich habe zwei Bilder als Hintergrund verwendet...Entweder ihr löscht den entsprechenden Code raus oder fügt selbst zwei beliebige Bilder hinzu...Einfach dem Client-Projekt einen neuen Ordner hinzufügen ("Bilder") und in diesen zwei Bilder hinzufügen
dann in die Namen im Code anpassen
(black1 und mm1 durch eure entsprechenden Namen austauschen (die der Bilder im Ordner "Bilder"
Jetzt geht man in die Codebehind-Klasse vom Client... -> MainWindows.xaml.cs
Dort ist eigentlich nur eine Zeile Code von elementarer Bedeutung:
Mit deren Hilfe wird das Binding weitergeleitet
Spoiler anzeigen
jetzt kann man das Viewmodel dem Client hinzufügen...
Man fügt dem Client eine neue Klasse hinzu und benennt diese jetzt Viewmodel oder wie auch immer (dann muss man die elementare Zeile Code aber wie folgt ändern: [...] = new WieAuchImmerName.MainModel();
der Viewmodel-Klasse fügt man jetzt die Gegenstücke der im XAML-Code vorbereiteten Bindings hinzu
Die in im Anführungszeichen stehenden Begriffe ("ipAddress") sind die im XAML genannten
So weiß das Programm wohin damit...Der UpdateSourceTrigger ist später interessant (bei den HilfeTools)
Wenn man dem Client eine Ip und eine PortNr. eingetragen hat, kann man diese auch Speichern (über die Menuzeile). So muss man nicht immer beides neu eintippen. Damit der Client das auch übernimmt, muss man die Textboxen "aktualisieren" dafür ist der UpdateSourceTrigger da...
Wenn man jetzt auf den Button klickt baut der Client die Verbindung zum Server auf...wichtig hierbei ist, dass man die bei Ip und PortNr. die Werte eintippt, die der Server hat, da der Client ja eine Verbindung zum PC aufbaut, auf dem der Server läuft.
Spoiler anzeigen
In der OnStart-Methode (beim Klicken auf den Button) ist noch was dabei, was verhindert, dass die Verbindung fehlschlägt, da es zu lange dauert oder die Nachricht zu groß ist. TimeSpan und MaxReceivedMessageSize ...Je nachdem wie groß der String ist, kann es sonst zu Problemen führen...ist aber nicht zwingend notwendig
Damit die erwähnten Bindings/UpdateSourceTrigger funktionieren ist noch ein weiteres Projekt wichtig. Die HilfeTools
hier braucht man zwei Klassen...nichts aufwändiges...
Cbefehl:
Spoiler anzeigen
Hier ist wichtig, dass man von ICommand erbt -> siehe Viewmodel Speichern/Laden
Chilfe
Spoiler anzeigen
hier ist wichtig, dass man von INotifyPropertyChanged erbt -> siehe Viewmodel Properties für die Textboxen
Wichtig ist auch, dass man das Projekt HilfeTools dem Client als Verweis hinzufügt...
Jetzt kommt das Herzstück des Ganzen...
Der Vertrag:
Hier legt man 1 Klasse an -> CdatenVertrag
und zwei Schnittstellen (Interface) -> Ivertrag und IvertragAsynchron
der CdatenVertrag sieht wie folgt aus:
Spoiler anzeigen
Bei den beiden Schnittstellen Ivertrag und IvertragAsynchron ist wichtig, dass diese den selben Namen bekommen...
Ja ich war hier etwas unkreativ und mir ist nichts sinnvolles eingefallen
Beide müssen in dieser Zeile den Namen identisch haben:
Ivertrag:
IvertragAsynchron:
Spoiler anzeigen
Das ist eigentlich auch schon alles... Der OperationContract mit bool Ping(); kann auch gelöscht werden...das war eigentlich nur zu Testzwecken...
hier noch ein Paar nähere Infors zu einigen Sachen im Code
Channelfactory (im Viewmodel)
MVVM und MVVM2
Vertrag
WCF
Binding
Ich hoffe ich kann hiermit dem ein oder anderen helfen...Ich hab mir das hier alles selbst beigebracht und es hat einiges an Kraft gekostet durch zu halten, da ich hier nur das Forum hatte, wo ich um Hilfe fragen konnte, weil in meiner Firma keiner mit WCF vertraut ist...
[edit]
Auf Wunsch von @ErfinderDesRades habe ich auf meiner Platte noch zwei Beispiele gesucht (VB.Net und C#)...
das VB-Beispiel ist ein wenig "verkrüpelt" und nicht auf dem aktuellen Stand...
Das C# Beispiel sollte jetzt dem Tut entsprechen
ich habe vor einiger Zeit zwecks meiner Ausbildung ein Praktikum angefangen und arbeite deswegen in meinem zweiten Ausbildungsjahr komplett in einer Firma als Praktikant. Dort wurde mir die Aufgabe zugeteilt, die bisherige Schnittstelle, die mit DCOM arbeitet zu ersetzen. Hier setzt WCF (Windows Communication Foundation) an. Sie ist der "Nachfolger" von diversen Diensten und stellt ein recht mächtiges Framework zur Verfügung. Damit kann man recht "einfach" Server-Client-Applikationen realisieren...
In diesem Tutorial möchte ich eine einfache Möglichkeit zeigen, wie man von einem Client einen Server anfunkt und dieser dann eine gewisse Aufgabe ausführt. Damit der Client während der Wartezeit nicht einfriert läuft das ganze asynchron. Zur Übersichtlichkeit wurde beim Client das MVVM-Pattern verwendet (näheres dazu findet sich hier)
Ein WCF-Projekt ist nach dem ABC (Address, Binding, Contract) aufgebaut...ohne das läuft nichts. Wenn man weiß, was für Funktionen man hat/braucht kann man auch von hinten anfangen und nach CBA das ganze aufbauen
C -> Contract
nach Möglichkeit durch ein Interface (Schnittstelle) zu lösen, welche druch eine Klasse aufgerufen wird (siehe CdatenVertrag/Ivertrag/IvertragAsynchron)
B -> Binding
Angabe des Transportprotokolls (zb. TCP oder Http) in meinem Projekt habe ich TCP gewählt (NetTcpBinding). Bei Microsoft findet man auch noch mehr. Je nach Einsatzzweck wählt man hier das passende aus oder kreiert sich selbst eines...
A -> Address
Beinhaltet Angabe zum Protokoll, IpAdresse, Portnummer und Pfad zur Anwendung
Binding//IpAdresse:Portnummer/PfadzurAnwendung -> in meinem Projekt: net.tcp://localhost:2121/IvertragAsynchron (anstatt localhost kann man auch eine Ipadresse eintragen)
Damit es am Anfang keine Probleme gibt sollte man setzen.... (siehe im Code zb. Bei Server -> Program Zeile 28)
Folgender Aufbau:
Man startet mit einer leeren Projektmappe und fügt dieser folgende Projekte hinzu:
Client -> WPF-Application
HilfeTools -> Class Library ("Klassenbibliothek") (wird für das MVVM benötigt)
Server -> Console-Application
Vertrag -> Class Library
Am besten fängt man mit dem Server an...
Dem Serverprojekt fügt man zwei Klassen hinzu (bzw eine, da eine ja schon standardmäßig angelegt wird) "Cservice" und "Program" oder wie man sie auch immer nennen möchte...
Die Klasse Cservice führt das aus, was der Server machen soll...zum Beispiel rechnet es Primzahlen aus oder gibt einfach nur nen String "Hallo Welt" zurück
Die Klasse Program baut eine Verbindung zur "Außenwelt" auf...
Program:
C#-Quellcode
- using System;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.ServiceModel; //unabdingbar für WCF
- using System.Windows.Forms;
- using Vertrag;
- namespace Server
- {
- class Program
- {
- static void Main(string[] args)
- {
- using (ServiceHost sh = new ServiceHost(typeof(Cservice)))
- {
- string Host = System.Net.Dns.GetHostName();
- var IPAddress = Dns.GetHostEntry(Host).AddressList.First(adr => adr.AddressFamily != AddressFamily.InterNetworkV6); //gibt ipv4 zurück
- FileInfo fi = new FileInfo(Application.ExecutablePath); // Pfad von .txt-Datei im Debug-Verzeichnis vom Server
- string path;
- path= fi.DirectoryName + "\\Portnummer.txt";
- string portnummer;
- portnummer = File.ReadAllText(path); // liest Portnummer aus .txt heraus
- Console.WriteLine("Der Service läuft auf dem Port: " + portnummer);
- Console.WriteLine("");
- sh.AddServiceEndpoint(typeof(Ivertrag), new NetTcpBinding(SecurityMode.None), "net.tcp://" + IPAddress + ":" + portnummer + "/IvertragAsynchron");
- sh.Open();
- Console.WriteLine("Server läuft auf: " + "net.tcp://" + IPAddress + ":" + portnummer + "/IvertragAsynchron");
- Console.WriteLine("");
- Console.WriteLine("Zum Beenden bitte Taste drücken!");
- Console.Read();
- }
- }
- }
- }
Hier ließt der Server die Ip-Adresse des Rechners aus, auf dem er gestartet wird und öffnet einen Port. Die Portnummer wird im Debugverzeichnis des Servers als .txt Datei hinterlegt. So kann man recht einfach die Portnummer anpassen/ändern. Nachdem der Server diese Portnummer ausgelesen hat, baut er aus der Ip-Adresse und dem Port sowie dem zugehörigen Vertrag (dazu später mehr) eine Verbindung auf... Dann wartet der Server auf eine Anfrage vom Client...
Cservice:
C#-Quellcode
- using Vertrag;
- namespace Server
- {
- class Cservice : Ivertrag
- {
- public bool Ping()
- {
- return true;
- }
- public CdatenVertrag DoIt() //public Cvertrag DoIt()
- {
- System.Threading.Thread.Sleep(1000);
- string ergebnis = null;
- int n = 0;
- n = 100000; // Obergrenze (bis dahin wird nach Primzahlen gesucht)
- bool[] prime = new bool[n];
- for (int i = 2; i <= n - 1; i++)
- {
- prime[i] = true;
- }
- if (true)
- {
- int i = 2;
- while (i * i < n)
- {
- if (prime[i])
- {
- ergebnis = ergebnis + " " + i.ToString();
- int j = i * i;
- while (j < n)
- {
- prime[j] = false;
- j = j + i;
- }
- }
- i += 1;
- }
- while (i < n)
- {
- if (prime[i])
- {
- ergebnis = ergebnis + " " + i.ToString();
- }
- i += 1;
- }
- }
- return new CdatenVertrag
- {
- Content = ergebnis//"Daten vom Server!"
- };
- }
- }
- }
Hier werden einfach Primzahlen berechnet...(hier jetzt mal bis 100.000). Das Ergebnis wird in einen String geschrieben und an den Client dann zurück geschickt... return new CdatenVertrag -> Content = ergebnis
Das war es auch schon zum Server...zu beachten sind die References (Verweise) und Usings
Jetzt zum Client...
Dem Client verpasst man eine nicht allzu aufwändige Oberfläche mit WPF/XAML...
Textbox1 = Eingabefeld für die Ipadresse -> Name: hostNameFeld
Textbox2 = Eingabefeld für die Portnummer -> Name: portnummerFeld
Textbox3 = Ausgabefeld des Ergebnisses welches man vom Server erhält -> Name: Ausgabe
Button = zum Verbindung aufbauen/zum Starten des Clients...
Den Controlls gibt man halbwegs sinnvolle Namen, sodass man damit später auch was anfangen kann...
Im XAML-Code sind noch 6 Zeilen wichtig:
XML-Quellcode
genaueres ließt man hier
Ebenfalls wichtig im XAML-Code sind die folgenden Dinge:
in der Textbox für die IP-Adresse und die Portnummer
bei der Textbox der Portnummer lautet der Code entsprechend
beim Button
beim Ausgabefeld
so wird der entsprechende Inhalt gebunden.
XML-Quellcode
- <Window x:Class="Client.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Client" Height="345" Width="786" WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- mc:Ignorable="d"
- d:DesignHeight="300" d:DesignWidth="300"
- xmlns:vm="clr-namespace:Client.Viewmodel"
- d:DataContext="{d:DesignInstance Type=vm:MainModel}" >
- <!--die 4 letzten Zeilen ermöglichen Binding-Picking-->
- <!--https://www.vb-paradise.de/index.php/Thread/83442-MVVM-Binding-Picking-im-Xaml-Editor/-->
- <Window.Resources>
- <Style TargetType="{x:Type TextBlock}">
- <Setter Property="Margin" Value="5,0,4,0" />
- <Setter Property="VerticalAlignment" Value="Center" />
- </Style>
- <ImageBrush x:Key="blue" ImageSource="/Bilder/black1.jpg" />
- <ImageBrush x:Key="technik" ImageSource="/Bilder/mm1.png" />
- </Window.Resources>
- <Grid Height="328"
- VerticalAlignment="Top"
- Background="{StaticResource blue}" Margin="0,0,0,-28">
- <Grid.ColumnDefinitions>
- <ColumnDefinition/>
- <ColumnDefinition Width="0*"/>
- </Grid.ColumnDefinitions>
- <Grid.RowDefinitions >
- <RowDefinition Height="217.28"/>
- </Grid.RowDefinitions>
- <Menu Name="menu1" Grid.Row="0" VerticalAlignment="Top" Height="21">
- <MenuItem Header="Datei">
- <MenuItem.ToolTip>
- <ToolTip>
- <StackPanel>
- <TextBlock FontWeight="Bold">Speichern/Laden/Beenden</TextBlock>
- <TextBlock>Zum Speichern der Ip-Adresse und der Portnummer</TextBlock>
- <TextBlock>Zum Laden der Ip-Adresse und der Portnummer</TextBlock>
- <TextBlock>Zum Beenden des Programms</TextBlock>
- </StackPanel>
- </ToolTip>
- </MenuItem.ToolTip>
- <MenuItem Header="Speichern"
- Command="{Binding Speichern}"/>
- <MenuItem Header="Laden"
- Command="{Binding Laden}"/>
- <Separator/>
- <MenuItem Click="OnBeenden"
- Header="Beenden"/>
- </MenuItem>
- </Menu>
- <!--UpdateSourceTrigger aktualisiert den Inhalt der TextBox...so wird das erkannt, was hinein geladen wird-->
- <TextBox Margin="216,72,376,118"
- FontWeight="Medium"
- FontSize="16"
- Name="hostNameFeld"
- Text="{Binding IpAddress, UpdateSourceTrigger=PropertyChanged}"
- AcceptsReturn="False">
- <TextBox.ToolTip>
- <ToolTip>
- <StackPanel>
- <TextBlock FontWeight="Bold">Ip-Adresse/Host Name</TextBlock>
- <TextBlock>Tragen Sie hier die Server-Ip-Adresse oder den Server-Hostnamen ein</TextBlock>
- </StackPanel>
- </ToolTip>
- </TextBox.ToolTip>
- </TextBox>
- <!--UpdateSourceTrigger aktualisiert den Inhalt der TextBox...so wird das erkannt, was hinein geladen wird-->
- <TextBox Margin="424,72,206,118"
- FontWeight="Medium"
- FontSize="16"
- Name="portnummerFeld"
- Text="{Binding PortNumber, UpdateSourceTrigger=PropertyChanged}"
- AcceptsReturn="False">
- <TextBox.ToolTip>
- <ToolTip>
- <StackPanel>
- <TextBlock FontWeight="Bold">Server Portnummer</TextBlock>
- <TextBlock>Tragen Sie bitte die Portnummer des Servers ein, mit dem sich der Client verbinden soll</TextBlock>
- </StackPanel>
- </ToolTip>
- </TextBox.ToolTip>
- </TextBox>
- <TextBox Name="Ausgabe"
- Margin="30,138,30,-78"
- TextWrapping="Wrap"
- Background="{StaticResource blue}"
- Foreground="Red"
- VerticalScrollBarVisibility="Hidden"
- AcceptsReturn="False"
- Text="{Binding StringContent}">
- <TextBox.ToolTip>
- <ToolTip>
- <StackPanel>
- <TextBlock FontWeight="Bold">Ausgabe</TextBlock>
- <TextBlock>Gibt die Antwort des Servers zurück</TextBlock>
- </StackPanel>
- </ToolTip>
- </TextBox.ToolTip>
- </TextBox>
- <Label Foreground="Red"
- Margin="216,37,322,146">Server Ip-Adresse / Hostname
- </Label>
- <Label Foreground="Red"
- Margin="424,37,222,146">Server Portnummer
- </Label>
- <Label Foreground="Red"
- Margin="30,105,660,85">Ausgabe
- </Label>
- <Label Background="{StaticResource technik}"
- Margin="30,21,594,90"/>
- <Button Margin="594,72,30,118"
- Background="White"
- Command="{Binding GetDataCommand}"
- Focusable="True"
- IsDefault="True"
- >Client starten
- <Button.ToolTip>
- <ToolTip>
- <StackPanel>
- <TextBlock FontWeight="Bold">Client starten</TextBlock>
- <TextBlock>Startet den Client und baut eine Verbindung zum gewünschten Server auf</TextBlock>
- </StackPanel>
- </ToolTip>
- </Button.ToolTip>
- </Button>
- </Grid>
- </Window>
Das ist der vollständige Code meines Fensters...Ich habe zwei Bilder als Hintergrund verwendet...Entweder ihr löscht den entsprechenden Code raus oder fügt selbst zwei beliebige Bilder hinzu...Einfach dem Client-Projekt einen neuen Ordner hinzufügen ("Bilder") und in diesen zwei Bilder hinzufügen
dann in die Namen im Code anpassen
(black1 und mm1 durch eure entsprechenden Namen austauschen (die der Bilder im Ordner "Bilder"
Jetzt geht man in die Codebehind-Klasse vom Client... -> MainWindows.xaml.cs
Dort ist eigentlich nur eine Zeile Code von elementarer Bedeutung:
Mit deren Hilfe wird das Binding weitergeleitet
C#-Quellcode
- using System.Windows;
- //View (inkl. MainWindow.xaml)
- namespace Client
- {
- public partial class MainWindow : Window
- {
- public MainWindow()
- {
- InitializeComponent();
- this.DataContext = new Viewmodel.MainModel();
- // fürs Binding/MVVM hergestellte "Verknüpfung"
- }
- private void OnBeenden(object sender, RoutedEventArgs e)
- {
- this.Close();
- }
- }
- }
jetzt kann man das Viewmodel dem Client hinzufügen...
Man fügt dem Client eine neue Klasse hinzu und benennt diese jetzt Viewmodel oder wie auch immer (dann muss man die elementare Zeile Code aber wie folgt ändern: [...] = new WieAuchImmerName.MainModel();
der Viewmodel-Klasse fügt man jetzt die Gegenstücke der im XAML-Code vorbereiteten Bindings hinzu
Die in im Anführungszeichen stehenden Begriffe ("ipAddress") sind die im XAML genannten
So weiß das Programm wohin damit...Der UpdateSourceTrigger ist später interessant (bei den HilfeTools)
Wenn man dem Client eine Ip und eine PortNr. eingetragen hat, kann man diese auch Speichern (über die Menuzeile). So muss man nicht immer beides neu eintippen. Damit der Client das auch übernimmt, muss man die Textboxen "aktualisieren" dafür ist der UpdateSourceTrigger da...
Wenn man jetzt auf den Button klickt baut der Client die Verbindung zum Server auf...wichtig hierbei ist, dass man die bei Ip und PortNr. die Werte eintippt, die der Server hat, da der Client ja eine Verbindung zum PC aufbaut, auf dem der Server läuft.
C#-Quellcode
- using System.ServiceModel;
- using System.Windows.Input;
- using Vertrag;
- using HilfeTools;
- using System.Threading;
- using System.Net;
- using System.Windows;
- using System;
- using System.Diagnostics;
- using System.IO;
- //Viewmodel
- namespace Client.Viewmodel
- {
- public partial class MainModel : Chilfe
- {
- private IvertragAsynchron objProxy;
- public string _StringContent = "";
- public string StringContent
- {
- get { return _StringContent; }
- set { _StringContent = value; NotifyPropertyChanged("StringContent"); }
- }
- public string _IpAddress = "";
- public string IpAddress
- {
- get { return _IpAddress; }
- set { _IpAddress = value; NotifyPropertyChanged("ipAddress"); }
- }
- public string _PortNumber = "";
- public string PortNumber
- {
- get { return _PortNumber; }
- set { _PortNumber = value; NotifyPropertyChanged("portNumber"); }
- }
- private Cbefehl _GetDataCommand;
- public ICommand GetDataCommand
- {
- get { return (_GetDataCommand ?? (_GetDataCommand = new Cbefehl(OnStart))); }
- //Der Operator ?? wird NULL-Sammeloperator genannt.
- //Der linke Operand wird zurückgegeben, falls dieser NICHT NULL ist.
- //Andernfalls wird der rechte Operand zurückgegeben.
- }
- private Cbefehl _Speichern;
- public ICommand Speichern
- {
- get { return (_Speichern ?? (_Speichern = new Cbefehl(OnSpeichern))); }
- }
- private Cbefehl _Laden;
- public ICommand Laden
- {
- get { return (_Laden ?? (_Laden = new Cbefehl(OnLaden))); }
- }
- public void OnSpeichern()
- {
- Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog();
- dialog.Filter = "Txt-Files(*.txt)|*.txt";
- Nullable<bool> result = dialog.ShowDialog();
- if (result == true)
- {
- File.WriteAllText(dialog.FileName, IpAddress);
- }
- Nullable<bool> result2 = dialog.ShowDialog();
- if (result2 == true)
- {
- File.WriteAllText(dialog.FileName, PortNumber);
- }
- }
- public void OnLaden()
- {
- Microsoft.Win32.OpenFileDialog dialogo = new Microsoft.Win32.OpenFileDialog();
- dialogo.Filter = "Txt-Files(*.txt)|*.txt";
- Nullable<bool> result = dialogo.ShowDialog();
- if (result == true)
- {
- IpAddress = File.ReadAllText(dialogo.FileName);
- }
- Nullable<bool> result2 = dialogo.ShowDialog();
- if (result2 == true)
- {
- PortNumber = File.ReadAllText(dialogo.FileName);
- }
- }
- public void OnStart()
- {
- string rmAddres = null;
- rmAddres = "net.tcp://" + IpAddress + ":" + PortNumber + "/IvertragAsynchron";
- NetTcpBinding nettcpbinding = new NetTcpBinding(SecurityMode.None); // new NetTcpBinding(hier MUSS was stehen!! sonst tuts ned)
- ChannelFactory<IvertragAsynchron> chFactory = new ChannelFactory<IvertragAsynchron>(nettcpbinding);
- nettcpbinding.MaxReceivedMessageSize = 10485760; // 10485760 entspricht 10 MB
- TimeSpan mSpan = default(TimeSpan);
- mSpan = new TimeSpan(1200000000); // entspricht 2 Minuten (1 Millisekunde = 10.000 Ticks) -> 2min = 120.000ms = 1.200.000.000Ticks
- nettcpbinding.SendTimeout = mSpan;
- EndpointAddress epAddress = new EndpointAddress(rmAddres);
- objProxy = chFactory.CreateChannel(epAddress);
- this.StringContent = "Anfrage läuft...währenddessen kann das Fenster frei bewegt werden";
- if (objProxy.Ping())
- {
- objProxy.BeginDoIt(asyncResult =>
- {
- this.StringContent = objProxy.EndDoIt(asyncResult).Content; // hier wird die Fehlermeldung geworfen... -> CallbackException
- }, null);
- }
- }
- }
- }
In der OnStart-Methode (beim Klicken auf den Button) ist noch was dabei, was verhindert, dass die Verbindung fehlschlägt, da es zu lange dauert oder die Nachricht zu groß ist. TimeSpan und MaxReceivedMessageSize ...Je nachdem wie groß der String ist, kann es sonst zu Problemen führen...ist aber nicht zwingend notwendig
Damit die erwähnten Bindings/UpdateSourceTrigger funktionieren ist noch ein weiteres Projekt wichtig. Die HilfeTools
hier braucht man zwei Klassen...nichts aufwändiges...
Cbefehl:
C#-Quellcode
- using System;
- using System.Windows.Input;
- namespace HilfeTools
- {
- public class Cbefehl : ICommand
- {
- private Action _execute;
- public Cbefehl(Action execute)
- {
- _execute = execute;
- }
- public bool CanExecute(object parameter)
- {
- return true;
- }
- public event EventHandler CanExecuteChanged;
- public void Execute(object parameter)
- {
- _execute();
- }
- }
- }
Hier ist wichtig, dass man von ICommand erbt -> siehe Viewmodel Speichern/Laden
Chilfe
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- namespace HilfeTools
- {
- public abstract class Chilfe : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
- public void NotifyPropertyChanged(string info)
- {
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs(info));
- }
- }
- }
- }
hier ist wichtig, dass man von INotifyPropertyChanged erbt -> siehe Viewmodel Properties für die Textboxen
Wichtig ist auch, dass man das Projekt HilfeTools dem Client als Verweis hinzufügt...
Jetzt kommt das Herzstück des Ganzen...
Der Vertrag:
Hier legt man 1 Klasse an -> CdatenVertrag
und zwei Schnittstellen (Interface) -> Ivertrag und IvertragAsynchron
der CdatenVertrag sieht wie folgt aus:
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.Serialization;
- using System.ServiceModel;
- using System.Text;
- namespace Vertrag
- {
- [DataContract(Name = "DemoData")]
- public class CdatenVertrag
- {
- [DataMember(Name = "Content")]
- public string Content { get; set; }
- }
- }
Bei den beiden Schnittstellen Ivertrag und IvertragAsynchron ist wichtig, dass diese den selben Namen bekommen...
Ja ich war hier etwas unkreativ und mir ist nichts sinnvolles eingefallen
Beide müssen in dieser Zeile den Namen identisch haben:
Ivertrag:
IvertragAsynchron:
C#-Quellcode
- using System;
- using System.ServiceModel;
- namespace Vertrag
- {
- [ServiceContract(Name = "Grapefruit")]
- public interface IvertragAsynchron
- {
- [OperationContract(AsyncPattern= true)]
- IAsyncResult BeginDoIt(AsyncCallback callback, object asyncState);
- CdatenVertrag EndDoIt(IAsyncResult result);
- [OperationContract(AsyncPattern = false)]
- bool Ping();
- }
- }
Das ist eigentlich auch schon alles... Der OperationContract mit bool Ping(); kann auch gelöscht werden...das war eigentlich nur zu Testzwecken...
hier noch ein Paar nähere Infors zu einigen Sachen im Code
Channelfactory (im Viewmodel)
MVVM und MVVM2
Vertrag
WCF
Binding
Ich hoffe ich kann hiermit dem ein oder anderen helfen...Ich hab mir das hier alles selbst beigebracht und es hat einiges an Kraft gekostet durch zu halten, da ich hier nur das Forum hatte, wo ich um Hilfe fragen konnte, weil in meiner Firma keiner mit WCF vertraut ist...
[edit]
Auf Wunsch von @ErfinderDesRades habe ich auf meiner Platte noch zwei Beispiele gesucht (VB.Net und C#)...
das VB-Beispiel ist ein wenig "verkrüpelt" und nicht auf dem aktuellen Stand...
Das C# Beispiel sollte jetzt dem Tut entsprechen
Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Runshak“ ()