不知道这是否是一种“最佳”做法,但我见过这样做:
使数据协定成为一个将操作和参数数组作为参数的单一方法。然后,在服务器上,通过反射获得实现方法,并简单地传递参数数组。这是我整理的一个快速示例(经过测试,它很有效)。
合同:
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
-
我本想提到参数验证,但后来我继续做了,使用反射来验证计数
object
可以转换参数。
-
然后我想到了复杂的物体。。。是的,它们可以作为
对象
在参数数组中(通过上述方法可以正确验证),但它们需要由服务公开。要在服务定义中包含未使用的类,请使用
ServiceKnownType
属性
-
拥有这种服务定义将打开一个全新的机会级别(对于混乱!:),您可以为
终止
的
Operation
枚举服务器,而不中断客户端。或使用
string
有关操作代码(不要将复杂类型用作参数,请参见2)尽情享受吧!!!多个版本的服务器与多个版本的客户端协商,部分服务器实现。。。成为可能,显然需要一些版本控制和发现逻辑(在中央服务上?)
总而言之:我在3点的时候有点神魂颠倒。我所描述的内容必须与WCF服务的最佳实践完全相反。如果我没有弄错的话,更改服务器会破坏客户端这一事实被认为是WCF的优势之一。但我仍然认为上述解决方案对于以下一些场景是有效的