WCF Data Service return Complex Type - wcf

I created a ComplexType and am returning it in a service operation as shown here:
[WebGet]
public IQueryable<ComplexAddressType> GetCityByZip(string zip)
{
List<AddressType> normalizeAddress = NormalizeAddressProcess(new AddressType
{
ZIP = zip,
}).AddressList;
return normalizeAddress.Select(x =>new ComplexAddressType
{
ZIP = x.zip,
City = x.City,
State = x.State
}).AsQueryable();
}
When I try to invoke the service operation by calling http://localhost/MyService.svc/GetCityByZip?zip='20000', the service operation invocation works and the browser displays a list of cities.
When I try to invoke the service operation by calling http://localhost/MyService.svc/GetCityByZip?zip='20000'&$top=1, the browser displays an error page.
Could you can help me?

Assuming ComplexAddressType is actually a complex type, you cannot use the $top system query option with that service operation. If you enable verbose errors per the comment above, you are likely getting back this error:
Query options $orderby, $inlinecount, $skip and $top cannot be applied to the requested resource.
To be able to use $top with the service operation, you will need to return a collection of entity types rather than complex types.
You could also just introduce another parameter to your function call, so that you can use a URL such as the following:
http://localhost:59803/ScratchService.svc/GetProfiles?startsWith='ABC'&top=2
Sample code:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace Scratch.Web
{
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class ScratchService : DataService<ScratchContext>
{
static ScratchService()
{
Database.SetInitializer(new ScratchContextInitializer());
}
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
config.UseVerboseErrors = true;
}
[WebGet]
public IQueryable<User> GetUsers(int numUsers)
{
var users = new List<User>();
for (int i = 0; i < numUsers; i++)
{
users.Add(new User
{
Id = i,
Password = i.ToString(),
Username = i.ToString()
});
}
return users.AsQueryable();
}
[WebGet]
public IQueryable<Profile> GetProfiles(string startsWith, int top)
{
var profiles = new List<Profile>
{
new Profile{ DisplayName = "A", Preferences = "1" },
new Profile{ DisplayName = "AB", Preferences = "2" },
new Profile{ DisplayName = "ABC", Preferences = "3" },
new Profile{ DisplayName = "ABCD", Preferences = "4" },
new Profile{ DisplayName = "ABCDE", Preferences = "5" },
new Profile{ DisplayName = "ABCDEF", Preferences = "6" },
new Profile{ DisplayName = "ABCDEFG", Preferences = "7" }
};
return profiles.Where(p => p.DisplayName.StartsWith(startsWith)).Take(top).AsQueryable();
}
}
public class ScratchContextInitializer : DropCreateDatabaseAlways<ScratchContext>
{
}
public class ScratchContext : DbContext
{
public DbSet<User> Users { get; set; }
}
public class Profile
{
public string DisplayName { get; set; }
public string Preferences { get; set; }
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public Profile Profile { get; set; }
}
}

Last code will work when GetCityByZip method has 2 parameters. First one for zip and second one for top. In your case you have parameters inconsistency and wcf can't find method.

Related

Xunit Parameterize Selenium By Type

