integrating unity.wcf with asp.net routes - c#

I am trying to use unity.wcf inside my wcf web services without an svc file.
My code:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
//[BasicAuthentication] TODO: this need to be set up
public class SsoAuthenticationService : ISsoAuthenticationService
{
private readonly ICustomerManager _manager;
internal const string ServiceUri = "SsoAuthenticationService";
public SsoAuthenticationService(ICustomerManager manager)
{
_manager = manager;
}
[WebGet(UriTemplate = "CreateSsoLogin/{request}")]
public ServiceResponse<bool> CreateSsoLogin(string request)
{
// TODO: inputs and outputs are wrong
_manager.DoWork();
return new ServiceResponse<bool>();
}
}
public class ServiceFactory : UnityServiceHostFactory
{
protected override void ConfigureContainer(Microsoft.Practices.Unity.IUnityContainer container)
{
container.RegisterType<ISsoAuthenticationService, SsoAuthenticationService>("authentication")
.RegisterType<ICustomerManager, CustomerManager>();
}
}
Now the problem comes when I try and connect the ServiceFactory class - in all the code examples I have seen they do this via an svc file, but I don't have one in my application, as it is using ASP.NET routing.
So my Global.asax looks like:
private static void RegisterRoutes(RouteCollection routes)
{
//Integrate WCF services with the ASP.NET routing feature
routes.Add(new ServiceRoute(SsoAuthenticationService.ServiceUri, new ServiceFactory(), typeof(SsoAuthenticationService)));
}
When I call my web service method, I am not hitting the web method code (it does call ServiceFactory.ConfigureContainer though). If I weren't using Unity, the Global.asax would look like:
private static void RegisterRoutes(RouteCollection routes)
{
//Integrate WCF services with the ASP.NET routing feature
routes.Add(new ServiceRoute(SsoAuthenticationService.ServiceUri, new WebServiceHostFactory(), typeof(SsoAuthenticationService)));
}
And if I change it to this, then the web method is hit, but of course it complains about the constructor.
What extra configuration do I need to make the ServiceFactory class behave like WebServiceHostFactory?
Or would a better option be to not use Unity.Wcf, and try and implement with plain Unity?

OK, I have managed to find the answer to this using the following link http://www.agile-code.com/blog/rest-web-services-with-unity-wcf/
I had to create a new pair of classes to inherit from WebServiceHostFactory (abstract) and WebServiceHost as described in the link.
Once these were in place, and my ServiceFactory class inherited from the new class, everything started working.

Related

