FakeitEasy return null object - visual-studio-2010

I made a repository class to access my DB and then I made a unit test using the FakeItEasy library. Using a real repository I got the expected result, while using the fake repository returns null.
[TestClass]
public class clientRepositoryTest
{
[TestMethod]
public void GetclientById()
{
var fakeRepository = A.Fake<IclientRepository>();
const int expectedclient = 1;
IclientRepository realRepository = new clientRepository();
var realResult = realRepository.GetclientById(expectedclient); // returns expected object
try
{
A.CallTo(() => fakeRepository.GetclientById(expectedclient)).MustHaveHappened();
var fakeResult = fakeRepository.GetSupplierById(expectedSupplier); // returns null
A.CallTo(() => fakeRepository.GetSupplierById(expectedSupplier).IdSupplier).Equals(expectedSupplier);
}
catch (Exception ex)
{
//The current proxy generator can not intercept the specified method for the following reason:
// - Non virtual methods can not be intercepted.
}
}

Before calling any actual function call you need to make sure to call all the internal fake call like below
A.CallTo(() => fakeRepository.GetclientById(expectedclient)).WithAnyArguments().Returns(Fakeobject/harcoded object);
Then go for the unit test call
var fakeResult = fakeRepository.GetSupplierById(expectedSupplier);
after that go for MustHaveHappened/ MustNotHaveHappened/Equals
A.CallTo(() => fakeRepository.GetclientById(expectedclient)).MustHaveHappened();
A.CallTo(() => fakeRepository.GetSupplierById(expectedSupplier).IdSupplier).Equals(expectedSupplier);
The Implementation should be like this
[TestClass]
public class clientRepositoryTest
{
[TestMethod]
public void GetclientById()
{
var fakeRepository = A.Fake<IclientRepository>();
const int expectedclient = 1;
IclientRepository realRepository = new clientRepository();
var realResult = realRepository.GetclientById(expectedclient); // returns expected object
try
{
A.CallTo(() => fakeRepository.GetclientById(expectedclient)).WithAnyArguments().Returns(Fakeobject/harcoded object);
var fakeResult = fakeRepository.GetSupplierById(expectedSupplier); // returns null
A.CallTo(() => fakeRepository.GetclientById(expectedclient)).MustHaveHappened();
A.CallTo(() => fakeRepository.GetSupplierById(expectedSupplier).IdSupplier).Equals(expectedSupplier);
}
catch (Exception ex)
{
//The current proxy generator can not intercept the specified method for the following reason:
// - Non virtual methods can not be intercepted.
}
}

Related

Mocking a class with internal SDK calls using NSubstitute

First time trying to use NSubstitute.
I have the following method in my Web API.
For those who don't know Couchbase, lets say that a collection/bucket is like a DB table and a key is like a DB row.
Couchbase_internal.Collection_GET returns Task<ICouchbaseCollection>
I would like to write 2 unit tests.
One that tests the returned class when the key exist and one when it doesn't (couchbaseServiceResultClass).
I don't really understand where is the part where I control whether or not the key exist in the mocked data.
public class CouchbaseAPI : ControllerBase, ICouchbaseAPI
{
// GET /document_GET?bucketName=<bucketName>&key=<key>
[HttpGet]
[Consumes("application/x-www-form-urlencoded")]
[Produces(MediaTypeNames.Application.Json)]
public async Task<couchbaseServiceResultClass> document_GET([FromQuery, BindRequired] string bucketName, [FromQuery, BindRequired] string key)
{
var collection = await Couchbase_internal.Collection_GET(bucketName);
if (collection != null)
{
IGetResult result;
try
{
// get document
result = await collection.GetAsync(key);
}
catch (CouchbaseException ex)
{
return new ErrorHandling().handleCouchbaseException(ex);
}
couchbaseServiceResultClass decryptResult = new();
try
{
// decrypt document
decryptResult = Encryption.decryptContent(result);
}
catch (Exception ex)
{
return new ErrorHandling().handleException(ex, null);
}
// remove document if decryption failed
if (!decryptResult.DecryptSuccess)
{
try
{
await collection.RemoveAsync(key);
}
catch (CouchbaseException ex)
{
return new ErrorHandling().handleCouchbaseException(ex);
}
}
decryptResult.Message = "key retrieved successfully";
// return result
return decryptResult;
}
else
{
return new ErrorHandling().handleError("Collection / bucket was not found.");
}
}
This is what I have so far for the first test:
public class CouchbaseAPITests
{
private readonly CouchbaseAPI.Controllers.ICouchbaseAPI myClass = Substitute.For<CouchbaseAPI.Controllers.ICouchbaseAPI>();
[Fact]
public async Task document_GET_aKeyIsRetrievedSuccessfully()
{
// Arrange
string bucketName = "myBucket";
string keyName = "myKey";
couchbaseServiceResultClass resultClass = new();
resultClass.Success = true;
resultClass.Message = "key retrieved successfully";
myClass.document_GET(bucketName, keyName).Returns(resultClass);
// Act
var document = await myClass.document_GET(bucketName, keyName);
// Assert
Assert.True(document.Success);
Assert.Equal("key retrieved successfully", document.Message);
}
}
If we want to test that we are retrieving documents from the Couchbase API properly, then generally we want to use a real instance (local test setup) of that API where possible. If we are mocking this then our tests are not really telling us about whether our code is working correctly (just that our mock is working the way we want it to).
When certain APIs are difficult to use real instances for (e.g. non-deterministic code, difficult to reproduce conditions such as network errors, slow dependencies, etc), that's when it can be useful to introduce an interface for that dependency and to mock that for our test.
Here's a very rough example that doesn't quite match the code snippets posted, but hopefully will give you some ideas on how to proceed.
public interface IDataAdapter {
IEnumerable<IGetResult> Get(string key);
}
public class CouchbaseAdapter : IDataAdapter {
/* Implement interface for Couchbase */
}
public class AppApi {
private IDataAdapter data;
public AppApi(IDataAdapter data) {
this.data = data;
}
public SomeResult Lookup(string key) {
try {
var result = data.Get(key);
return Transform(Decrypt(result));
} catch (Exception ex) { /* error handling */ }
}
}
[Fact]
public void TestWhenKeyExists() {
var testAdapter = Substitute.For<IDataAdapter>();
var api = new AppApi(testAdapter);
testAdapter.Get("abc").Returns(/* some valid data */);
var result = api.Lookup("abc");
/* assert that result is decrypted/transformed as expected */
Assert.Equal(expectedResult, result);
}
[Fact]
public void TestWhenKeyDoesNotExist() {
var testAdapter = Substitute.For<IDataAdapter>();
var api = new AppApi(testAdapter);
var emptyData = new List<IGetResult>();
testAdapter.Get("abc").Returns(emptyData);
var result = api.Lookup("abc");
/* assert that result has handled error as expected */
Assert.Equal(expectedError, result);
}
Here we've introduced a IDataAdapter type that our class uses to abstract the details of which implementation we are using to get data. Our real code can use the CouchbaseAdapter implementation, but our tests can use a mocked version instead. For our tests, we can simulate what happens when the data adapter throws errors or returns specific information.
Note that we're only testing AppApi here -- we are not testing the CouchbaseAdapter implementation, only that AppApi will respond in a certain way if its IDataAdapter has certain behaviour. To test our CouchbaseAdapter we will want to use a real instance, but we don't have to worry about those details for testing our AppApi transformation and decryption code.

Getting Masstransit to use scoped lifetime

I'm having an issue with getting the same instance of the IIdentityService(my own class) or IServiceProvider in my consumer observer. I have an identity service where I set the credentials for the user, which is used later in the consumer pipeline.
I have tried the code below along with other code and configuration changes.
_serviceProvider.GetRequiredService<IIdentityService>()
_serviceProvider.CreateScope()
var consumerScopeProvider = _serviceProvider.GetRequiredService<IConsumerScopeProvider>();
using (var scope = consumerScopeProvider.GetScope(context))
{
// this next line of code is where we must access the payload
// using a container specific interface to get access to the
// scoped IServiceProvider
var serviceScope = scope.Context.GetPayload<IServiceScope>();
var serviceProviderScoped = serviceScope.ServiceProvider;
IIdentityService identityService = _serviceProvider.GetRequiredService<IIdentityService>();
}
// Also tried this as Scoped
services.AddTransient<CustomConsumer>();
var busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri(busConfiguration.Address), h =>
{
h.Username(busConfiguration.Username);
h.Password(busConfiguration.Password);
});
cfg.ReceiveEndpoint(host, "queue_1", endpointCfg => ConfigureConsumers(endpointCfg, provider));
cfg.UseServiceScope(provider);
});
busControl.ConnectConsumeObserver(new ConsumeObserver(provider));
public class ConsumeObserver : IConsumeObserver
{
private readonly IServiceProvider _serviceProvider;
public ConsumeObserver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
Task IConsumeObserver.PreConsume<T>(ConsumeContext<T> context)
{
var identityMessage = (IIdentityMessage)context.Message;
if (identityMessage == null)
{
return Task.CompletedTask;
}
// Here I get a "Cannot resolve scoped service '' from root provider." error
IIdentityService identityService = _serviceProvider.GetRequiredService<IIdentityService>();
var task = identityService.SetIdentityAsync(identityMessage.Identity.TenantIdentifier, identityMessage.Identity.UserIdentifier);
// This gets a different instance of the IIdentityService.cs service.
//IIdentityService identityService = _serviceProvider.CreateScope().ServiceProvider.GetRequiredService<IIdentityService>();
// called before the consumer's Consume method is called
return task;
}
Task IConsumeObserver.PostConsume<T>(ConsumeContext<T> context)
{
// called after the consumer's Consume method is called
// if an exception was thrown, the ConsumeFault method is called instead
return TaskUtil.Completed;
}
Task IConsumeObserver.ConsumeFault<T>(ConsumeContext<T> context, Exception exception)
{
// called if the consumer's Consume method throws an exception
return TaskUtil.Completed;
}
}
// This is where I need to identity credentials
services.AddScoped<UserContext>((provider =>
{
// This gets a different instance of IIdentityService
var identityService = provider.GetRequiredService<IIdentityService>();
var contextProvider = provider.GetRequiredService<IContextProvider>();
var identity = identityService.GetIdentity();
return contextProvider.GetContext(identity.UserId);
}));
var task = identityService.SetIdentityAsync(identityMessage.Identity.TenantIdentifier, identityMessage.Identity.UserIdentifier);
The setting of the identity above should be retrievable below.
var identity = identityService.GetIdentity();
What I get is null reference as the service provider is of a different instance.
Can anyone tell me how to get the same instance of the service provider through out the consumer pipeline?

