代码之家  ›  专栏  ›  技术社区  ›  CesarGon

“远程处理”的最佳实践a。NET类库与WCF

  •  3
  • CesarGon  · 技术社区  · 6 年前

    我有一个大的。NET 4.5类库包含60多个类和许多公共方法。这是我的应用程序的编程接口。现在,我想使用WCF通过网络调用这个库。实现这一点的最佳做法是什么?

    一种常见的方法是用WCF服务库包装我的类库,该服务库复制原始类库的类和方法结构,其中每个方法都有一个方法。然而,这似乎有点过头了,违背了制作笨重而不是健谈的网络接口的原则。那么,我应该如何构建WCF服务库呢?它应该有什么结构?是否有任何公认的最佳实践指南?谢谢

    1 回复  |  直到 6 年前
        1
  •  1
  •   Stefan Balan    6 年前

    不知道这是否是一种“最佳”做法,但我见过这样做:

    使数据协定成为一个将操作和参数数组作为参数的单一方法。然后,在服务器上,通过反射获得实现方法,并简单地传递参数数组。这是我整理的一个快速示例(经过测试,它很有效)。

    合同:

    namespace GenericWcfService
    {
        [ServiceKnownType(typeof(Pair))] //there's another way to add more, look it up
        [ServiceContract]
        public interface ICalculatorService
        {
            [OperationContract]
            OperationResult GetResult(Operation op, object[] parameteres);
        }
    
        public enum Operation
        {
            Add,
            Substract,
            Multiply,
            Divide,
            Print,
            AddPair
        }
    
        [DataContract]
        public class OperationResult
        {
            [DataMember]
            public object Result { get; set; }
    
            [DataMember]
            public string Error { get; set; }
        }
    
        [DataContract]
        public class Pair
        {
            [DataMember]
            public int V1;
            [DataMember]
            public int V2;
        }
    }
    

    服务器:

    namespace GenericWcfService
    {
        public class CalculatorService : ICalculatorService
        {
            public OperationResult GetResult(Operation op, object[] parameteres)
            {
                var calc = new CalculatorImpl();
                var method = typeof(CalculatorImpl).GetMethod(op.ToString());
    
                var result = new OperationResult();
                if (method == null) { result.Error = "Incompatible"; return result; }
                var mParameters = method.GetParameters();
                if (mParameters.Length != parameteres.Length) { result.Error = "Incompatible"; return result; }
                for (int i = 0; i < parameteres.Length; i++)
                {
                    try
                    {
                        var paramVal = Convert.ChangeType(parameteres[i], mParameters[i].ParameterType);
                    }
                    catch (Exception)
                    {
                        { result.Error = $"Parameter [{i}]({mParameters[i]})={parameteres[i]} is incompatible"; return result; }
                    }
                }
    
    
                try
                {
                    result.Result = method?.Invoke(calc, parameteres);
                }
                catch (Exception e)
                {
                    result.Error = e.Message;
                }
                return result;
            }
        }
    
        public class CalculatorImpl
        {
            public int Add(int p1, int p2)
            {
                return p1 + p2;
            }
    
            public string Print(string text, int n1)
            {
                return $"{text}: {n1}";
            }
    
            public int AddPair(Pair p)
            {
                return p.V1 + p.V2;
            }
        }
    }
    

    客户:

    class Program
    {
        static void Main(string[] args)
        {
            var calc = new CalculatorServiceClient();
            var result = calc.GetResult(Operation.Add, new object[] { 2, 3 });
            if (string.IsNullOrEmpty(result.Error))
                Console.WriteLine(result.Result);
            else
                Console.WriteLine(result.Error);
    
            result = calc.GetResult(Operation.Print, new object[] { "result", result.Result });
            if (string.IsNullOrEmpty(result.Error))
                Console.WriteLine(result.Result);
            else
                Console.WriteLine(result.Error);
    
            result = calc.GetResult(Operation.Add, new object[] { 2, "c3" });
            if (string.IsNullOrEmpty(result.Error))
                Console.WriteLine(result.Result);
            else
                Console.WriteLine(result.Error);
    
            result = calc.GetResult(Operation.AddPair, new object[] { new Pair { V1 = 3, V2 = 4 } });
            if (string.IsNullOrEmpty(result.Error))
                Console.WriteLine(result.Result);
            else
                Console.WriteLine(result.Error);
    
            Console.ReadKey();
        }
    }
    

    输出:

    5
    result: 5
    Parameter [1](Int32 p2)=c3 is incompatible
    7
    
    1. 我本想提到参数验证,但后来我继续做了,使用反射来验证计数 object 可以转换参数。
    2. 然后我想到了复杂的物体。。。是的,它们可以作为 对象 在参数数组中(通过上述方法可以正确验证),但它们需要由服务公开。要在服务定义中包含未使用的类,请使用 ServiceKnownType 属性
    3. 拥有这种服务定义将打开一个全新的机会级别(对于混乱!:),您可以为 终止 Operation 枚举服务器,而不中断客户端。或使用 string 有关操作代码(不要将复杂类型用作参数,请参见2)尽情享受吧!!!多个版本的服务器与多个版本的客户端协商,部分服务器实现。。。成为可能,显然需要一些版本控制和发现逻辑(在中央服务上?)

    总而言之:我在3点的时候有点神魂颠倒。我所描述的内容必须与WCF服务的最佳实践完全相反。如果我没有弄错的话,更改服务器会破坏客户端这一事实被认为是WCF的优势之一。但我仍然认为上述解决方案对于以下一些场景是有效的

    • 快速包装大型库中的服务 这不会改变 或者客户不介意不总是从

    • 考虑到 一些 客户端数量众多且无法快速更新时的灵活性程度,因此不同版本需要并行工作。