trying to expose my Service layer as WCF - wcf

I have my service layer, entities and DTOS in a separeted assembly called CCL.Data
The problem:
All My application is referencing the service layer directly using interfaces and IoC.
For example, I have an interface in my CCL.Data assembly called ICustomerService it depends on ICustomerRepository that depends on MyContext. All my application is referencing ICustomerService to call its methods....... so far no problem.
So I created a WCF Project.... referencing CCL.Data in this project....
I create a new Service, but int this case below, I would need to change all points in my application that call ICustomerService to WCFCustomerServiceClient, does exists a better way without cause a big impact in my project?
[ServiceContract]
public interface IWCFCustomerService
{
[OperationContract]
CustomerDTO GetCustomerById(int id);
}
public class WCFCustomerService : IWCFCustomerService
{
ICustomerService _customerService;
public WCFCustomerService()
{
MyContext context = new MyContext();
ICustomerRepository customerRep = new CustomerRepository(context);
_customerService = new CustomerService(customerRep);
}
public CustomerDTO GetCustomerById(int id)
{
return _customerService.GetCustomerById(id);
}
}
Tks,
William

Do you need to redefine IWCFCustomerService in place of ICustomerService? Is it not possible just to add ServiceContract attributes to your original ICustomerService interface (they will just get ignored by non WCF code)? (Its true that this does give you a dependancy on ServiceModel - but I cant see a way out of that).
Note also that if you use a ServiceRefernce to generate proxy code then the code generated will include a your service interface in different namespace for use clientside. Its worth noting that your not abliged to use that version of the interface (which could be annoying if you have a proxy and not proxy implimentation) but can still use the org interface definition either from a dll or compiled into your client.

Related

Adding DbContext for list of context types at runtime

To implement a plug-in system in a AspNet Core Mvc app, I would like a non-generic method to add a data context from a list of assemblies loaded dynamically at runtime, taking a Type parameter like this:
foreach(Type tp in pluginContexts)
{
services.AddDbContext(tp, options => ...);
}
instead of the usual
services.AddDbContext<PluginDataContext>(options => ...);
That's because for dynamically loaded assemblies, I can not provide the TContext type parameter to the AddDbContextPool method, since that's statically compiled and not available at compile time.
Background
This is for a larger Asp.Net Core MVC app. The plugins must be able to both access the main database of the overall app and a separate database of their own.
Plugin assemblies, containing domain code and their private database context are to be dropped in a specified directory.
The main app loads the plugin assembly dynamically upon startup.
The way I am solving this now is to have each controller get the IConfiguration instance injected, obtain the appropriate connection string from the config, and the database context is instantiated in the controller. Not so nice but does work.
One can easily inject a general class into the Services collection with AddScoped<>, and then use it as a sort of ServiceLocator - however, that is considered an antipattern.
I looked into the source code for AddDbContext but honestly I am lost.
Is there any simple way to achieve this?
Solved it by creating an extensibility point in the plugin assembly.
Define an interface in the main app, which all plugins must implement.
public interface IPluginContextRegistration
{
void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction);
String GetDatabaseName();
}
Create a class implementing this interface (in the plugin). It has access to the type of its private database context, thus can use the generic AddDbContext method:
public class DatabaseRegistration : IPluginContextRegistration
{
public void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction)
{
services.AddDbContext<Test1DbContext>(optionsAction);
}
public String GetDatabaseName()
{
return "test-plugin-db";
}
}
Then in the main app ASP.Net Startup.cs file, add following code, which calls the RegisterContext() method for each plugin. For example, if you want to use Sql Server:
void RegisterPluginDbContexts(ref IServiceCollection services, List<Assembly> assemblyList)
{
IEnumerable<IPluginContextRegistration> registrars = new List<IPluginContextRegistration>();
foreach (Assembly assembly in assemblyList)
{
registrars = registrars.Concat(GetClassInstances<IPluginContextRegistration>(assembly));
}
foreach (var reg in registrars)
{
String name = reg.GetDatabaseName();
String connStr = Configuration.GetConnectionString(name);
reg.RegisterContext(ref services, options => options.UseSqlServer(connStr));
}
}
For completeness - the method "GetClassInstances" is just a helper method using Reflection to obtain an instance of classes implementing the specified interface.
So it's simple after all - no need for re-writing framework code .

Register WCF proxy wrapper using Unity

