一种减少冗余代码的泛型方法

时间:2016-05-08 04:51:09

标签: c# generics

我正在使用由@jweber

创建的名为WcfClientProxyGenerator的库

我使用nuget生成针对服务的代理。一切正常,这里是一个代理类:

using Silly.Services.CarPark.Logging;
using Silly.Services.CarPark.ServiceContracts.Common.v1;
using log4net;
using System.Linq;
using System.Threading.Tasks;
using WcfClientProxyGenerator;

namespace Silly.Services.CarPark.Client
{
    public class RequestManager
    {
        private static Log4NetLoggingAdapter systemLogger;
        private static IRequestService proxy = null;

        public RequestManager()
        {
            if (systemLogger == null)
            {
                LoggerSetup.Configure(Constants.ModuleNameForLogging);
                systemLogger = new Log4NetLoggingAdapter(LogManager.GetLogger(typeof(RequestManager)));
            }

            proxy = WcfClientProxy.Create<IRequestService>(c =>
            {
                c.SetEndpoint("BasicHttpBinding_IRequestService");

                c.OnCallBegin += (sender, args) => { };

                c.OnBeforeInvoke += (sender, args) =>
                {
                    systemLogger.Write(string.Format("{0}.{1} called with parameters: {2}", args.ServiceType.Name, args.InvokeInfo.MethodName, string.Join(", ", args.InvokeInfo.Parameters)), Core.EventSeverity.Verbose);
                };

                c.OnAfterInvoke += (sender, args) =>
                {
                    systemLogger.Write(string.Format("{0}.{1} returned value: {2}", args.ServiceType.Name, args.InvokeInfo.MethodName, args.InvokeInfo.ReturnValue), Core.EventSeverity.Verbose);
                };

                c.OnCallSuccess += (sender, args) =>
                {
                    systemLogger.Write(string.Format("{0}.{1} completed successfully", args.ServiceType.Name, args.InvokeInfo.MethodName));
                };

                c.OnException += (sender, args) =>
                {
                    systemLogger.Write(string.Format("Exception during service call to {0}.{1}: {2}", args.ServiceType.Name, args.InvokeInfo.MethodName, args.Exception.Message), args.Exception, Core.EventSeverity.Error);
                };
            });
        }
        }
}

过了一会儿,我发现自己还有三个服务和三个代理。我看到我重复所有四个类的构造函数中的任何内容,并且很难跟踪更改。现在,记录的数量是裸的,但这种情况在不断变化。还有三项服务即将开始。

有没有办法使用Class来取出公共代码?

3 个答案:

答案 0 :(得分:2)

依赖注入并未解决代码重复的主要问题,因为systemLoggerproxy都依赖于服务接口的类型,在本例中为IRequestService。因此,即使您从外部传递这些内容&#34;,您仍然需要为要构建的每个服务接口设置不同的代码,以创建传递的对象。

泛型确实是捕捉共性的方法,因为每个服务的重复代码的唯一区别是接口类型。我将使用通用基类,并从中派生每个服务类,类似于我在下面显示的内容。

出于可测试性原因,您可能仍需要考虑使用依赖项注入来避免新增日志记录适配器。但这是另一个问题。

基础课程

using log4net;
using System.Linq;
using System.Threading.Tasks;
using WcfClientProxyGenerator;
using Silly.Services.CarPark.Logging;

namespace Silly.Services.Carpark
{
    public class ClientBase<TClient> where TClient : class
    {
        private static Log4NetLoggingAdapter systemLogger;
        private static TClient proxy;

        public ClientBase(string binding)
        {
            if (systemLogger == null)
            {
                LoggerSetup.Configure(Constants.ModuleNameForLogging);
                systemLogger = new Log4NetLoggingAdapter(LogManager.GetLogger(this.GetType()));
            }

            proxy = WcfClientProxy.Create<TClient>(c =>
            {
                c.SetEndpoint(binding);

                c.OnCallBegin += (sender, args) => { };

                c.OnBeforeInvoke += (sender, args) =>
                {
                    systemLogger.Write(string.Format("{0}.{1} called with parameters: {2}", args.ServiceType.Name, args.InvokeInfo.MethodName, string.Join(", ", args.InvokeInfo.Parameters)), Core.EventSeverity.Verbose);
                };

                c.OnAfterInvoke += (sender, args) =>
                {
                    systemLogger.Write(string.Format("{0}.{1} returned value: {2}", args.ServiceType.Name, args.InvokeInfo.MethodName, args.InvokeInfo.ReturnValue), Core.EventSeverity.Verbose);
                };

                c.OnCallSuccess += (sender, args) =>
                {
                    systemLogger.Write(string.Format("{0}.{1} completed successfully", args.ServiceType.Name, args.InvokeInfo.MethodName));
                };

                c.OnException += (sender, args) =>
                {
                    systemLogger.Write(string.Format("Exception during service call to {0}.{1}: {2}", args.ServiceType.Name, args.InvokeInfo.MethodName, args.Exception.Message), args.Exception, Core.EventSeverity.Error);
                };
            });
        }

        // The following class property exposes static private var
        // to derived classes, as read-only. 
        protected TClient Proxy { get { return proxy; } }
    }
}

派生服务类

using Silly.Services.CarPark;
using Silly.Services.CarPark.ServiceContracts.Common.v1;
using log4net;
using System.Linq;
using System.Threading.Tasks;
using WcfClientProxyGenerator;

namespace Silly.Services.CarPark.Client
{
    public class RequestManager : ClientBase<IRequestService>, IRequestService
    {
        public RequestManager() : 
              base("BasicHttpBinding_IRequestService")
        {
        }

        // Implementation of IRequestService methods
        public string Alpha(...) {
          return Proxy.Alpha(...);  
        }

        public void Beta(...) {
              Proxy.Beta(...);
        }

    }
}

答案 1 :(得分:1)

你自己上来了什么?我的意思是,应该直截了当地做这样的事情。

Create方法接受Action<IRetryingProxyConfigurator>作为参数,因此我将构建一个类似

的方法
void ConfigureLogging(IRetryingProxyConfigurator proxyConfigurator)
{
    proxyConfigurator.OnBeforeInvoke += (sender, args) =>
            {
                systemLogger.Write(string.Format("{0}.{1} called with parameters: {2}", args.ServiceType.Name, args.InvokeInfo.MethodName, string.Join(", ", args.InvokeInfo.Parameters)), Core.EventSeverity.Verbose);
            };

}

并称之为:

proxy = WcfClientProxy.Create<IRequestService>(c =>
        {
            c.SetEndpoint("BasicHttpBinding_IRequestService");

            ConfigureLogging(c);
        }

尽可能多地重复代码重复代码并形成一个模式。您可以构建一个通用工厂,它接受服务接口作为类型参数,并在该类中进行所有模板化。

答案 2 :(得分:1)

请注意,鉴于此处正在创建所有绑定,所有这些服务都必须实现通用接口。使用它作为T,你应该没有太多的重构

protected void Page_Load(object sender, EventArgs e) {
    BLSecurity BLSecurity = new BLSecurity();

    wk_Gebruiker gebruiker = (wk_Gebruiker)Session["gebruiker"];
    if (gebruiker != null) {
        LabelTest.Text = gebruiker.voornaam;
    } else {
        LabelTest.Text = "no name";
    }
    BLSecurity.Gebruiker = gebruiker;

    ...
}

这可能无法编译,您可能需要更改内容,但我认为意图应该在那里