I have asked several basic questions related to this in the past and got great answers that explained several issues. I think i'm now in a position to ask the correct question now that I'm more aware of how Xunit works!
I am trying to parametrize several tests in C# using visual studio. I need each parameter to be displayed as an individual test that can be ran in isolation if required (I know there is a test collection runner and a separate test runner). The test collection runner is my issue.
I know that Xunit requires the parameters to be serialized in order for them to be picked up by the test collection runner. I also know that it by default can easily serialize basic data types like string, bool, int etc.
I have tried various approaches to do this with mixed results. My issue is trying to parameterize the Selenium type 'By'. I can't seem to be able to serialize this. I've tried to trick Xunit for example by using a dictionary List<string, By> and trying to serialize the in the dictionary (no luck!)
Here is the cleanest code I have come across that is simple and elegant for what i'm trying to do, but again I can't serialize the 'By' type. I have played around with changing the static property from bool to By and it returns only 1 test for all params, so it's not being serialized
public class ParamTest1
{
static string test3 = "TestXYZ";
public static TheoryData<int, bool, string, string> DataForTest1 = new TheoryData<int, bool, string, string>
{
{ 1, true, "First", test3 },
{ 2, false, "Second", test3},
{ 3, true, "Third", test3}
};
[Theory(DisplayName = "My First Test"), MemberData(nameof(DataForTest1))]
public void Test1(int valA, bool valB, string valC, string valD)
{
Assert.True(valB);
}
}
Which gives me
I am aware this particular code isn't invoking the IXunitSerializable
So here is an another working example of what I need but I just can't get it to work with the 'By' Type
public class ValidateTestCase : IXunitSerializable
{
public Guid Coupon { get; set; }
public bool IsValid { get; set; }
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue(nameof(Coupon), Coupon.ToString());
}
public void Deserialize(IXunitSerializationInfo info) { }
}
public class Testing
{
public static IEnumerable<object[]> ValidateTestCases
{
get
{
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("73e4d185-70cf-4ce4-bc3f-187b7a40e167"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("93b983fb-5b6a-4845-a769-db41900b7df9"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("99c03283-33cb-4e56-a010-c2bc0758ad27"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("16a7fe80-3111-44b0-9ebf-c7159bea637d"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("8b38b4aa-d70f-4ce7-8992-8a60936c5c58"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("abc60aa0-a33b-4057-8f99-5cdceda35c70"), IsValid = true } };
}
}
[Theory(DisplayName = "CouponService should validate coupons")]
[MemberData(nameof(ValidateTestCases))]
public void MyCouponService_Validates(ValidateTestCase vtc)
{
Assert.Equal(vtc.IsValid, true);
}
}
And finally for anyone wondering what the 'By' type is I am referring to it's :
[![enter image description here][2]][2]
Here it is in the debugger so you can see what's going on inside:
[![enter image description here][3]][3]
I know there's a lot going on in there but if anyone has any ideas or suggestions it would be great!
To summarize, I can't parameterize the Selenium 'data type' By.
[2]: https://i.stack.imgur.com/XcLcn.png
[3]: https://i.stack.imgur.com/T9so1.png
Serializing Class
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using OpenQA.Selenium;
using Xunit.Abstractions;
namespace XUnitTestProject1
{
public class ParameterizedHook
{
public By p1 { get; set; }
public By p2 { get; set; }
public string assertion { get; set; }
}
public class TheoryWrapper<T> : IXunitSerializable
{
public TheoryWrapper(string label, T #object)
{
Name = label;
Object = #object;
}
public TheoryWrapper()
{
}
public string Name { get; set; }
public T Object { get; set; }
public void Deserialize(IXunitSerializationInfo info)
{
Name = info.GetValue<string>("Label");
Object = JsonConvert.DeserializeObject<T>(info.GetValue<string>("objValue"));
}
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue("Label", Name, typeof(string));
var json = JsonConvert.SerializeObject(Object);
info.AddValue("objValue", json);
}
public override string ToString()
{
return Name;
}
}
}
Working Test
public static IEnumerable<object[]> ComplexTheoryData
{
get
{
return new List<object[]>
{
new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { p1 = nav_hold.pip_builder.dash_expand_pip_builder_menu, p2 = nav_hold.pip_builder.dash_project_stages, assertion = "WORK STAGE" })},
new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { p1 = nav_hold.pip_builder.dash_expand_pip_builder_menu, p2 = nav_hold.pip_builder.dash_project_stages, assertion = "WORK STAGES" })},
//new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { TestData = b.login })},
//new object[] {1, new TheoryWrapper<ParameterizedHook>("Pass1", new ParameterizedHook { TestData = b.password })}
};
}
}
[SkippableTheory]
[Trait("xUnit", "ForTestRunner")]
[MemberData(nameof(ComplexTheoryData))]
public void Test_Navigation(int id, TheoryWrapper<ParameterizedHook> test)
{
nav_met.NavMethodTest(test.Object.p1, test.Object.p2);
By page_title = By.Id("ctl00_lblPageTitle");
Assert.True(nav_met.VerifyText(page_title, test.Object.assertion));
}

