Laden...

Proxy-Klassen erstellen

Erstellt von david.ka vor 13 Jahren Letzter Beitrag vor 13 Jahren 1.787 Views
D
david.ka Themenstarter:in
343 Beiträge seit 2005
vor 13 Jahren
Proxy-Klassen erstellen

Hallo,

ich arbeite mich gerede etwas in wcf ein und verwende in meiner Anwendung das nettcpbinding.
kann ich mir hier auch Proxy-Klassen erstellen? wenn ja, womit?
oder ist es sinnvoller mir der ChannelFactory zu arbeiten?

Grüße
David

Programming is like sex: One mistake and
you have to support it your lifetime

6.910 Beiträge seit 2009
vor 13 Jahren

Hallo,

mit der svcutil.exe kannst du dir einen Proxy generieren lassen der von ClientBase<T> erbt. Bei der Instanziierung des Proxy kannst du dann das Binding angegeben. zB


using (MyGeneratedProxy proxy = new MyGeneratedProxy(new NetTcpBinding()))
{
    //
}

Ev. muss im Ctor auch noch der Endpunkt angegeben werden, weiß ich gerade nicht auswendig.

Ich arbeite eigentlich immer direkt mit der ChannelFactory bzw. mit einer eigenen ProxyBase die ich dafür erstellt habe. Als Gründe dafür sprechen u.a.*Keine Codedoppelung (wenn auch generiert) für die Contracts -> diese können per Assembly im Server und Client referenziert werden. Das ermöglicht es nicht nur Eigenschaften in den Klassen zu verwenden sondern auch Methoden, dadurch erhöht sich für mich die Transparenz in der Verwendung => es gibt keinen Unterschied zu "normalen" CLR-Klassen. *Ich kann das Proxy so gestalten wie ich will. ZB bei Verwendung von Callbacks kann ich das im selbst erstellten Client in eine "normales" CLR-Event überführen => wieder ganz transparent in der Verwendung. Selbiges gilt für das Async-Pattern das mit den BeginXYZ/EndXYZ nicht wikrklich praktisch ist und somit in eine Event-based-Pattern übertragen wird. Oder grundlegender: Ich kann damit die FaultException<T> in normale Expections überführen was wiederum die Transparenz in der Verwendung erhöht.

Also kurz: der Proxy lässt sich wie ein normale CLR-Klasse vewenden und dass dahinter ein WCF-Serviceaurfur steckt wird komplett wegabstrahiert.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

D
david.ka Themenstarter:in
343 Beiträge seit 2005
vor 13 Jahren

Vielen Dank für Deine Antwort.

eine Frage habe ich aber noch:
wie erstelle ich das mit svcutil? muss ich das Binding des Host dann beim erstellen auf wsHttpBinding umstellen? oder kann ich da direkit die tcp adresse angeben?

Programming is like sex: One mistake and
you have to support it your lifetime

6.910 Beiträge seit 2009
vor 13 Jahren

Hallo,

svcutil wird auch im VS beim "Add Service Reference" verwendet. Da ich das so gut wie nie verwende weiß ich es nicht mehr genau, aber soweit ich mich erinnere muss ein MEX-Endpunkt (für Metadata-Exchange) definiert sein. Suche mal danach mit dem Stichwort "mexendpoint".

Ich würds trotzdem selber coden, mittlerweile wärst du damit schon fertig 😁

Edit: das gibts jetzt auch als Komponente in WCF: Basisklasse für einen Proxy

So grob schaut mein Proxy aus:


using System;
using System.Diagnostics.Contracts;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Xml;