I am trying to consume WCF in my MVC web app. I have implemented the channel factory for instantiating the proxy client.
I am stuck at a point. Here is the code highlight -
I created a proxy base class where i am creating the channel :
public abstract class ServiceProxyBase<T> : IDisposable where T : class
For creating teh proxy wrapper class i have inherited this base class as :
public class ProxyWrapper : ServiceProxyBase<IMyService>,IMyService
Here "IMyService" is the WCf contract.
Now, in the controllers i have added overloaded constructors as :
public class AccountController : Controller
{
private IMyService businessService;
public AccountController(IMyService _businessService)
{
this.businessService = _businessService;
}
}
For injecting dependency I have included unity.mvc4 package.
It works fine when I am using the following code :
container.RegisterType<IMyService, ProxyWrapper>();
This works as long as the ProxyWrapper is inheriting the IMyService interface directly. If i remove the inheritance like
public class ProxyWrapper : ServiceProxyBase<IMyService>
it gives an error while registering type.
I would like to have a way without inherting the contract in the proxy wrapper. I have spent almost a day trying to fix this. But am able to figure out a solution.
Please give your valuable suggestions on this.
If I understand correctly, your application is using a WCF service but the functionality your application needs is limited compared to the functionality that the service offers (it contains more methods than you need). According to the Interface Segregation Principle, "no client should be forced to depend on methods it does not use" and the Dependency Inversion Principle states that clients own the abstraction.
In other words, you should define your own interface that the application should use and define an implementation that wraps (i.e. composition over inheritance) the generated WCF proxy class.
For instance:
public interface IMyApplicationService
{
object GetStuff();
void PutStuff(object instance);
}
public class MyServiceApplicationProxy : IMyApplicationService
{
private readonly ProxyWrapper wcfProxy;
public MyServiceApplicationProxy(ProxyWrapper wcfProxy) {
this.wcfProxy = wcfProxy;
}
public object GetStuff() {
return this.wcfProxy.GetStuff();
}
public void PutStuff(object instance) {
this.wcfProxy.PutStuff(instance);
}
}
To make application development easier, makes your code easier to read, maintain and test.
You might even want to change the methods of your interface to better suit your application needs. Remember: the client defines the interface! So that might mean that you need to do more mapping inside the MyServiceApplicationProxy class to map adapt your core domain to the contract of the external web service. Don't let the external WCF service's contract leak into your core domain.

WCF DataContract centralization

I have several WCF services that use the same dataContracts to interchange data between them.
Can I have a sort of Metadata Endpoint for centralizing the management of all this schemas?
When creating a new WCF service, how can i make it to load the schemas from the Metadata Endpoint (instead of redefining them) to use inside its operationContracts?
Thanks!
As I said in the comment, I'm not sure I understand your question (at least with regards to wanting a metadata endpoint), but it sounds like you want to have the ability to reuse existing data contracts in multiple different services.
The easiest way to do that is to put the data contracts in a separate assembly (class library - DLL), and then have the services reference that assembly - they don't have to be defined inside the service itself.
Here's a trivial example (code is for illustration only, not intended to actually do anything or be a complete sample):
MyDataContracts assembly
namespace MyDataContracts
{
[DataContract]
public class DataContract1
{
}
[DataContract]
public class DataContract2
{
}
}
MyService1 assembly
using MyDataContracts;
namespace MyService1
{
[ServiceContract]
public interface IMyService1
{
[OperationContract]
DataContract1 GetSomeData(string someParm);
}
}
MyService2 assembly
using MyDataContracts;
namespace MyService2
{
[ServiceContract]
public interface IMyService2
{
[OperationContract]
void SendSomeData(DataContract1 dc);
[OperationContract]
DataContract2 GetSomeOtherData(int parm);
}
}
Of course, each of the WCF service projects will need to include a reference to the MyDataContracts assembly as well.
Now whenever you create a new service, you simply add a reference to the MyDataContracts.dll and you can reuse your data contracts in it.

Extension methods in a data contract

Can we two WCF services where one service contract derives from another and have an extension method inside the derived contract. what will be the result of accessing this contract from the WCF Client. I.e. what will happen if IDServiceis accessed
E.g.
[ServiceContract]
public interface IBaseService
{
public void A();
...
}
[ServiceContract]
public interface IDService: IBaseService
{
public static void B(this IBaseService S);
....
}
You can't define static methods in an interface (nor the access modifier public which you've specified above either).
A good question - got me to a lot of head scratching.
Extension method is meaningless to WCF - and WSDL for that matter.
If you use Service Reference to generated the client, you would not see the extension method (since WSDL would not know anything about the extension method) so you cannot use it.
If you use DLL/Project reference, your code will be called locally and not through the proxy.

Object hierarchy returned by WCF Service is different than expected