How do I perform a query and then a command based on the query's result with MediatR?

I am attempting to use the MediatR library to implement a command pattern in my net core web API, however, I am unsure how to proceed.
I have a situation where when a user attempts to registers an account, the API should check the database for a company with a domain that matches that of the provided email address then attach the company id to the user object as a foreign key or return an error if no company exists with that domain.
I have all the necessary commands and handler to perform these separately:
GetCompanyByDomainHandler.cs
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Application.Application.Exceptions;
using Application.Domain.Entities;
using Application.Persistence;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Application.Application.Companies.Queries.GetCompanyByDomain
{
public class GetCompanyByDomainHandler
IRequestHandler<GetCompanyByDomainQuery, Company>
{
private readonly ApplicationDbContext _context;
public GetCompanyByDomainHandler(ApplicationDbContext context)
{
_context = context;
}
public async Task<Company> Handle(GetCompanyByDomainQuery request,
CancellationToken cancellationToken)
{
var company = await _context.Companies.Where(c => c.Domain ==
request.Domain).SingleOrDefaultAsync();
if (company != null) {
return company;
}
throw new NotFoundException(nameof(Company), request.Domain);
}
}
}
GetCompanyByDomainQuery.cs
using Application.Domain.Entities;
using MediatR;
namespace Application.Application.Companies.Queries.GetCompanyByDomain
{
public class GetCompanyByDomainQuery : IRequest<Company>
{
public string Domain { get; set; }
}
}
CreateUserCommand.cs
using MediatR;
namespace Application.Application.Users.Commands.CreateUser
{
public class CreateUserCommand : IRequest<int>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public int CompanyId { get; set; }
}
}
CreateUserCommandHandler.cs
using MediatR;
using Application.Domain.Entities.Identity;
using Microsoft.AspNetCore.Identity;
using System.Threading;
using System.Threading.Tasks;
using System;
namespace Application.Application.Users.Commands.CreateUser
{
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, int>
{
private readonly UserManager<User> _userManager;
public CreateUserCommandHandler(UserManager<User> userManager)
{
_userManager = userManager;
}
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var entity = new User
{
FirstName = request.FirstName,
LastName = request.LastName,
Email = request.EmailAddress,
UserName = request.EmailAddress,
CompanyId = request.CompanyId
};
var createUserResult = await _userManager.CreateAsync(entity, request.Password);
if (createUserResult.Succeeded)
{
return entity.Id;
}
throw new Exception("failed to create user");
}
}
}
CreateUserCommandValidator.cs
using FluentValidation;
namespace Application.Application.Users.Commands.CreateUser
{
public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
{
public CreateUserCommandValidator()
{
RuleFor(v => v.Password)
.Equal(v => v.ConfirmPassword).WithName("password").WithMessage("Passwords do not match");
RuleFor(v => v.ConfirmPassword)
.Equal(v => v.Password).WithName("confirmPassword").WithMessage("Passwords do not match");
RuleFor(v => v.EmailAddress)
.NotEmpty().WithName("emailAddress").WithMessage("Email Address is required")
.EmailAddress().WithName("emailAddress").WithMessage("Invalid email address");
RuleFor(v => v.FirstName)
.NotEmpty().WithName("firstName").WithMessage("First Name is required");
RuleFor(v => v.LastName)
.NotEmpty().WithName("lastName").WithMessage("Last Name is required");
}
}
}
AuthenticationController.cs
using System.Threading.Tasks;
using Application.Application.Users.Commands.CreateUser;
using Microsoft.AspNetCore.Mvc;
namespace Application.WebUI.Controllers
{
public class AuthenticationController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Register([FromBody] CreateUserCommand command)
{
return Ok(Mediator.Send(command));
}
}
}
But how do I make them part of a single request?
First of all, change your GetCompanyByDomainHandler to NOT throw an exception if the company isn't found.
A company not found isn't an exception, it's a result of a query. Just return null
See why
Now, you can get the result of the query, act upon it (rather than a try catch)
//todo: implement a way of getting the domain from an email address - regex, or string.split('.') ??
var domain = GetDomainFromEmail(command.EmailAddress);
//now get the company (or null, if it doesn't exist)
var getCompanyByDomainQuery = new GetCompanyByDomainQuery() { Domain = domain}
var company = await _mediator.SendAsync(getCompanyByDomainQuery);
//if theres a company, attach the id to the createUserCommand
if(company != null)
{
command.CompanyId = company.Id;
}
//now save the user
var createdUser = await _mediator.SendAsync(command);
You can either wrap this up in another Handler - or treat the API Action Method as the 'orchestrator' (my preference)
Many years later, having worked with MediatR a lot more, I have realised that my question is fundamentally wrong. You shouldn't call a command after a query. If you need to do that, then you should merge the two together as they are performing a single domain driven action. The GetDomainFromEmail should be something that the CreateUserCommand uses within its own Handler rather than it being its own query. It could indeed be its own query if an endpoint existed to get the domain from an email, and both the CreateUserCommand Handler and the GetDomainFromEmailQuery handler would utilise a common utility that actually extracts the domain from an email.

