Ninject.Web.Common No parameterless constructor defined for this object - asp.net-mvc-4

I used Nuget to install the Ninject.Web.Common reference. I use it with ASP.net Web API (APIController) with no problem, but I hit problem when using the ASP.net MVC 4(Controller).
The error:
No parameterless constructor defined for this object.
NinjectWebCommon.cs:
[assembly: WebActivator.PreApplicationStartMethod(typeof(CarsApp.Web.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(CarsApp.Web.App_Start.NinjectWebCommon), "Stop")]
namespace CarsApp.Web.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
//using App.WebUI.Infrastructure;
using App.Domain.Abstract;
using App.Domain.Concreate;
using System.Web.Http;
using App.Web.Infrastructure;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// Install our Ninject-based IDependencyResolver into the Web API config
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IMembershipRepository>().To<EFMembershipRepository>();
kernel.Bind<IBranchRepository>().To<EFBranchRepository>();
}
}
}

I am using MVC5 and using Install-Package Ninject.MVC5 solved my issue.

try with Install-Package Ninject.MVC3

Check if in your Web.config you have two lines for MVC versioning, I had something like:
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="4.0.0.0-4.0.0.1" newVersion="4.0.0.1" />
</dependentAssembly>
If you do then replace them with one line like, to redirect to highest version that you are using in my case it's 4.0.0.1:
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.1" newVersion="4.0.0.1" />
</dependentAssembly>
This did it for me.

Related

Using Microsoft.AspNetCore.Mvc.Versioning, Swashbuckle.AspNetCore, and MaptoApiVersion

I am having trouble with the following use of MapToApiVersion. From http://localhost:5000/swagger, the 1.0 Docs and 2.0 Docs render the swagger ui properly and the corresponding swagger.json file is available. The 3.0 Docs fail to render and the swagger.json file does not appear to be generated.
The actual service is up and running properly for all 3 versions. If I hit it from postman, I get the response I expect.
This is running the 1.1.1-rc1 of Mvc.Versioning and 1.0.0 of Swashbuckle.AspNetCore. Full .csproj below
Full code at https://github.com/senften/AspNetCoreVersionedWebApi/tree/maptoapiversion
I don't see any errors or have any exceptions when I run this in debug in Visual Studio Code. Have I screwed up the startup or declaration or have I simply run into a bug either in the shim or swashbuckle?
using Microsoft.AspNetCore.Mvc;
using VersionedWebApi.Model;
namespace VersionedWebApi.Controllers
{
/// <summary>
/// GoodByeController, just saying Goodbye!
/// </summary>
[ApiVersion("1.0", Deprecated = true)]
[ApiVersion("3.0")]
[Route("api/v{api-version:apiVersion}/[controller]")]
public class GoodByeController : Controller
{
/// <summary>
/// Default Get call returning Goodbye world!
/// </summary>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(GoodByeWorldModel), 200)]
[ProducesResponseType(typeof(void), 404)]
public GoodByeWorldModel Get()
{
return new GoodByeWorldModel();
}
/// <summary>
/// Default Get call returning Goodbye world!
/// </summary>
/// <returns></returns>
[HttpGet, MapToApiVersion("3.0")]
[ProducesResponseType(typeof(VersionedWebApi.Model.v3.GoodByeWorldModel), 200)]
[ProducesResponseType(typeof(void), 404)]
public VersionedWebApi.Model.v3.GoodByeWorldModel GetV3()
{
return new VersionedWebApi.Model.v3.GoodByeWorldModel();
}
}
}
startup.cs
using System.Xml.XPath;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SwashbuckleAspNetVersioningShim;
namespace VersionedWebApi
{
public class Startup
{
public Startup()
{
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
var mvcBuilder = services.AddMvc();
services.AddMvcCore().AddVersionedApiExplorer();
// Adds versioning capabilities, defaulting to version 1.0 calls if available
services.AddApiVersioning(o =>
{
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
});
// Add generated documentation
services.AddSwaggerGen(c =>
{
var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
SwaggerVersioner.ConfigureSwaggerVersions(c, provider);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, ApplicationPartManager partManager, IApiVersionDescriptionProvider provider)
{
// Generate swagger.json
app.UseSwagger();
// Let's enable SwaggerUI
app.UseSwaggerUI(c =>
{
SwaggerVersioner.ConfigureSwaggerVersions(c, provider);
});
app.UseMvc();
// This is new for v1.1 and is a behavioral breaking change from previous (including 1.1-beta)
// See the release notes: https://github.com/Microsoft/aspnet-api-versioning/releases/tag/v1.1-rc1
app.UseApiVersioning();
}
}
}
csproj file
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<Company />
<Description>A .NET Core Web API project demonstrating versioning a Web API and generating interactive documentation with Swagger.</Description>
<RepositoryUrl>https://github.com/senften/AspNetCoreVersionedWebApi</RepositoryUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile></DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.1.0-rc1"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
<PackageReference Include="SwashbuckleAspNetVersioningShim" Version="1.0.0-beta4"/>
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0-msbuild3-final" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.0"/>
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="*"/>
</ItemGroup>
</Project>
The issue you've run in to is a behaviour of the shim library.
When the Swagger document is generated the ApiVersions specified on the controller get applied to all of the methods within it where they're not explicitly set to something else.
In this instance because there is no MapToApiVersion attribute on the Get() method it gets added to both the V1 and V3 Swagger documents because that's what's specified on the controller. This then causes an overload error whilst generating the Swagger doc.
The good news is it's a simple fix, just add [HttpGet, MapToApiVersion("1.0")] to your Get() method so it looks like this
/// <summary>
/// Default Get call returning Goodbye world!
/// </summary>
/// <returns></returns>
[HttpGet, MapToApiVersion("1.0")]
[ProducesResponseType(typeof(GoodByeWorldModel), 200)]
[ProducesResponseType(typeof(void), 404)]
public GoodByeWorldModel Get()
{
return new GoodByeWorldModel();
}
The Get() method will then only get added to the V1 doc and the rest of your code can stay exactly as it is.
There's a bit more detail about this behaviour on the GitHub Readme for the Shim
Need to make couple of changes to your code.
1) Change below code to
[ApiVersion("3.0"), Route("api/v{api-version:apiVersion}/[controller]")]
[Route("api/v{api-version:apiVersion}/[controller]")]
public class HelloController : Controller
{ }
[ApiVersion("3.0"), Route("api/v{api-version:apiVersion}/[controller]")]
public class HelloController : Controller
{ }
2) Add MapToApiVersion 1.0 to avoid conflict
[HttpGet, MapToApiVersion("1.0")]
[ProducesResponseType(typeof(GoodByeWorldModel), 200)]
[ProducesResponseType(typeof(void), 404)]
public GoodByeWorldModel Get()
{
return new GoodByeWorldModel();
}

