সারসংক্ষেপ
এই উত্তরে বর্ণিত কৌশলগুলি ব্যবহার করে নিম্নলিখিত সিনট্যাক্স সহ যে কোনও একটি ব্যবহার ব্লকে ডাব্লুসিএফ পরিষেবা গ্রহণ করতে পারে:
var channelFactory = new ChannelFactory<IMyService>("");
var serviceHelper = new ServiceHelper<IMyService>(channelFactory);
var proxy = serviceHelper.CreateChannel();
using (proxy as IDisposable)
{
proxy.DoWork();
}
আপনার অবস্থার সাথে সুনির্দিষ্ট আরও সংক্ষিপ্ত প্রোগ্রামিং মডেল অর্জনের জন্য আপনি অবশ্যই এটিকে আরও মানিয়ে নিতে পারেন - তবে মূল বিষয়টি হ'ল আমরা IMyService
চ্যানেলটিকে নিন্দা করার একটি প্রয়োগ তৈরি করতে পারি যা সঠিকভাবে ডিসপোজেবল প্যাটার্নকে কার্যকর করে imple
বিস্তারিত
এখন পর্যন্ত প্রদত্ত সমস্ত উত্তর ডাব্লুসিএফ চ্যানেল প্রয়োগের "বাগ" এর আশেপাশের সমস্যার সমাধান করে IDisposable
। এর জবাব হলো, সবচেয়ে সংক্ষিপ্ত প্রোগ্রামিং (আপনি ব্যবহার করতে সক্ষম হবেন মডেল প্রস্তাব বলে মনে হয় using
অপরিচালিত সম্পদের উপর মীমাংসা করা ব্লক) হয় এই এক যেখানে প্রক্সি বাস্তবায়ন modifed হয় - IDisposable
একটি বাগ-মুক্ত বাস্তবায়ন। এই পদ্ধতির সমস্যাটি হ'ল রক্ষণাবেক্ষণযোগ্যতা - আমাদের ব্যবহৃত প্রক্সির জন্য আমাদের এই কার্যকারিতাটি পুনরায় প্রয়োগ করতে হবে। এই উত্তরের পরিবর্তনে আমরা দেখতে পাব কীভাবে আমরা এই কৌশলটি জেনেরিক করার জন্য উত্তরাধিকারের চেয়ে রচনাটি ব্যবহার করতে পারি ।
প্রথম প্রচেষ্টা
বাস্তবায়নের জন্য বিভিন্ন বাস্তবায়ন রয়েছে বলে মনে হয় IDisposable
, তবে যুক্তির স্বার্থে আমরা বর্তমানে গৃহীত উত্তর দ্বারা ব্যবহৃত একটি রূপান্তর ব্যবহার করব ।
[ServiceContract]
public interface IMyService
{
[OperationContract]
void DoWork();
}
public class ProxyDisposer : IDisposable
{
private IClientChannel _clientChannel;
public ProxyDisposer(IClientChannel clientChannel)
{
_clientChannel = clientChannel;
}
public void Dispose()
{
var success = false;
try
{
_clientChannel.Close();
success = true;
}
finally
{
if (!success)
_clientChannel.Abort();
_clientChannel = null;
}
}
}
public class ProxyWrapper : IMyService, IDisposable
{
private IMyService _proxy;
private IDisposable _proxyDisposer;
public ProxyWrapper(IMyService proxy, IDisposable disposable)
{
_proxy = proxy;
_proxyDisposer = disposable;
}
public void DoWork()
{
_proxy.DoWork();
}
public void Dispose()
{
_proxyDisposer.Dispose();
}
}
উপরের ক্লাসগুলি দিয়ে সজ্জিত আমরা এখন লিখতে পারি
public class ServiceHelper
{
private readonly ChannelFactory<IMyService> _channelFactory;
public ServiceHelper(ChannelFactory<IMyService> channelFactory )
{
_channelFactory = channelFactory;
}
public IMyService CreateChannel()
{
var channel = _channelFactory.CreateChannel();
var channelDisposer = new ProxyDisposer(channel as IClientChannel);
return new ProxyWrapper(channel, channelDisposer);
}
}
এটি আমাদের using
ব্লকটি ব্যবহার করে আমাদের পরিষেবা গ্রাস করতে দেয় :
ServiceHelper serviceHelper = ...;
var proxy = serviceHelper.CreateChannel();
using (proxy as IDisposable)
{
proxy.DoWork();
}
এই জেনেরিক করা
আমরা এ পর্যন্ত যা করেছি তা হ'ল টমাসের সমাধানটি সংস্কার করা । এই কোডটি জেনেরিক হতে বাধা দেয় তা হ'ল ProxyWrapper
আমরা চাই প্রতিটি পরিষেবার চুক্তির জন্য ক্লাসটি পুনরায় প্রয়োগ করতে হবে। আমরা এখন এমন একটি শ্রেণীর দিকে নজর দেব যা আমাদের আইএল ব্যবহার করে এই ধরণের তৈরি করতে দেয়:
public class ServiceHelper<T>
{
private readonly ChannelFactory<T> _channelFactory;
private static readonly Func<T, IDisposable, T> _channelCreator;
static ServiceHelper()
{
/**
* Create a method that can be used generate the channel.
* This is effectively a compiled verion of new ProxyWrappper(channel, channelDisposer) for our proxy type
* */
var assemblyName = Guid.NewGuid().ToString();
var an = new AssemblyName(assemblyName);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName);
var proxyType = CreateProxyType(moduleBuilder, typeof(T), typeof(IDisposable));
var channelCreatorMethod = new DynamicMethod("ChannelFactory", typeof(T),
new[] { typeof(T), typeof(IDisposable) });
var ilGen = channelCreatorMethod.GetILGenerator();
var proxyVariable = ilGen.DeclareLocal(typeof(T));
var disposableVariable = ilGen.DeclareLocal(typeof(IDisposable));
ilGen.Emit(OpCodes.Ldarg, proxyVariable);
ilGen.Emit(OpCodes.Ldarg, disposableVariable);
ilGen.Emit(OpCodes.Newobj, proxyType.GetConstructor(new[] { typeof(T), typeof(IDisposable) }));
ilGen.Emit(OpCodes.Ret);
_channelCreator =
(Func<T, IDisposable, T>)channelCreatorMethod.CreateDelegate(typeof(Func<T, IDisposable, T>));
}
public ServiceHelper(ChannelFactory<T> channelFactory)
{
_channelFactory = channelFactory;
}
public T CreateChannel()
{
var channel = _channelFactory.CreateChannel();
var channelDisposer = new ProxyDisposer(channel as IClientChannel);
return _channelCreator(channel, channelDisposer);
}
/**
* Creates a dynamic type analogous to ProxyWrapper, implementing T and IDisposable.
* This method is actually more generic than this exact scenario.
* */
private static Type CreateProxyType(ModuleBuilder moduleBuilder, params Type[] interfacesToInjectAndImplement)
{
TypeBuilder tb = moduleBuilder.DefineType(Guid.NewGuid().ToString(),
TypeAttributes.Public | TypeAttributes.Class);
var typeFields = interfacesToInjectAndImplement.ToDictionary(tf => tf,
tf => tb.DefineField("_" + tf.Name, tf, FieldAttributes.Private));
#region Constructor
var constructorBuilder = tb.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName,
CallingConventions.Standard,
interfacesToInjectAndImplement);
var il = constructorBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
for (var i = 1; i <= interfacesToInjectAndImplement.Length; i++)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg, i);
il.Emit(OpCodes.Stfld, typeFields[interfacesToInjectAndImplement[i - 1]]);
}
il.Emit(OpCodes.Ret);
#endregion
#region Add Interface Implementations
foreach (var type in interfacesToInjectAndImplement)
{
tb.AddInterfaceImplementation(type);
}
#endregion
#region Implement Interfaces
foreach (var type in interfacesToInjectAndImplement)
{
foreach (var method in type.GetMethods())
{
var methodBuilder = tb.DefineMethod(method.Name,
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig |
MethodAttributes.Final | MethodAttributes.NewSlot,
method.ReturnType,
method.GetParameters().Select(p => p.ParameterType).ToArray());
il = methodBuilder.GetILGenerator();
if (method.ReturnType == typeof(void))
{
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, typeFields[type]);
il.Emit(OpCodes.Callvirt, method);
il.Emit(OpCodes.Ret);
}
else
{
il.DeclareLocal(method.ReturnType);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, typeFields[type]);
var methodParameterInfos = method.GetParameters();
for (var i = 0; i < methodParameterInfos.Length; i++)
il.Emit(OpCodes.Ldarg, (i + 1));
il.Emit(OpCodes.Callvirt, method);
il.Emit(OpCodes.Stloc_0);
var defineLabel = il.DefineLabel();
il.Emit(OpCodes.Br_S, defineLabel);
il.MarkLabel(defineLabel);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
}
tb.DefineMethodOverride(methodBuilder, method);
}
}
#endregion
return tb.CreateType();
}
}
আমাদের নতুন সহায়ক শ্রেণীর সাহায্যে আমরা এখন লিখতে পারি
var channelFactory = new ChannelFactory<IMyService>("");
var serviceHelper = new ServiceHelper<IMyService>(channelFactory);
var proxy = serviceHelper.CreateChannel();
using (proxy as IDisposable)
{
proxy.DoWork();
}
মনে রাখবেন যে আপনি স্বয়ংক্রিয়ভাবে উত্পাদিত ক্লায়েন্টদের ClientBase<>
(ব্যবহারের পরিবর্তে ChannelFactory<>
) উত্তরাধিকার সূত্রেও একই কৌশল (সামান্য পরিবর্তন সহ) ব্যবহার করতে পারেন, বা যদি আপনি IDisposable
চ্যানেলটি বন্ধ করতে কোনও আলাদা বাস্তবায়ন ব্যবহার করতে চান ।