Setting up examples in Swagger

I am using Swashbuckle.AspNetCore.Swagger (1.0.0) and Swashbuckle.AspNetCore.SwaggerGen (1.0.0). I am trying to add default examples to my API following Default model example in Swashbuckle (Swagger). I created a new class file and added,
public class SwaggerDefaultValue : Attribute
{
public string ParameterName { get; set; }
public string Value { get; set; }
public SwaggerDefaultValue(string parameterName, string value)
{
this.ParameterName = parameterName;
this.Value = value;
}
}
public class AddDefaultValues : IOperationFilter
{
public void Apply(Operation operation, DataTypeRegistry dataTypeRegistry, ApiDescription apiDescription)
{
foreach (var param in operation.Parameters)
{
var actionParam = apiDescription.ActionDescriptor.GetParameters().First(p => p.ParameterName == param.Name);
if (actionParam != null)
{
var customAttribute = actionParam.ActionDescriptor.GetCustomAttributes<SwaggerDefaultValue>().FirstOrDefault();
if (customAttribute != null)
{
param.DefaultValue = customAttribute.Value;
}
}
}
}
}
but I get this error - AddDefaultValues does not implement interface member IOperationFilter.Apply(Operation, OperationFilterContext)
That link you are following is not for the Swashbuckle.AspNetCore version
Look in the correct project for the proper examples:
https://github.com/domaindrivendev/Swashbuckle.AspNetCore/search?q=IOperationFilter&unscoped_q=IOperationFilter

webapi receiving null parameter

I'm attempting to POST an object to a WebAPI controller that accepts a complex type as the parameter but the parameter recived is null. Any ideas why? The request is hitting the WebAPI method properly and the parameter is null.
Model :
namespace DMAX.BLL.MASReports.Models
{
public class StatsCriteria
{
#region Constructors and Methods
public StatsCriteria()
{
}
#endregion
#region Properties and Fields
private string _masnum;
private string _notchosen;
private int _currentPage = 1;
private bool _isPrint = false;
private bool _isEmail = false;
private bool _isAjax = false;
public string Masnums { get {
if (!string.IsNullOrEmpty(_masnum)) {
_masnum = _masnum.Replace("'", "");
if (!string.IsNullOrEmpty(NotChosen)) {
string[] notchosenlist = NotChosen.Split(',');
foreach (var notchosen in notchosenlist) {
_masnum = this.RemoveNotChosen(_masnum, notchosen);
}
}
return _masnum;
}
return null;
}
set { _masnum = value; }
}
public string AgentId { get; set; }
public string LicenseNum { get; set; }
public string AgentFullName { get; set; }
public string HeaderName { get; set; }
#endregion
}
}
}
Here's the code at client : [ The StatsCriteria is part of the project BLL and I am referencing it in MASReports project]
namespace MASReports.Controllers
{
public ActionResult Reports(StatsCriteria criteria)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsJsonAsync("http://localhost:52765/api/reports", criteria.Masnums.ToString()).Result;
return View("CMAReport", response);
}
}
Here's the signature for my controller in Webapi.
[ The StatsCriteria is part of the project BLL and I have a reference to that project in ReportsAPI project]
[ The CMAReportVM, CMAReport are part of the project BLL and I have a reference to BLL project in ReportsAPIproject]
namespace ReportsAPI.Controllers
{
public class ReportsController : ApiController
{
[HttpPost]
public CMAReportVM Reports([FromBody] StatsCriteria criteria)
{
var cmaReport = Service3.GetCMAReport(criteria.Masnums);
//Create Map to enable mapping business object to View Model
Mapper.CreateMap<CMAReport, CMAReportVM>();
// Maps model to VM model class
var cmaVM = Mapper.Map<CMAReport, CMAReportVM>(cmaReport);
reutn cmaVM;
}
}
}
// and here's my routing:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
and here's my Golbal.asax of Web api
namespace ReportsAPI
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(new QueryStringMapping("json", "true", "application/json"));
}
}
}
You are posting a string:
var response = client.PostAsJsonAsync("http://localhost:52765/api/reports", criteria.Masnums.ToString()).Result;
Your controller method expect a StatsCriteria object. Either change the signature of your controller method to accept a string or change the post call.
Assuming that the controller method signature is correct the post should be something like this:
var response = client.PostAsJsonAsync("http://localhost:52765/api/reports", criteria).Result;
If this doesn't help I recommend to use fiddler to check what the message looks like when you post it.

RavenDB static index on dictionary

I have an application that uses documents, that contain list of attributes in a dictionary, for some reason we need to use a static index and query/filter over these attributes.
A prototype looks like this:
class Program
{
static void Main(string[] args)
{
IDocumentStore store = new DocumentStore() { DefaultDatabase = "Test", Url = "http://localhost:8081" };
store.Initialize();
IndexCreation.CreateIndexes(typeof(Program).Assembly, store);
using (var session = store.OpenSession())
{
session.Store(new Document { Id = "1", Name = "doc_name", Attributes = new Dictionary<string, object> { { "Type", "1" }, { "Status", "Active" } } });
session.SaveChanges();
}
using (var session = store.OpenSession())
{
// works
var l1 = session.Query<Document, Documents_Index>().Where(a => a.Attributes["Type"] == "1").ToList();
// not working
var l2 = session.Query<Document, Documents_Index>().Where(a => a.Attributes["Status"] == "Active").ToList();
}
}
}
public class Documents_Index : AbstractIndexCreationTask<Document>
{
public Documents_Index()
{
Map = docs => docs.Select(a =>
new
{
a.Name,
a.Attributes,
Attributes_Type = a.Attributes["Type"]
});
}
}
[Serializable]
public class Document
{
public string Id { get; set; }
public string Name { get; set; }
public Dictionary<string, object> Attributes { get; set; }
}
But since I need to query using any arbitrary Attribute name/value this index does solve our problem. Actually the list of attributes is known at run-time (so we tried modifying the Map expression to inject any number of attribute names, but so far we weren't successful). Is there a way how to define the index in some dynamic fashion?
You need to write it like:
public class Documents_Index : AbstractIndexCreationTask<Document>
{
public Documents_Index()
{
Map = docs => docs.Select(a =>
new
{
a.Name,
_ = a.Attributes.Select(x=>CreateField("Attributes_"+x.Key, x.Value),
});
}
}