Error activating IProductRepository, ninject issue learning With Apress Pro ASP.NET MVC 5

Hello I'm Reading the book "Apress Pro ASP.NET MVC 5" I have downloaded chapter 7, that is a running application using ninject.
The first Issue was
1st issue snagit
I read it was an Ninject issue versiĆ³n solved adding this code to the SportsStoreWebUI web.config:
enter code here
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Now when I try to run the App I get this Issue.
Error activating IProductRepository
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Injection of dependency IProductRepository into parameter productRepository of constructor of type ProductController
2) Request for ProductController
Suggestions:
1) Ensure that you have defined a binding for IProductRepository.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
The Content of NinjectWebCommon.cs is
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(SportsStore.WebUI.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(SportsStore.WebUI.App_Start.NinjectWebCommon), "Stop")]
namespace SportsStore.WebUI.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
}
}
}
I have looked for but I don't found the error.
Thank you very much.
You didn't add a RegisterServices in NinjectWebCommon:
System.Web.Mvc.DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
Listing 7-2: Integrating Ninject in the NinjectWebCommon.cs File (pg 166)
private static void RegisterServices(IKernel kernel) {
System.Web.Mvc.DependencyResolver.SetResolver(new SportsStore.WebUI.Infrastructure.NinjectDependencyResolver(kernel));
}

Custom Membership Provider with Windows authetication