My understanding may be wrong, but I thought once you applied the correct attributes the DataContractSerializer would render fully-qualified instances back to the caller.
The code runs and the objects return. But oddly enough, once I look at the returned objects I noticed the namespacing disappeared and the object-hierarchy being exposed through the (web applications) service reference seems to become "flat" (somehow). Now, I expect this from a web-service…but not through WCF. Of course, my understanding of what WCF can do may be wrong.
...please keep in mind I'm still experimenting with all this.
So my questions are…
Q: Can I do something within the WCF Service to force the namespacing to render through the (service reference) data client proxy?
Q: Or perhaps, am I (merely) consuming the service incorrectly?
Q: Is this even possible?
The service code looks like…
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class DataService : IFishData
{
public C1FE GetC1FE(Int32 key)
{
//… more stuff here …
}
public Project GetProject(Int32 key)
{
//… more stuff here …
}
}
[ServiceContract]
[ServiceKnownType(typeof(wcfFISH.StateManagement.C1FE.New))]
[ServiceKnownType(typeof(wcfFISH.StateManagement.Project.New))]
public interface IFishData
{
[OperationContract]
C1FE GetC1FE(Int32 key);
[OperationContract]
Project GetProject(Int32 key);
}
[DataContract]
[KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class Project
{
[DataMember]
public wcfFISH.StateManagement.ObjectState ObjectState { get; set; }
//… more stuff here …
}
[DataContract]
KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class C1FE
{
[DataMember]
public wcfFISH.StateManagement.ObjectState ObjectState { get; set; }
//… more stuff here …
}
[DataContract(Namespace = "wcfFISH.StateManagement")]
[KnownType(typeof(wcfFISH.StateManagement.C1FE.New))]
[KnownType(typeof(wcfFISH.StateManagement.Project.New))]
public abstract class ObjectState
{
//… more stuff here …
}
[DataContract(Namespace = "wcfFISH.StateManagement.C1FE", Name="New")]
[KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class New : ObjectState
{
//… more stuff here …
}
[DataContract(Namespace = "wcfFISH.StateManagement.Project", Name = "New")]
[KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class New : ObjectState
{
//… more stuff here …
}
The web application code looks like…
public partial class Fish_Invite : BaseForm
{
protected void btnTest_Click(object sender, EventArgs e)
{
Project project = new Project();
project.Get(base.ProjectKey, base.AsOf);
mappers.Project mapProject = new mappers.Project();
srFish.Project fishProject = new srFish.Project();
srFish.FishDataClient fishService = new srFish.FishDataClient();
mapProject.MapTo(project, fishProject);
fishProject = fishService.AddProject(fishProject, IUser.UserName);
project = null;
}
}
In case I’m not being clear…
The issue arises in that the namespacing I expect to see (returned) is different from what is actually returned.
fishProject.ObjectState SHOULD look like...
srFish.StateManagement.Project.New
fishC1FE.ObjectState SHOULD look like...
srFish.StateManagement.C1FE.New
fishProject.ObjectState ACTUALLY looks like...
srFish.New1
fishC1FE.ObjectState ACTUALLY looks like...
srFish.New
OK - default behavior for a WCF Service is this:
you define your service contracts, operations, and data contract on the server (e.g. in namespace "Server.MyService")
once the service is up and running, on your client, you create a service reference
when doing so, what Visual Studio or svcutil.exe do, is interrogate that service for its metadata (description of service methods and data)
based on that metadata, the client side proxy is generated (namespace "Client.MyService") and it contains replicas of the service contract (the methods) and the data contract
Important: it contains replicas of those things! They look the same, and they serialize into the same XML format on the wire - but they are different - in different namespaces, most notably.
This is the very nature of WCF - all you do is exchange serialized messages between client and server - all that goes back and forth are textual messages. Nothing more - no object references, no remote object - nothing like that. Toss that out of your mind! :-)
If you control both ends of the wire, this can be a pain - if you need to change anything, you have to change it on the server side, update the client references and so forth.
So if you control both ends of the wire - both the server and the client - and they're both .NET based, you can do the following:
put your service contracts and your data contracts (only the contracts - no implementations!) into a separate assembly
from your service implementation, reference that contracts assembly
copy the contracts assembly to your client, and also reference it in your client project
Now, if you add the service reference, by default, the Add Service Reference function in Visual Studio will reuse existing types in referenced assemblies - so if you have referenced your common "Contracts" assembly, those types (in their full glory, including their namespace) will be reused - no additional copies will be created.
That way, you can create a single, shared contracts assembly used by both the server side code, as well as your client, and you don't have to mess with any duplication of data structures. But again: that only works if you are in control of both ends of the wire, and both are .NET