Unit Tests on Method that uses GetEntitiesAync (DocumentDB)

After mocking DocumentDBRepository class with its GetEntitiesAsync() method in the unit test, it is return a null value which is not I expect it to return.
Here's my method that I need to test
public async Task<Books> GetBooksByBookIdAsyncByLinq(string bookId)
{
var books = await _individualRepository.GetEntitiesAsync(t => t.BookID == bookId);
if (individualResponse.Any())
{
return individualResponse.FirstOrDefault();
}
return null;
}
Here's the unit test of this method, noticed that I set up the GetEntitiesAsync() method and expect it to return a book value. But it is returning null when I ran it:
[Fact]
public void Test_GetBooksByBookIdAsyncByLinq()
{
//Arrange
var bookID = "000";
//Mock DocumentDBRepository
var mockDocumentDBRepository = new Mock<IRepository<Book>>();
var expected = Get_BookValue();
mockDocumentDBRepository.Setup(x => x.GetEntitiesAsync(x => x.BookID == bookID))
.Returns(Task.FromResult(expected));
var component = new BookComponent(mockDocumentDBRepository.Object);
//Act
var result = component.GetBooksByBookIdAsyncByLinq(bookID);
//Assert
result.Result.Should().NotBeNull().And.
BeOfType<Book>();
}
private Book Get_BookValue(){
IEnumerable<Book> result = new List<Book>
{ new Book
{ BookID = "000", BookName = "TestSourceSystemName" } };
return result;
}
When I ran the unit test and debug inside the GetBooksByBookIdAsyncByLinq() method, it is not getting any results from the books variable and return null without any error.
The interesting thing is, when I change the GetEntitiesAsync() method to RunSQLQueryAsync() method, which means using SQL query instead of Linq, the unit test is returning the correct result.
Here's the method I am testing:
public async Task<Books> GetBooksByBookIdAsyncBySQL(string bookId)
{
var books = await _individualRepository.RunSQLQueryAsync("select * from c where c.BookID ==" + bookId);
if (individualResponse.Any())
{
return individualResponse.FirstOrDefault();
}
return null;
}
And here's the unit test for this method, noticed that I set up the RunQueryAsync() method and expect to return a book value. And it works:
[Fact]
public void Test_GetBooksByBookIdAsyncBySQL()
{
//Arrange
var bookID = "000";
var sqlQuery = "select * from c where c.BookID ==" + bookId;
//Mock DocumentDBRepository
var mockDocumentDBRepository = new Mock<IRepository<Book>>();
var expected = Get_BookValue();
//mockDocumentDBRepository.Setup(x => x.GetEntitiesAsync(x => x.BookID == bookID))
// .Returns(Task.FromResult(expected));
mockDocumentDBRepository.Setup(x => x.RunQueryAsync(sqlQuery))
.Returns(Task.FromResult(expected));
var component = new BookComponent(mockDocumentDBRepository.Object);
//Act
var result = component.GetBooksByBookIdAsyncBySQL(bookID);
//Assert
result.Result.Should().NotBeNull().And.
BeOfType<Book>();
}
private Book Get_BookValue(){
IEnumerable<Book> result = new List<Book>
{ new Book
{ BookID = "000", BookName = "TestSourceSystemName" } };
return result;
}
So I am thinking maybe the way I mock GetEntitiesAsync() method is incorrect. But I am not sure why...
Here are the RunQueryAsync() and GetEntitiesAsync() methods for reference:
public async Task<IEnumerable<T>> GetEntitiesAsync(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = GetQueryByPredicate(predicate);
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
public async Task<IEnumerable<T>> RunQueryAsync(string queryString)
{
IDocumentQuery<T> query = GetQueryBySQL(queryString);
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
The Setup method tries to look at the arguments passed into your method, and only match the specified behavior if those arguments are the same.
When you use GetQueryBySQL, Moq is able to detect that the sqlQuery string is the same (as in object.Equals()) as what's passed in, so it works correctly.
When you use GetEntitiesAsync, Moq looks at the two Expressions and thinks they are different because Expression comparison is based on memory equality. So even though the two x => x.BookID == bookIDs look the same to you and me, they are different Expressions at runtime.
Try using It.IsAny<>() instead:
mockDocumentDBRepository
.Setup(x => x.GetEntitiesAsync(It.IsAny<Expression<Func<Book, bool>>()))
.Returns(Task.FromResult(expected));
Supposing you get that working, you can use Callback or other strategies to test that the expression passed into GetEntitiesAsync has the behavior you're expecting it to have.

Disable Wrapping of Controller Results

I am currently using v3.2.5 of Abp.AspNetCore.
I am trying to integrate an Alpha package of Microsoft.AspNetCore.OData into the project which is so far looking ok.
However when i try and query the metadata controller http://localhost:51078/odata/v1/$metadata the result is wrapped.
Now this was an issue for the ODataControllers as well, but i could simply add
the [DontWrapResult] attribute.
I dont have direct access to the MetadataController so i am unable to add the attribute. Is there anyway to disable wrapping for an Abp project?
Thanks
Edit
Here is the current ConfigureServices method
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });
services
.AddAuthentication()
.AddCsDeviceAuth(options => { });
services
.AddOData();
//Configure Abp and Dependency Injection
var provider = services.AddAbp<PortalWebODataModule>(options =>
{
//Configure Log4Net logging
options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.LogUsing<Log4NetLoggerFactory>().WithConfig("log4net.config")
);
});
services.Configure<MvcOptions>(options =>
{
var abpResultFilter = options.Filters.First(f => f is AbpResultFilter);
options.Filters.Remove(abpResultFilter);
options.Filters.AddService(typeof(ODataResultFilter));
});
return provider;
}
You can implement IResultFilter and set WrapOnSuccess to false:
public class ResultFilter : IResultFilter, ITransientDependency
{
private readonly IAbpAspNetCoreConfiguration _configuration;
public ResultFilter(IAbpAspNetCoreConfiguration configuration)
{
_configuration = configuration;
}
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.HttpContext.Request.Path.Value.Contains("odata"))
{
var methodInfo = context.ActionDescriptor.GetMethodInfo();
var wrapResultAttribute =
GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(
methodInfo,
_configuration.DefaultWrapResultAttribute
);
wrapResultAttribute.WrapOnSuccess = false;
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
// No action
}
private TAttribute GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<TAttribute>(MemberInfo memberInfo, TAttribute defaultValue = default(TAttribute), bool inherit = true)
where TAttribute : class
{
return memberInfo.GetCustomAttributes(true).OfType<TAttribute>().FirstOrDefault()
?? memberInfo.DeclaringType?.GetTypeInfo().GetCustomAttributes(true).OfType<TAttribute>().FirstOrDefault()
?? defaultValue;
}
}
Then, in Startup class, add the filter in ConfigureServices method:
services.AddMvc(options =>
{
options.Filters.AddService(typeof(ResultFilter));
});
References:
AbpResultFilter.OnResultExecuting
ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault
Alternative solution; to completely disable WrapResult behavior within the system ( at the Core module registration):
var abpAspNetCoreConfiguration = Configuration.Modules.AbpAspNetCore();
abpAspNetCoreConfiguration.DefaultWrapResultAttribute.WrapOnSuccess = false;
abpAspNetCoreConfiguration.DefaultWrapResultAttribute.WrapOnError = false;
abpAspNetCoreConfiguration
.CreateControllersForAppServices(
typeof(AccessApplicationModule).GetAssembly()
);
WrapOnSuccess and WrapOnError flags can be set to false values.
ABP v6.5 and later
Implement IWrapResultFilter and add it to WrapResultFilters in the module's PreInitialize method.
See https://stackoverflow.com/questions/70947461/how-to-control-response-wrapping-in-abp-on-a-per-route-basis/70955045#70955045 for more details.
Before ABP v6.5
...including ABP v3.2.5 mentioned in the question.
Subclass AbpResultFilter:
using Abp.AspNetCore.Configuration;
using Abp.AspNetCore.Mvc.Results;
using Abp.AspNetCore.Mvc.Results.Wrapping;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
namespace AbpODataDemo.Web.Host.Filters
{
public class ODataResultFilter : AbpResultFilter
{
public ODataResultFilter(IAbpAspNetCoreConfiguration configuration, IAbpActionResultWrapperFactory actionResultWrapperFactory)
: base(configuration, actionResultWrapperFactory)
{
}
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.HttpContext.Request.Path.Value.StartsWith("/odata", StringComparison.InvariantCultureIgnoreCase))
{
return;
}
base.OnResultExecuting(context);
}
}
}
Replace AbpResultFilter with it in the Startup ConfigureServices method:
services.PostConfigure<MvcOptions>(options =>
{
var index = options.Filters.IndexOf(new ServiceFilterAttribute(typeof(AbpResultFilter)));
if (index != -1)
{
options.Filters.RemoveAt(index);
options.Filters.Insert(index, new ServiceFilterAttribute(typeof(ODataResultFilter)));
}
});
Reference: https://github.com/aspnetboilerplate/sample-odata/pull/16