I've created my own Membership Provider where I have below method:
public override bool ValidateUser(string username, string password)
{
if (username == "John")
return true;
else
return false;
}
I've also added below lines to web.config file:
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
<membership defaultProvider="MembershipProviter">
<providers>
<clear />
<add name="cls_MembershipProvider" type="App.cls_MembershipProvider"
enablePasswordRetrieval="false"
enablePasswordReset="false"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="5"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10"
applicationName="App"
/>
</providers>
</membership>
As you may notice I am using Windows authentication and I don't have Log In page. By default all users from Active Directory has access to the page. My goal is to check if user exist in my database.
Everywhere I searched, there is Log In page, where ValidateUser is launched. My question is where should I implement ValidateUser method as I don't have Log In page. I just want to have control on each Controler method so I could add [Authorize] so only users from my database can actually access the page.
You can define your own CustomAuthorizeAttribute deriving from AuthorizeAttribute. Override OnAuthorization method to perform validation using details in context. Apply your custom filter on top of each controller or define a BaseController and derive your controllers from BaseController. For example you can define a class like:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class RdbiAuthorizationAttribute : AuthorizeAttribute
{
/// <summary>
/// Verifies that the logged in user is a valid organization user.
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
Guard.ArgumentNotNull(filterContext, "filterContext");
Guard.ArgumentNotNull(filterContext.Controller, "filterContext.Controller");
bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(
typeof(AllowAnonymousAttribute), inherit: true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
typeof(AllowAnonymousAttribute), inherit: true);
if (skipAuthorization)
{
return;
}
if (string.IsNullOrEmpty(filterContext.HttpContext.User.Identity.Name))
throw new AuthenticationException("User must be logged in to access this page.");
var controller = filterContext.Controller as BaseController;
if (controller != null)
{
var user = controller.GetUser();
if (user == null)
{
throw new InvalidOperationException(string.Format("Logged in user {0} is not a valid user", filterContext.HttpContext.User.Identity.Name));
}
}
base.OnAuthorization(filterContext);
}
}
Then you can define controller like:
[RdbiAuthorization]
public class BaseController : Controller
{
}
public class MyTestController : BaseController
{
}

Ninject Topshelf Microsoft.Owin.Hosting

