简体   繁体   中英

Generate Server Side WCF Service automatically from existing API

How would a person go about exposing method per method an API comprised of several classes through WCF without using a WCF project.

For example, let's say I have the following

public interface RainfallMonitor
{
    [ExposeToWeb]
    void RecordRainfall(string county, float rainfallInches);

    [ExposeToWeb]
    float GetTotalRainfall(string county);

    void ClearRainfall(string county);
}

I understand I could create a WCF service library as usual and just add a WCF service called "RainfallMonitor".

What I'm exploring is... is it possible/reasonable to somehow generate all of the WCF related code at compile time for an entire API without actually making the classes WCF services. Possibly using attributes such as ExposeToWeb to denote which methods to expose via the services. The resultant would function like this:

  1. Create/modify classes in project called RainfallAPI
  2. Compile and have another project/dll generated called RainfallService automatically.

Essentially:

  • If this is possible what approach could I take to actually implement it?
  • What serious pitfalls could I run into?
  • Is there any existing codebase that does something similar I could look into for inspiration

For clarification: I am not asking about auto-generating the client stub, I am asking about creating services on the server side.

I recently head of this library: Fody . As I understand, it makes it possible to hook into the build process and inject IL into the assembly. I'm not completely sure how it works, but it might be possible to search though the IL, find all the methods with the ExposeToWeb attribute and use that to emit the contract for the WCF service in to the assembly.

But on the other hand, if you are already adding attributes to the class, why not just add the correct WFC attributes to begin with, and then use SvcUtil to generate the contracts in post build?

EDIT: Here is an example of how you could use svcutil :

C#:

[ServiceContract]
public interface IRainfallMonitor
{
    [OperationContract]
    void RecordRainfall(string county, float rainfallInches);
}

public class RainfallMonitor : IRainfallMonitor
{
    public void RecordRainfall(string county, float rainfallInches)
    {
        // code
    }
}

post build PowerShell:

$svcutil = "C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\SvcUtil.exe"
$csc = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe"
$assembly = "bin/debug/ProjectWithoutWCF.dll"
$service = "ProjectWithoutWCF.RainfallMonitor"
$outputns = "ProjectWithoutWCF.RainfallMonitor.Service"
$outputdir = "bin/debug"

md svcutil_tmp
cd svcutil_tmp

& $svcutil /serviceName:"$service" "../$assembly"
& $svcutil *.wsdl *.xsd /importxmltypes /out:"output.cs" /n:"*,$outputns"
& $csc /target:library /out:$outputns.dll "output.cs"

cp "$outputns.dll" "../$outputdir"
cp output.config "../$outputdir/$outputns.dll.config"
cd ..
rm -r .\svcutil_tmp

and you will need something like this in you project config:

<system.serviceModel>
  <services>
    <service name="ProjectWithoutWCF.RainfallMonitor" >
      <endpoint address="" binding="basicHttpBinding" contract="ProjectWithoutWCF.IRainfallMonitor">
      </endpoint>
    </service>
  </services>
</system.serviceModel>

Its a little fiddly and you will most likely need some tweaking to the script and the config. But the result is that you have a ProjectWithoutWCF.RainfallMonitor.Service.dll file with the WCF service contracts.

For your question regarding "What I'm exploring is... is it possible/reasonable to somehow generate all of the WCF related code at compile time for an entire API without actually making the classes WCF services.", there are options, but they will all take a lot of work.

You would need to write a code generation application/library that utilizes classes such as CSharpCodeProvider (there is one for VB as well) and reflection to inspect your library as a post build step, build the code that you want in memory, and save it off as a DLL.

This application would need to find a custom attribute you create that indicates that it should be a WCF service, and output WCF code based upon the rules you define.

In effect you need to write code using the CodeDOM model, which requires thinking about code in a much different fashion. Not everyone is able to abstract their thinking to that level.

Keep in mind that utilizing the CodeDOM model, you can overcome some of the issues that nodots mentioned such as requiring serializable for data contracts. This could be added and a new DLL be output by your CodeDOM based reflection library.

Through thoughtful design and quite a bit of work, it is possible to achieve the output you are seeking. It is just a dark road with lots of pitfalls to get there.

Yes, this can be done, with moderate effort using the right tools. If you have Visual Studio, you already have Microsoft's T4 code generator. It allows you to generate code by writing "text templates", which are very reminiscent of ASP.NET's RAZOR syntax. Using T4, you can actually instantiate your existing classes and use reflection to read all the class names and method signatures, and ultimately generate your WCF services. It's not that hard!

Here's a sample T4 template from Oleg Sych's tutorial :

<#@ template language=“C#v3.5” #>
<#@ output extension=“SQL” #>
<#@ assembly name=“Microsoft.SqlServer.ConnectionInfo” #>
<#@ assembly name=“Microsoft.SqlServer.Smo” #>
<#@ import namespace=“Microsoft.SqlServer.Management.Smo” #>
<#
    Server server = new Server();
    Database database = new Database(server, “Northwind”);
    Table table = new Table(database, “Products”);
    table.Refresh();
#>
create procedure <#= table.Name #>_Delete
<#
    PushIndent(”\t”);
    foreach (Column column in table.Columns)
    {
        if (column.InPrimaryKey)
            WriteLine(”@” + column.Name + ” ” + column.DataType.Name);
    }
    PopIndent();
#>
as
    delete from <#= table.Name #>
    where
<#
    PushIndent(”\t\t”);
    foreach (Column column in table.Columns)
    {
        if (column.InPrimaryKey)
            WriteLine(column.Name + ” = @” + column.Name);
    }
    PopIndent();
#>

The output would look like this:

create procedure Products_Delete
    @ProductID int
as
    delete from Products
    where ProductID = @ProductID

Of course, your example, you'd use reflection on your existing class library instead of sql queries. The WCf services that you generate can simply call your existing library, so that you don't have to copy all the actual domain logic.

MSDN

https://msdn.microsoft.com/en-us/library/bb126445.aspx

You can use Roslyn, Roslyn is new compiler as service, where in you can parse C# file and generate source code as you need.

You can see some examples here,

http://www.codeproject.com/Articles/302595/Roslyn-CTP-Three-Introductory-Projects

It should not be that difficult.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM