I have a large .NET 4.5 class library containing 60+ classes and many public methods. This works as a programmatic interface to my app. Now I would like to invoke this library over the network using WCF. What are the best practices to do this?
A naïve approach would be to wrap my class library with a WCF service library that replicates the class and method structure of the original class library, having one method for each method in it. However, this seems to be overkill and go against the principle to make chunky rather than chatty network interfaces. How should I construct the WCF service library then? What structure should it have? Is there any recognised best practice guidance on this? Thanks.
Don't know if it's a "best" practice, but I've seen this done:
Make the data contract a single method that takes as arguments the operation and an array of parameters. Then, on the server, get the implementing method via reflection and simply pass the array of parameters. Here's a quick sample I've put together (and tested, it works).
Contract:
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;
}
}
Server:
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;
}
}
}
Client:
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();
}
}
Output:
5
result: 5
Parameter [1](Int32 p2)=c3 is incompatible
7
object
parameter can be converted. object
in the parameter array (and they get validated correctly by the above), but they need to be exposed by the service. To include an unused class in the service definition use ServiceKnownType
attribute. Operation
enum on the server and not break the client. Or use a string
for the operation code (and not use complex types as parameters, see 2.) and go wild!!! Multiple versions of servers negotiating with multiple versions of clients, partial server implementations... become possible, obviously requiring some versioning and discovery logic (on a central service?) In conclusion: I got a little carried away at 3. above, and what I'm describing there must be the exact opposite of a best practice for WCF services. If I'm not mistaken, the fact that changing the server breaks the clients is considered one of the advantages of WCF. But I'd still consider the above solution as valid for some scenarios like
quickly wrapping in a service a large library that doesn't change or that the clients don't mind not always getting a response from
allowing for some degree of flexibility when clients are numerous and cannot be updated quickly so different versions need to work in parallel.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.