How can I utilize Topshelf.Ninject and incorporate OwinNinjectDependencyResolver (from Ninject.Web.WebApi.OwinHost)?
I can get it to work but I need to instantiate the Ninject kernel twice (once for Topshelf and once for my HttpConfiguration.DependencyResolver. This does not seem to be the right way to use Ninject.
Any help or example code related to this specific design would be very helpful.
So I had this same problem and I managed to solve it.
The key to this is to:
Not use the [assembly: OwinStartup(...)] attribute to bootstrap OWIN.
Inject an IKernel using Ninject into the service class being used to configure Topshelf.
Use the Start(StartOptions options, Action<Owin.IAppBuilder> startup) method of the Microsoft.Owin.Hosting.WebApp class to start the self-hosted web-app.
Pass in the injected kernel to the OWIN bootstrap routine via the startup action.
Use the passed-in reference to the kernel in the call to appBuilder.UseNinjectMiddleware(() => kernel); in the OWIN startup routine, rather than appBuilder.UseNinjectMiddleware(CreateKernel);.
Profit.
The complete solution is as follows:
Create a console application solution called BackgroundProcessor.
Add packages listed in the Packages.config listing below to the solution using NuGet.
Add an App.config to the solution using the listing below.
Add each of the code files provided below to the solution; I have provided the full listings because of the lines of code are quite sensitive to the using statements because of heavy use of extension methods by the libraries utilized by the solution.
Compile and run the project.
Test the solution by hitting the URL http://localhost:9000/test on your local machine.
Packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.WebApi" version="5.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.0.0" targetFramework="net45" />
<package id="Microsoft.Owin" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Host.HttpListener" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Hosting" version="2.1.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
<package id="Ninject" version="3.2.2.0" targetFramework="net45" />
<package id="Ninject.Extensions.ContextPreservation" version="3.2.0.0" targetFramework="net45" />
<package id="Ninject.Extensions.NamedScope" version="3.2.0.0" targetFramework="net45" />
<package id="Ninject.Web.Common" version="3.2.2.0" targetFramework="net45" />
<package id="Ninject.Web.Common.OwinHost" version="3.2.2.0" targetFramework="net45" />
<package id="Ninject.Web.WebApi" version="3.2.0.0" targetFramework="net45" />
<package id="Ninject.Web.WebApi.OwinHost" version="3.2.1.0" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="Topshelf" version="3.1.3" targetFramework="net45" />
<package id="Topshelf.Ninject" version="0.3.0.0" targetFramework="net45" />
</packages>
App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Topshelf" publicKeyToken="b800c4cfcdeea87b" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.122.0" newVersion="3.1.122.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Ninject" publicKeyToken="c7192dc5380945e7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.2.0.0" newVersion="3.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject;
using Topshelf;
using Topshelf.Ninject;
namespace BackgroundProcessor
{
using Modules;
using Services;
public class Program
{
public static int Main(string[] args)
{
var exitCode = HostFactory.Run
(
c =>
{
c.UseNinject(new Module());
c.Service<Service>
(
sc =>
{
sc.ConstructUsingNinject();
sc.WhenStarted((service, hostControl) => service.Start(hostControl));
sc.WhenStopped((service, hostControl) => service.Stop(hostControl));
}
);
c.SetServiceName("BackgroundProcessorSvc");
c.SetDisplayName("Background Processor");
c.SetDescription("Processes things in the background");
c.EnablePauseAndContinue();
c.EnableShutdown();
c.StartAutomaticallyDelayed();
c.RunAsLocalSystem();
}
);
return (int)exitCode;
}
}
}
Modules\Module.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Modules;
namespace BackgroundProcessor
{
using Contracts;
using Services;
namespace Modules
{
public class Module : NinjectModule
{
public override void Load()
{
Bind<IService>().To<Service>();
}
}
}
}
Contracts\IService.cs
using System;
namespace BackgroundProcessor
{
namespace Contracts
{
public interface IService
{
bool Start(Topshelf.HostControl hostControl);
bool Stop(Topshelf.HostControl hostControl);
}
}
}
Services\Service.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Owin.Hosting;
using Ninject;
using Topshelf;
namespace BackgroundProcessor
{
using Configs;
using Contracts;
namespace Services
{
public class Service : IService
{
private readonly IKernel kernel;
public Service(IKernel kernel)
: base()
{
this.kernel = kernel;
}
protected IKernel Kernel
{
get
{
return this.kernel;
}
}
protected IDisposable WebAppHolder
{
get;
set;
}
protected int Port
{
get
{
return 9000;
}
}
public bool Start(HostControl hostControl)
{
if (WebAppHolder == null)
{
WebAppHolder = WebApp.Start(new StartOptions { Port = Port }, appBuilder =>
{
new StartupConfig().Configure(appBuilder, Kernel);
});
}
return true;
}
public bool Stop(HostControl hostControl)
{
if (WebAppHolder != null)
{
WebAppHolder.Dispose();
WebAppHolder = null;
}
return true;
}
}
}
}
Configs\StartupConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Http;
using Ninject;
using Ninject.Web.Common.OwinHost;
using Ninject.Web.WebApi.OwinHost;
using Owin;
namespace BackgroundProcessor
{
namespace Configs
{
public class StartupConfig
{
public void Configure(IAppBuilder appBuilder, IKernel kernel)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.MapDefinedRoutes();
appBuilder.UseNinjectMiddleware(() => kernel);
appBuilder.UseNinjectWebApi(config);
}
}
}
}
Configs\RoutesConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace BackgroundProcessor
{
namespace Configs
{
public static class RoutesConfig
{
public static void MapDefinedRoutes(this HttpConfiguration config)
{
config.Routes.MapHttpRoute
(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);
}
}
}
}
Controllers\TestController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace BackgroundProcessor
{
namespace Controllers
{
[RoutePrefix("test")]
public class TestController : ApiController
{
[HttpGet]
[Route("")]
public HttpResponseMessage Index()
{
return Request.CreateResponse<string>(HttpStatusCode.OK, "Hello world!");
}
}
}
}

Protobuf net not serializing when using WCF

I am trying to use protobuf to serialize my WCF calls, but it seems the object is not getting serialized by the client. Some things to note:
I am using a shared DTO library.
I am using a ChannelFactory to
invoke the service (so the types are not losing their datamember
attributes).
I can serialize and deserialize the objects just using
normal protobuf.net code, so the types themselves seem to be ok
I am using version 2.0.0.480 of protobuf.net
I haven't posted the service code as the problem is with the outgoing message (message log posted below)
The client and service work fine if I don't use the protobuf endpoint behaviour.
My main method looks as follows
static void Main(string[] args)
{
Base.PrepareMetaDataForSerialization();
FactoryHelper.InitialiseFactoryHelper(new ServiceModule());
Member m = new Member();
m.FirstName = "Mike";
m.LastName = "Hanrahan";
m.UserId = Guid.NewGuid();
m.AccountStatus = MemberAccountStatus.Blocked;
m.EnteredBy = "qwertt";
ChannelFactory<IMembershipService> factory = new ChannelFactory<IMembershipService>("NetTcpBinding_MembershipService");
var client = factory.CreateChannel();
using (var ms = new MemoryStream())
{
Serializer.Serialize<Member>(ms, m);
Console.WriteLine(ms.Length.ToString());
ms.Position = 0;
var member2 = Serializer.Deserialize<Member>(ms);
Console.WriteLine(member2.EnteredBy);
Console.WriteLine(member2.FirstName);
}
var result = client.IsMemberAllowedToPurchase(m);
System.Console.Write(result.IsValid.ToString());
factory.Close();
var input = Console.ReadLine();
}
My client configuration looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
</configSections>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_Common" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288"
maxBufferSize="1000065536" maxConnections="10" maxReceivedMessageSize="1000000">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="Proto.Common.EndpointBehavior">
<protobuf />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=2.0.0.480, Culture=neutral, PublicKeyToken=257b51d87d2e4d67" />
</behaviorExtensions>
</extensions>
<client>
<endpoint address="net.tcp://mikes-pc:12002/MembershipService.svc"
behaviorConfiguration="Proto.Common.EndpointBehavior" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_Common" contract="PricesForMe.Core.Entities.ServiceInterfaces.IMembershipService"
name="NetTcpBinding_MembershipService">
<identity>
<userPrincipalName value="Mikes-PC\Mike" />
</identity>
</endpoint>
</client>
<diagnostics>
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
maxSizeOfMessageToLog="2000"/>
</diagnostics>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="traceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="E:\Work\Logs\IMembershipServiceWcfTrace_Client.svclog" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="E:\Work\Logs\IMembershipServiceWcfTrace_Client_messages.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
After logging the client message, I get the following entry in the log
<MessageLogTraceRecord>
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://www.pricesforme.com/services/MembershipService/IsMemberAllowedToPurchase</a:Action>
<a:MessageID>urn:uuid:8b545576-c453-4be6-8d5c-9913e2cca4bf</a:MessageID>
<ActivityId CorrelationId="b4e9361f-1fbc-4b2d-b7ee-fb493847998a" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">6d712899-62fd-4547-9517-e9de452305c6</ActivityId>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink"></VsDebuggerCausalityData>
</s:Header>
<s:Body>
<IsMemberAllowedToPurchase xmlns="http://www.pricesforme.com/services/">
<proto></proto>
</IsMemberAllowedToPurchase>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
So as can been seen in the above log message the proto entry has no data in it. My member class looks as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using PricesForMe.Core.Entities.Common;
using PricesForMe.Core.Entities.Ordering;
namespace PricesForMe.Core.Entities.Members
{
/// <summary>
/// This entity represents a member or user of the site.
/// </summary>
[DataContract]
[Serializable]
public class Member: User
{
public Member()
:base()
{
EntityType = Entities.EntityType.Member;
}
[DataMember(Order = 20)]
public int Id { get; set; }
[DataMember(Order = 21)]
public string MemberName { get; set; }
[DataMember(Order = 22)]
public PaymentInfo DefaultPaymentMethod { get; set; }
[DataMember(Order = 23)]
public MemberAccountStatus AccountStatus { get; set; }
#region static
public static readonly string CacheCollectionKey = "MemberCollection";
private static readonly string CacheItemKeyPrefix = "Member:";
public static string GetCacheItemKey(int id)
{
return CacheItemKeyPrefix + id.ToString();
}
#endregion
}
}
The parent user class looks as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics.Contracts;
namespace PricesForMe.Core.Entities.Common
{
/// <summary>
/// This class represents a user in the system. For example, a user could be a member or a merchant user.
/// </summary>
[DataContract]
[Serializable]
public class User: Base
{
public User()
:base()
{
EntityType = Entities.EntityType.User;
}
[DataMember(Order = 10)]
public Guid UserId { get; set; }
[DataMember(Order = 11, Name = "First Name")]
public string FirstName { get; set; }
[DataMember(Order = 12, Name = "Last Name")]
public string LastName { get; set; }
}
}
}
And the base class looks as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics.Contracts;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using ProtoBuf.Meta;
namespace PricesForMe.Core.Entities
{
/// <summary>
/// This is the base class for all entities involved in the request/response pattern of our services
/// </summary>
/// <remarks>
/// The objects derived from this class are used to transfer data from our service classes to our UIs and back again and they should
/// not contain any logic.
/// </remarks>
[DataContract]
[Serializable]
public abstract class Base
{
public Base()
{
//Set some defaults for this
EnteredBy = System.Environment.UserName;
EnteredSource = System.Environment.MachineName;
}
/// <summary>
/// This is the record timestamp
/// </summary>
[DataMember(Order = 2)]
public DateTime RecordTimeStamp { get; set; }
/// <summary>
/// This is the name of the user who last edited the entity
/// </summary>
[DataMember(Order = 3)]
public string EnteredBy { get; set; }
/// <summary>
/// This is the source of the last edited entity
/// </summary>
[DataMember(Order = 4)]
public string EnteredSource { get; set; }
[DataMember(Order = 5)]
private PricesForMe.Core.Entities.Common.ValidationResult _validationResult = null;
/// <summary>
/// Data on the validity of the entity.
/// </summary>
public PricesForMe.Core.Entities.Common.ValidationResult ValidationData
{
get
{
_validationResult = Validate();
return _validationResult;
}
set
{
_validationResult = value;
}
}
/// <summary>
/// Flag denoting if the record is a new record or not.
/// </summary>
/// <remarks>
/// To flag an entity as an existing record call the "FlagAsExistingReport()" method.
/// </remarks>
public bool IsNewRecord
{
get
{
return _isNewRecord;
}
}
[DataMember(Order = 6)]
protected bool _isNewRecord = true;
/// <summary>
/// Flags the entity as a record that already exists in the database
/// </summary>
/// <remarks>
/// This is a method rather than a field to demonstrait that this should be called with caution (as opposed to inadvertantly setting a flag!)
/// <para>
/// Note that this method should only need to be called on object creation if the entity has a composite key. Otherwise the flag is
/// set when the id is being set. It should always be called on saving an entity.
/// </para>
/// </remarks>
public void FlagAsExistingRecord()
{
_isNewRecord = false;
}
public virtual PricesForMe.Core.Entities.Common.ValidationResult Validate()
{
if (_validationResult == null)
{
_validationResult = new PricesForMe.Core.Entities.Common.ValidationResult();
_validationResult.MemberValidations = new List<Common.ValidationResult>();
_validationResult.RulesViolated = new List<Common.ValidationRule>();
}
return _validationResult;
}
/// <summary>
/// This is the type of entity we are working with
/// </summary>
[DataMember(Order = 7)]
private EntityType _entityType = EntityType.Unknown;
public EntityType EntityType
{
get
{
return _entityType;
}
protected set
{
_entityType = value;
}
}
/// <summary>
/// Flag to say if the id generated for this class need to be int64 in size.
/// </summary>
[DataMember(Order = 9)]
public bool IdRequiresInt64 { get; protected set; }
/// <summary>
/// This method tells us if the database id has been assigned. Note that this does
/// not mean the entity has been saved, only if the id has been assigned (so the id could be greater than 0, but the
/// entity could still be a NewRecord
/// </summary>
/// <returns></returns>
[DataMember(Order = 8)]
public bool HasDbIdBeenAssigned { get; protected set; }
private Guid _validationId = Guid.NewGuid();
public Guid EntityValidationId
{
get
{
return _validationId;
}
}
/// <summary>
/// Converts an object into another type of object based on the mapper class provided.
/// </summary>
/// <remarks>
/// This method allows us to easily convert between objects without concerning ourselves with the mapping implementation. This
/// allows us to use various mapping frameworks (e.g. Automapper, ValueInjector) or create our own custom mapping.
/// </remarks>
/// <typeparam name="TDestination">The type we want to convert to</typeparam>
/// <typeparam name="KMapper">The mapping type</typeparam>
/// <returns>The new type</returns>
public TDestination ConvertTo<TDestination, TSource, KMapper>()
where KMapper : IEntityMapper<TDestination, TSource>
where TSource : class
{
return Base.ConvertToItem<TDestination, TSource, KMapper>(this as TSource);
}
/// <summary>
/// Returns all known child types
/// </summary>
public IEnumerable<Type> GetAllTypes()
{
Assembly current = Assembly.GetCallingAssembly();
List<Type> derivedTypes = new List<Type>();
var allTypes = current.GetTypes();
foreach (var t in allTypes)
{
if (t.IsAssignableFrom(typeof(Base)))
{
derivedTypes.Add(t);
}
}
return derivedTypes;
}
#region Static Methods
/// <summary>
/// Converts a list of one type to a list of another type
/// </summary>
/// <typeparam name="TDestination">The type we want to convert to</typeparam>
/// <typeparam name="TSource">The source type</typeparam>
/// <typeparam name="KMapper">The mapper class</typeparam>
/// <param name="source">The source list of items.</param>
/// <returns></returns>
public static List<TDestination> ConvertToList<TDestination, TSource, KMapper>(IEnumerable<TSource> source)
where KMapper : IEntityMapper<TDestination, TSource>
where TSource : class
{
List<TDestination> result = new List<TDestination>();
KMapper mapper = Activator.CreateInstance<KMapper>();
foreach (var item in source)
{
result.Add(mapper.Convert(item));
}
return result;
}
public static TDestination ConvertToItem<TDestination, TSource, KMapper>(TSource source)
where KMapper : IEntityMapper<TDestination, TSource>
where TSource : class
{
//Return default (i.e. null for ref objects) if the source is null.
if (source == null) { return default(TDestination); }
KMapper mapper = Activator.CreateInstance<KMapper>();
return mapper.Convert(source);
}
private static object _metaLock = new object();
private static bool _metaDataPrepared = false;
/// <summary>
/// Creates protobuf type models from the entities in this assembly
/// </summary>
public static void PrepareMetaDataForSerialization()
{
lock (_metaLock)
{
if (_metaDataPrepared) { return; }
Assembly current = Assembly.GetExecutingAssembly();
var allTypes = current.GetTypes();
foreach (var t in allTypes)
{
checkType(t);
}
}
}
private static void checkType(Type type)
{
Assembly current = Assembly.GetExecutingAssembly();
var allTypes = current.GetTypes();
int key = 1000;
foreach (var t in allTypes)
{
if (t.IsSubclassOf(type) && t.BaseType == type)
{
RuntimeTypeModel.Default[type].AddSubType(key, t);
key++;
}
}
}
#endregion
}
}
The PrepareMetaDataForSerialization method on base configures the RuntimeModel for protobuf.net, but I mentioned earlier, the serialization and deserialization works fine outside of the WCF, so I think the DTOs are ok. Any ideas around what could be causing the problem are greatly appreciated.
k; the element name looks correct (proto, matching XmlProtoSerializer.PROTO_ELEMENT), so protobuf-net definitely tried to do something. It also doesn't include #nil to represent null, so it knows there was data. Beyond that, it serializes the object to a MemoryStream and writes it as base-64 (identical to how byte[] etc is represented, which allows WCF to hoist the data silently and automatically if things like MTOM are enabled). So the question becomes "why would my type serialize to nothing?"
The use of DataContract/DataMember is fine, and matches my existing WCF integration tests.
I wonder how much of this is due to the inheritance (only one of the members shown relates to the concrete type, and I would hazard a guess that Blocked is 0, which has particular handling).
I cannot, however, emphasise enough how unsafe your current inheritance handling is; the numbers matter, and reflection makes no guarantees re order. I would strongly suggest you revisit this and make inheritance numbering more predicatable.
Very minor observation, but there is no need to store EntityType - it is entirey redundant and could be handled via polymorphism with no need for storage.
Also, there's an important bug that _metaDataPrepared is never set to true.
However! Ultimately I cannot reproduce this; I have used your code (or most of it) to generate an integration test, and - it passes; meaning: using WCF, NetTcpBinding, your classes (including your inheritance fixup code), and the protobuf packing, it just works. The data that goes over the wire is the data we expect.
Happy to try to help further, but I need to be able to repro it... and right now, I can't.
The first thing I'd do is add the missing _metaDataPrepared = true; to see if that helps.
I thought you need to use ProtoContract instead of DataContract per this SO? Also, make sure you set "Reuse types in referenced assemblies" when configuring service reference. And according to this SO they support data contracts but you have to have the order set [DataMember(Order = 0)] (at least that's what worked for me).