namespace gfoidl.XXX.Service.Utils
{
	public abstract class ProxyBase<TChannel> : IDisposable
		where TChannel : ICommunicationObject
	{
		private readonly ChannelFactory<TChannel> _channelFactory;
		private readonly TChannel _proxy;
		//---------------------------------------------------------------------
		protected ChannelFactory<TChannel> ChannelFactory { get { return _channelFactory; } }
		protected TChannel Proxy { get { return _proxy; } }
		//---------------------------------------------------------------------
		#region Ctor
		protected ProxyBase(string endpointAddress) :
			this(endpointAddress, false)
		{ }
		//---------------------------------------------------------------------
		protected ProxyBase(string endpointAddress, bool namedPipes)
			: this(endpointAddress, namedPipes, 16384)
		{ }
		//---------------------------------------------------------------------
		protected ProxyBase(string endpointAddress, int maxSizeInBytes)
			: this(endpointAddress, false, maxSizeInBytes)
		{ }
		//---------------------------------------------------------------------
		protected ProxyBase(string endpointAddress, bool namedPipes, int maxSizeInBytes)
		{
			Contract.Requires<ArgumentNullException>(!string.IsNullOrWhiteSpace(endpointAddress));
			Contract.Requires<ArgumentOutOfRangeException>(maxSizeInBytes > 0);
			//-----------------------------------------------------------------
			Binding binding = namedPipes ?
				//new NetNamedPipeBinding() as Binding :
				new NetNamedPipeBinding
				{
					ReaderQuotas           = new XmlDictionaryReaderQuotas { MaxArrayLength = maxSizeInBytes },
					MaxReceivedMessageSize = maxSizeInBytes
				} as Binding :
				new NetTcpBinding
				{
					ReaderQuotas           = new XmlDictionaryReaderQuotas { MaxArrayLength = maxSizeInBytes },
					MaxReceivedMessageSize = maxSizeInBytes
				} as Binding;

			_channelFactory = new ChannelFactory<TChannel>(
				binding,
				new EndpointAddress(endpointAddress));

			_channelFactory.Open();
			_proxy = _channelFactory.CreateChannel();
			_proxy.Open();
		}
		#endregion
		//---------------------------------------------------------------------
		#region IDisposable Members
		private bool _isDisposed = false;
		//---------------------------------------------------------------------
		protected virtual void Dispose(bool disposing)
		{
			if (!_isDisposed)
			{
				if (disposing)
				{
					_proxy.Close();
					_channelFactory.Close();
				}

				_isDisposed = true;
			}
		}
		//---------------------------------------------------------------------
		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}
		#endregion
	}
}

Anmerkung(en):*Der Proxy entspricht nicht ganz dem GoF-Pattern Proxy, denn hier wird das ICommunicationObjekt in Form des Channels eager anstatt lazy erstellt. Das stört mich jedoch nicht da der Proxy eh nur dann erstellt wird wenn auch eine Kommunikation erfolgen soll. *Die Angabe von maxSizeInBytes wird auch 1:1 für MaxArrayLength in den XmlDictionaryReaderQuotas verwendet, obwohl nicht unbedingt ein Zusammenhand in dieser Hinsischt besetht, aber es funktioniert so ganz gut 😉

Ein konkreter Proxy am Beispiel eine FooServices:


using System;
using System.Collections.Generic;
using System.ServiceModel;
using gfoidl.XXX.FooService.Contracts;
using gfoidl.XXX.Service.Utils;

namespace gfoidl.XXX.FooService.Client
{
	public interface IFooServiceChannel : IFooService, ICommunicationObject { }
	//-------------------------------------------------------------------------
	public sealed class FooServiceProxy : ProxyBase<IFooServiceChannel>, IFooService
	{
		public FooServiceProxy(string endpointAddress) 
			: base(endpointAddress) 
		{ }
		//---------------------------------------------------------------------
		public FooServiceProxy(string endpointAddress, bool namedPipes)
			: base(endpointAddress, namedPipes)
		{ }
		//---------------------------------------------------------------------
		#region IFooService Members
		public string Hello()
		{
			return this.Proxy.Hello();
		}
		//---------------------------------------------------------------------
		public IList<Foo> GetFoo()
		{
			try
			{
				return this.Proxy.GetFoo();
			}
			catch (FaultException<Exception> ex)
			{
				throw ex.Detail;
			}
		}
		#endregion
	}
}

Anmerkung(en):*IFooServiceChannel ist notwendig damit die generische Einschränkung der ProxyBase (ICommunicationObject) befriedigt ist. ICommunicationObject ist nämlich praktisch damit Open, Close, etc. direkt aufgerufen werden kann - sonst müsste gecastet werden. *Die Hello-Methode verwende ich beim Start von UI-Programmen sozusagen als "Ping" um die Verbindung zu Prüfen. *Hier siehst du auch wie die FaultException<Exception> in eine normale Exception gewandelt wird -> ist für die Anwenderklasse einfacher zu handhaben. Anstatt Exception kann natürlich auch jede spezifischere Exception behandelt werden. Exception selbst habe ich jedoch beim WCF immer dabei da sonst die CommunicationException nix gescheites hergibt.

Verwendung:


using (FooServiceProxy fooService = new FooServiceProxy("net.tcp://IPAddress:Port/fooService))
{
    string helloMsg = fooService.Hello();
}

Noch als Hinweis für die Erstellung der Service-Klassen (auch wenns nicht ganz zum Thema passt): Erstell die Service-Klasse so dass sie quasi nur eine Fassade für eine (mehrere) dahinterliegende Klasse(n) ist, ganz ohne WCF-Zeugs. Somit hast du den Vorteil dass diese Klasse ganz normal Unit-getestet werden kann (was ja sonst mit WCF-Zeugs schwierig ist).

Die obige ProxyBase<T>-Klasse ist wiederverwendbar und diese kannst du gerne verwenden (wenn du willst 😉.
Irgendwie findet dabei von WCF auch Caching der Channels statt (wie genau hab ich noch nicht exakt untersucht).

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"