Unit testing APIGatewayProxyFunction API Controller methods (C# inheritance problem maybe?)

I have an ASP.NET Core web API application that's set up as an AWS Serverless lambda function with API Gateway.
I'm working with an instance of APIGatewayProxyFunction and I'm trying to unit test some of my controller behavior by injecting NSubstitute versions of my database repo.
Now, of course I can instantiate my controller directly and inject the mocked dependencies, but using the auto-gen'd LambdaEntryPoint class and taking advantage of the host builder logic lets me get all the MVC routing goodies and I can test actual HTTP method matching and route matching.
My problem is, builder.UseStartup<Startup>() uses my real DI service registration code. I was hoping I could override this behavior and register a mocked instance of my database repo class.
There doesn't seem to be a way to get at the ServiceCollection in the unit test once the object is constructed, so I thought I'd just sub-class the LambdaEntryPoint and override the Init() function to supply a subclass of Startup MockStartup instead which registers mocked instances.
The problem it seems is that Init() actually gets called during the constructor chain, so I can't really feed mocked instances into my subclass to be used during the Init() override.
Super pared down example:
var myClass = new ChildClass(7);
public class BaseClass
{
public BaseClass()
{
Console.WriteLine("Base class constructing");
Init();
}
public virtual void Init()
{
Console.WriteLine("Base class init");
}
}
public class ChildClass : BaseClass
{
private readonly int? _mockedService;
public ChildClass(int mockedService)
{
_mockedService = mockedService;
Console.WriteLine("Child class constructed");
}
public override void Init()
{
Console.WriteLine($"Child class init with mocked service {_mockedService}");
}
}
This, of course does not work, because _mockedService is still null when we get to executing the overridden Init() function.
So, I'm looking for guidance on how I can write a unit test which can submit actual JSON posts to prove MVC routes and HTTP methods for my application while still providing a mocked instance of my database interface?
I'm open to all options, but if possible, I'd like to do this without spinning up a full http webservice and actually submitting http requests, but if that's the only option, guidance on the best way to do that with substitutes would be appreciated as well.
Thanks.
I was able to solve this using a static Dictionary and GetHashCode() to uniquely identify each LambdaEntryPoint object created by different tests.
public class TestEntryPoint : LambdaEntryPoint
{
private static readonly Dictionary<int, IDbRepository> Repos = new();
public IDbRepository Repository => Repos[GetHashCode()];
protected override void Init(IWebHostBuilder builder)
{
builder.UseStartup(context =>
{
var repo = Substitute.For<IDbRepository>();
Repos[GetHashCode()] = repo;
var startup = new MockStartup(context.Configuration, repo);
return startup;
});
}
}
public class MockStartup : Startup
{
private readonly IDbRepository _repository;
public MockStartup(IConfiguration configuration, IDbRepository repository) : base(configuration)
{
_repository = repository;
}
public override void RegisterServices(IServiceCollection services)
{
services
.AddTransient<IServiceConfiguration, LambdaServiceConfiguration>()
.AddTransient(_ => _repository);
}
}
This allows my tests to do:
var lambdaFunction = new TestEntryPoint();
lambdaFunction.Repository.Whatever(...).Returns(...);
using lambdaFunction.Repository as my mock

Difficulty constructing a controller with injected dependencies in Unity ASP.NET Web API

I know there are similar questions for this out there, but I've spent hours trying to follow them to no avail so I would really appreciate some help.
I'm working on a simple ASP.NET MVC project, and I'm trying to inject dependencies into a Web API Controller with Unity. The basic structure is:
EmailController <- NotificationEngine <- EmailAccessor
EmailAccessor's constructor needs to have injected values from Web.config AppSettings. EmailController and NotificationEngine only take a single object in their constructors. Here's an abridged version of the code:
EmailAccessor
public class EmailAccessor : IEmailAccessor
{
private string senderEmail;
private string senderUsername;
private string senderPassword;
private int port;
public EmailAccessor(string senderEmail, string senderUsername, string senderPassword, int port)
{
this.senderEmail = senderEmail;
this.senderUsername = senderUsername;
this.senderPassword = senderPassword;
this.port = port;
}
//...
}
NotificationEngine
public class NotificationEngine : INotificationEngine
{
private IEmailAccessor emailAccessor;
public NotificationEngine(IEmailAccessor emailAccessor)
{
this.emailAccessor = emailAccessor;
}
//...
}
EmailController
[RoutePrefix("api/email")]
public class EmailController : ApiController
{
private INotificationEngine notificationEngine;
public EmailController(INotificationEngine notificationEngine)
{
this.notificationEngine = notificationEngine;
}
[HttpPost]
[Route("send")]
public void Post([FromBody] EmailNotification email)
{
//...
}
//...
}
UnityConfig
Finally, here's the class where I register my types
public static class UnityConfig
{
public static void RegisterTypes(IUnityContainer container)
{
container.LoadConfiguration();
//...
container.RegisterType<IEmailAccessor, EmailAccessor>(new InjectionConstructor(
ConfigurationManager.AppSettings["SenderEmail"],
ConfigurationManager.AppSettings["SenderUsername"],
ConfigurationManager.AppSettings["SenderPassword"],
int.Parse(ConfigurationManager.AppSettings["Port"])));
container.RegisterType<INotificationEngine, NotificationEngine>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
}
The settings are all pulled from <appSettings> in Web.config.
When I try and POST to localhost:63855/api/email/send, I get the following response:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:63855/api/email/send'.",
"MessageDetail": "No type was found that matches the controller named 'email'."
}
I'm working on a simple ASP.NET MVC project, and I'm trying to inject dependencies into a Web API Controller
It is not a "simple ASP.NET MVC project". It is a hybrid project with both ASP.NET MVC and Web API in it, which are 2 separate frameworks. Each of these frameworks has its own types and its own configuration.
You have configured DI with ASP.NET MVC, but not with Web API. To use both frameworks with DI, you must set the Dependency Resolver for both frameworks in your project.
// MVC's DependencyResolver
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
// Web API's DependencyResolver
GlobalConfiguration.Configuration.DependencyResolver
= new UnityDependencyResolver(container);
This assumes that the UnityDependencyResolver you are using implements System.Web.Http.Dependencies.IDependencyResolver. If not, there are instructions on how to build one at Dependency Resolution with the Unity Container.
Also, be sure you have enabled attribute routing, or none of your Web API attribute routes will be picked up by the framework. This method must be called at application startup.
GlobalConfiguration.Configure(config =>
{
// ** Enable Attribute Routing for Web API **
config.MapHttpAttributeRoutes();
// For the config you are showing, you don't necessarily need this
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
});
If that doesn't solve it, there is another similar question you should check out the answers for.

Using custom route in WCF service hosted in an ASP.NET application

I have this simple service in my ASP.NET application:
// IBilling.cs
namespace WebApplication3.V1 {
[ServiceContract]
public interface IBilling {
[OperationContract]
string Get();
}
}
// Billing.svc
namespace WebApplication3.V1 {
public class Billing : IBilling {
public string Get() { return "Amiry"; }
}
}
It's accessible (by default) through /V1/Billing.svc route. However, I changed the route by adding this simple line to my route-table:
RouteTable.Routes.Add(
new ServiceRoute("api/ws/v1/billing",
new WebServiceHostFactory(),
typeof(Billing)));
Now, I can execute the Get() method and get result:
// it works:
http://localhost:7475/api/ws/v1/billing/get
BUT, the discovery address is not accessible:
http://localhost:7475/api/ws/v1/billing
// returns "Endpoint not found."
I'm pretty new to WCF services; so I have no idea where the problem could be and googling didn't help. Can you guide me to achieve the route I mentioned above please? Thanks in advance.

Pass ServiceBase class instance to ApiController hosted on it

I'm developing a Windows Service with a RESTFul web service hosted on it. I'm going to communicate with the windows service throught the windows service.
This is my project structure:
These are my classes implementation.
namespace WindowsService_HostAPI
{
public partial class SelfHostService : ServiceBase
{
private int _value;
private HttpSelfHostServer _server;
private static readonly ILog _log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public int Value
{
get { return _value; }
set { _value = value; }
}
public SelfHostService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute(
name: "API",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
_server = new HttpSelfHostServer(config);
_server.OpenAsync().Wait();
}
protected override void OnStop()
{
if (_server != null)
_server.CloseAsync().Wait();
}
}
}
And ValuesController:
namespace WindowsService_HostAPI.Controllers
{
public class ValuesController : ApiController
{
[HttpGet]
[Route("api/Value/")]
public HttpResponseMessage GetValue()
{
HttpResponseMessage response = null;
return response;
}
[HttpPost]
[Route("api/Value/{value}")]
public HttpResponseMessage SetValue(int value)
{
HttpResponseMessage response = null;
return response;
}
}
}
This is only an example, but I need to communicate the ApiController with the Windows Service class.
I have to modify SelfHostService.Value property when someone do a Post (method SetValue) setting value passed. How can I do that?
I need to create a class instance and keep it alive until Windows
Service stops
The right way to do what you want with Web API is to use IDependencyResolver. Here's the sample (it uses Unity, but you can use any container).
The main concept is that you build up a contract for dependency, e.g.:
public interface IValueProvider
{
public int Value { get; set; }
}
then implement this contract somewhere (you can even implement it in SelfHostService, but, actually, you shouldn't), configure DI-container to use your implementation, and use it from controller:
public class ValuesController : ApiController
{
private readonly IValueProvider _valueProvider;
public ValuesController(IValueProvider valueProvider)
{
_valueProvider = valueProvider;
}
// the rest of code here
}
Note, that usually DI-containers allow to build parent/child hierarchy.
To reflect this, Web API DI approach uses scopes for this (see IDependencyResolver.BeginScope method).
There are global scope and child scopes. The Web API host creates global scope when it starts. This scope lives until host listens for requests. Host creates child scopes, when request is received, and host needs to create a controller to respond.
DI containers differ a little, when manage lifetime of objects, that were created using container. But the common is to place dependencies to the scope, where they needed. So, if you want to build some "global" dependency, then you need to place it into global scope.

Dispose WCF service used by authorize attribute

I have a custom authorize attribute like this :
public class AuthorizeApplicationAttribute : AuthorizeAttribute {
private MainServiceSoapClient client;
public AuthorizeApplicationAttribute() {
this.client = new MainServiceSoapClient();
}
public AuthorizeApplicationAttribute(MainServiceSoapClient client) {
this.client = client;
}
protected override bool IsAuthorized(HttpActionContext actionContext) {
if (!base.IsAuthorized(actionContext)) return false;
var currentUser = System.Web.Security.Membership.GetUser();
if (currentUser == null) return false;
var userId = (int)currentUser.ProviderUserKey;
return client.HasPermission(userId);
}
}
which I register in WebApiConfig as :
public class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Web API configuration and services
config.Filters.Add(new AuthorizeApplicationAttribute());
// more configuration ...
}
}
but AuthorizeAttribute doesn't have a dispose, I can call the constructor with the soap client instance, but then I'm not sure where to dispose it.
FYI: I'm not using a IoC container. Trying to learn how to make it manually.
This example uses a SoapClient but I have the same question about a DbContext while I can live without disposing DbContext and don't think not disposing a WCF Client will be that good.
Use a using statement inside your IsAuthorized method to initialize and Dispose when needed instead of initializing in constructor.
If you ever need dependency injection then you can use Property Injection.
This question has two answers with Ninject and StructureMap How to use dependency injection with an attribute?
Solution to this question might give you exactly what you want:
What is the best workaround for the WCF client `using` block issue?

Categories