How to generate a link to an HTTP POST action with Hyprlinkr?

I'm trying to use Hyprlinkr to generate URL to the HTTP Post action. My controller looks like this:
public class MyController : ApiController {
[HttpPost]
public void DoSomething([FromBody]SomeDto someDto) {
...
}
}
with this route:
routes.MapHttpRoute(
name: "MyRoute",
routeTemplate: "dosomething",
defaults: new { controller = "My", action = "DoSomething" });
I expect to get a simple URL: http://example.com/dosomething, but it does not work. I tried two methods:
1) routeLinker.GetUri(c => c.DoSomething(null)) - throws NullReferenceException
2) routeLinker.GetUri(c => c.DoSomething(new SomeDto())) - generates invalid URL:
http://example.com/dosomething?someDto=Namespace.SomeDto
Update:
Issue opened at github:
https://github.com/ploeh/Hyprlinkr/issues/17
I found a workaround, loosely based on Mark's answer. The idea is to go over every route parameter and remove those that have [FromBody] attribute applied to them. This way dispatcher does not need to be modified for every new controller or action.
public class BodyParametersRemover : IRouteDispatcher {
private readonly IRouteDispatcher _defaultDispatcher;
public BodyParametersRemover(String routeName) {
if (routeName == null) {
throw new ArgumentNullException("routeName");
}
_defaultDispatcher = new DefaultRouteDispatcher(routeName);
}
public Rouple Dispatch(
MethodCallExpression method,
IDictionary<string, object> routeValues) {
var routeKeysToRemove = new HashSet<string>();
foreach (var paramName in routeValues.Keys) {
var parameter = method
.Method
.GetParameters()
.FirstOrDefault(p => p.Name == paramName);
if (parameter != null) {
if (IsFromBodyParameter(parameter)) {
routeKeysToRemove.Add(paramName);
}
}
}
foreach (var routeKeyToRemove in routeKeysToRemove) {
routeValues.Remove(routeKeyToRemove);
}
return _defaultDispatcher.Dispatch(method, routeValues);
}
private Boolean IsFromBodyParameter(ParameterInfo parameter) {
var attributes = parameter.CustomAttributes;
return attributes.Any(
ct => ct.AttributeType == typeof (FromBodyAttribute));
}
}
The second option is the way to go:
routeLinker.GetUri(c => c.DoSomething(new SomeDto()))
However, when using a POST method, you'll need to remove the model part of the generated URL. You can do that with a custom route dispatcher:
public ModelFilterRouteDispatcher : IRouteDispatcher
{
private readonly IRouteDispatcher defaultDispatcher;
public ModelFilterRouteDispatcher()
{
this.defaultDispatcher = new DefaultRouteDispatcher("DefaultApi");
}
public Rouple Dispatch(
MethodCallExpression method,
IDictionary<string, object> routeValues)
{
if (method.Method.ReflectedType == typeof(MyController))
{
var rv = new Dictionary<string, object>(routeValues);
rv.Remove("someDto");
return new Rouple("MyRoute", rv);
}
return this.defaultDispatcher.Dispatch(method, routeValues);
}
}
Now pass that custom dispatcher into your RouteLinker instance.
Caveat: it's very late as I'm writing this and I haven't attempted to compile the above code, but I thought I'd rather throw an attempted answer here than have you wait several more days.
Dimitry's solution got me most of the way to where I wanted, however the routeName ctor param was a problem because StructureMap doesn't know what to put in there. Internally hyprlink is using UrlHelper to generate the URI, and that wants to know the route name to use
At that point, I see why URI generation is so tricky, because it is tied to the route names in the routing config and in order to support POST, we need to associate the method, with the correct routename and that is not known at dispatcher ctor time. Default hyprlinkr assumes there is only one route config named "DefaultRoute"
I changed Dimitry's code as follows, and adopted a convention based approach, where controller methods that start with "Get" are mapped to the route named "Get" and controller methods starting with "Add" are mapped to the route named "Add".
I wonder if there are better ways of associating a method with the proper named routeConfig?
public class RemoveFromBodyParamsRouteDispatcher : IRouteDispatcher
{
private static readonly ILog _log = LogManager.GetLogger(typeof (RemoveFromBodyParamsRouteDispatcher));
public Rouple Dispatch(MethodCallExpression method,
IDictionary<string, object> routeValues)
{
var methodName = method.Method.Name;
DefaultRouteDispatcher defaultDispatcher;
if (methodName.StartsWith("Get"))
defaultDispatcher = new DefaultRouteDispatcher("Get");
else if (methodName.StartsWith("Add"))
defaultDispatcher = new DefaultRouteDispatcher("Add");
else
throw new Exception("Unable to determine correct route name for method with name " + methodName);
_log.Debug("Dispatch methodName=" + methodName);
//make a copy of routeValues as contract says we should not modify
var routeValuesWithoutFromBody = new Dictionary<string, object>(routeValues);
var routeKeysToRemove = new HashSet<string>();
foreach (var paramName in routeValuesWithoutFromBody.Keys)
{
var parameter = method.Method
.GetParameters()
.FirstOrDefault(p => p.Name == paramName);
if (parameter != null)
if (IsFromBodyParameter(parameter))
{
_log.Debug("Dispatch: Removing paramName=" + paramName);
routeKeysToRemove.Add(paramName);
}
}
foreach (var routeKeyToRemove in routeKeysToRemove)
routeValuesWithoutFromBody.Remove(routeKeyToRemove);
return defaultDispatcher.Dispatch(method, routeValuesWithoutFromBody);
}
private static bool IsFromBodyParameter(ParameterInfo parameter)
{
//Apparently the "inherit" argument is ignored: http://msdn.microsoft.com/en-us/library/cwtf69s6(v=vs.100).aspx
const bool msdnSaysThisArgumentIsIgnored = true;
var attributes = parameter.GetCustomAttributes(msdnSaysThisArgumentIsIgnored);
return attributes.Any(ct => ct is FromBodyAttribute);
}
}

Resources