I am trying to post data from a react application to a wcf service class. Its a simple object with just name age and gender. The service is being called from the react app but with no data. If I use get request and parameter passing the data is passing to the service but I want to use post request. Is it necessary to match the field names as in the wcf service class.
For testing purpose in react component I am using this code
const headers = {
'content-type': 'application/json'
}
const puser = {
Name:'Test',
Age:'23',
Gender:'F',
}
axios.post('http://localhost:3292/myService.svc/adduser',{headers:headers}, puser)
.then(res=> {
console.log(res.data)
})
in wcf service the class code is
public class UserClass
{
string name;
int age;
string gender;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
public string Gender
{
get
{
return gender;
}
set
{
gender = value;
}
}
}
and in service call
public string AddUser(List<User> U)
{
UserClass usr = new UserClass();
return "success";
}
Axios post method expects data as second argument. Try to swap puser with {headers}.
https://github.com/axios/axios#axiosposturl-data-config
Do not add header in axios.post, code should look like this:
const puser = {
Name:'Test',
Age:'23',
Gender:'F',
}
axios.post('http://localhost:8000/AddUser', puser)
.then(res=> {
console.log(res.data)
})
Feel free to let me know if the problem persists.
UPDATE:
Set header in Postman:
Axios.post can also set the header like the following:
axios.post('http://localhost:8000/AddUser',{ Name:'Test',
Age:'2311',
Gender:'FF'}, {headers: {
'content-type': 'application/json'
}})
.then(res=> {
console.log(res.data)
})
Here is my project:
Program.cs:
using Demo_rest_ConsoleApp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp71
{
public class User
{
string name;
int age;
string gender;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
public string Gender
{
get
{
return gender;
}
set
{
gender = value;
}
}
}
[ServiceContract]
[CustContractBehavior]
public interface IService
{
[OperationContract]
[WebGet]
string EchoWithGet(string s);
[OperationContract]
[WebInvoke]
string EchoWithPost(string s);
[OperationContract]
[WebInvoke(ResponseFormat =WebMessageFormat.Json,RequestFormat =WebMessageFormat.Json)]
string AddUser(User U);
}
public class Service : IService
{
public string AddUser(User U)
{
Console.WriteLine(U.Age);
Console.WriteLine(U.Gender);
Console.WriteLine(U.Name);
return "success";
}
public string EchoWithGet(string s)
{
return "You said " + s;
}
public string EchoWithPost(string s)
{
return "You said " + s;
}
}
class Program
{
static void Main(string[] args)
{
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri("http://localhost:8000/"));
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "");
ep.EndpointBehaviors.Add(new WebHttpBehavior() { HelpEnabled=true});
host.Open();
Console.WriteLine("Open");
Console.ReadLine();
}
}
}
Soap.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
namespace Demo_rest_ConsoleApp
{
public class ServerMessageLogger : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
Console.WriteLine(request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
if (ctx.IncomingRequest.Method == "OPTIONS")
{
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,POST,DELETE,OPTIONS");
ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
}
}
}
public class ClientMessageLogger : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple = false)]
public class CustContractBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
{
public Type TargetContract => throw new NotImplementedException();
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageLogger());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new ServerMessageLogger());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
}
Related
In this, I have implemented a .Net Core web API where I have two model classes Student and Department.
I have implemented One to One relationship between these two entities. There are two controllers StudentController and DepartmentController. Student model has reference of Department entity and has DepartmentId as foreign key. Each student is assigned to a department. What implementation is required to perform multiple Students entry in database and how to check the same implementation in Postman?
How to create endpoint to add multiple students using JSON format without ViewModel?
Student.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Students.Models
{
public class Student
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int SId { get; set; }
[Required]
[Column(TypeName ="varchar(50)")]
public string Name { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}
Department.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Students.Models
{
public class Department
{
[Key]
public int Id { get; set; }
[Required]
[Column(TypeName = "varchar(20)")]
public string Dep { get; set; }
}
}
DepartmentController.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Students.Models;
namespace Students.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DepartmentController : ControllerBase
{
private readonly StudentContext _context;
public DepartmentController(StudentContext context)
{
_context = context;
}
// GET: api/Department
[HttpGet]
public async Task<ActionResult<IEnumerable<Department>>> GetDepartments()
{
return await _context.Departments.ToListAsync();
}
// GET: api/Department/5
[HttpGet("{id}")]
public async Task<ActionResult<Department>> GetDepartment(int id)
{
var department = await _context.Departments.FindAsync(id);
if (department == null)
{
return NotFound();
}
return department;
}
// PUT: api/Department/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task<IActionResult> PutDepartment(int id, Department department)
{
if (id != department.Id)
{
return BadRequest();
}
_context.Entry(department).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!DepartmentExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return Ok();
}
// POST: api/Department
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task<ActionResult<Department>> PostDepartment(Department department)
{
_context.Departments.Add(department);
await _context.SaveChangesAsync();
return CreatedAtAction("GetDepartment", new { id = department.Id }, department);
}
// DELETE: api/Department/5
[HttpDelete("{id}")]
public async Task<ActionResult<Department>> DeleteDepartment(int id)
{
var department = await _context.Departments.FindAsync(id);
if (department == null)
{
return NotFound();
}
_context.Departments.Remove(department);
await _context.SaveChangesAsync();
return department;
}
private bool DepartmentExists(int id)
{
return _context.Departments.Any(e => e.Id == id);
}
}
}
StudentsController.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Students.Models;
namespace Students.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentsController : ControllerBase
{
private readonly StudentContext _context;
public StudentsController(StudentContext context)
{
_context = context;
}
// GET: api/Students
[HttpGet]
public async Task<ActionResult<IEnumerable<Student>>> GetStudents()
{
return await _context.Students.Include(d => d.Department).ToListAsync();
}
// GET: api/Students/5
[HttpGet("{id}")]
public async Task<ActionResult<Student>> GetStudent(int id)
{
var student = await _context.Students.Include(d => d.Department).FirstOrDefaultAsync(i => i.SId == id);
if (student == null)
{
return NotFound();
}
return student;
}
// PUT: api/Students/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task<IActionResult> PutStudent(int id, Student student)
{
if (id != student.SId)
{
return BadRequest();
}
_context.Departments.Update(student.Department);
await _context.SaveChangesAsync();
_context.Entry(student).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!StudentExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return Ok();
}
// POST: api/Students
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task<ActionResult<Student>> PostStudent(Student student)
{
//_context.Departments.Add(student.Department);
//await _context.SaveChangesAsync();
_context.Students.Add(student);
await _context.SaveChangesAsync();
return CreatedAtAction("GetStudent", new { id = student.SId }, student);
}
// DELETE: api/Students/5
[HttpDelete("{id}")]
public async Task<ActionResult<Student>> DeleteStudent(int id)
{
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return NotFound();
}
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return student;
}
private bool StudentExists(int id)
{
return _context.Students.Any(e => e.SId == id);
}
}
}
StudentContext.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace Students.Models
{
public class StudentContext:DbContext
{
public StudentContext(DbContextOptions<StudentContext> options) : base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Department> Departments { get; set; }
}
}
StudentContextModelSnapshot.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Students.Models;
namespace Students.Migrations
{
[DbContext(typeof(StudentContext))]
partial class StudentContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Students.Models.Department", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Dep")
.IsRequired()
.HasColumnType("varchar(20)");
b.HasKey("Id");
b.ToTable("Departments");
});
modelBuilder.Entity("Students.Models.Student", b =>
{
b.Property<int>("SId")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("DepartmentId")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(50)");
b.HasKey("SId");
b.HasIndex("DepartmentId");
b.ToTable("Students");
});
modelBuilder.Entity("Students.Models.Student", b =>
{
b.HasOne("Students.Models.Department", "Department")
.WithMany()
.HasForeignKey("DepartmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
What implementation is required to perform multiple Students entry in database and how to check the same implementation in Postman?
You could add PostStudentList method like below :
[HttpPost]
[Route("StudentList")]
public async Task<ActionResult<Student>> PostStudentList([FromForm] List<Student> student)
{
_context.Students.AddRange(student);
await _context.SaveChangesAsync();
return CreatedAtAction("GetStudents", student);
}
Use Postman to send the list of students with form-data contentType
Updated ,send entries in JSON format
PostStudentList method :
public class StudentVM
{
public List<Student> student { get; set; }
}
[HttpPost]
[Route("StudentList")]
public async Task<ActionResult<Student>> PostStudentList(StudentVM model)
{
var students = new List<Student>();
students = model.student;
_context.Students.AddRange(students);
await _context.SaveChangesAsync();
return CreatedAtAction("GetStudents", students);
}
Postman:
I'm building a desktop APP using windows forms that needs to be authenticated via a WebAPI using Token authentication.
The API is proved that work because a mobile APP is using it and also I can get results using POSTMAN
The problem is when I'm calling the Authentication method from the desktop App.
When I do the request, the API recieves it and it only goes until ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context), not reaching GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) in the Auth process.
Here is my CustomAuthProvider
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "El nombre de usuario o contraseña son incorrectos");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
}
Here is my Startup class
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
HttpConfiguration httpConfig = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
ConfigureWebApi(httpConfig);
}
}
At the moment I'm trying two different ways to authenticate the APP.
First One:
public LoginResponseModel Authenticate(LoginRequestModel applicationUser)
{
using (var client = new WebClient())
{
try
{
client.Headers["Content-Type"] = "application/json";
var data = applicationUser.Serialize();
var response = client.UploadString(Context.ApiUrl + "Authenticate","POST", JsonConvert.SerializeObject(applicationUser));
var resultJson = JsonConvert.DeserializeObject<LoginResponseModel>(response);
return resultJson;
}
catch (Exception exception)
{
}
}
return null;
}
And second one:
public async Task<ApplicationUser> Authenticate(LoginRequestModel applicationUser)
{
var client = new HttpClient();
try
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var data = applicationUser.Serialize();
var response = await client.PostAsJsonAsync(Context.ApiUrl + "Authenticate",data );
// return null by default (test)
return null;
}
catch (Exception exception)
{
}
return null;
}
And this is the model I'm using for the request
public class LoginRequestModel
{
public string Grant_type { get; set; } = "Password";
public string UserName { get; set; }
public string Password { get; set; }
}
And this should be the response:
public class LoginResponseModel
{
public string Access_token { get; set; }
public string Token_type { get; set; }
public string Expires_in { get; set; }
}
Ah the moment both ways of calling the API only reach the initial verification of the owin process (ValidateClientAuthentication). What can be happening? How I can fix this? What I need to do to make the process go to GrantResourceOwnerCredentials?
thanks for the help
I solved my problem. The problem was that the form wasn't being filled and sent correctly.
private AuthToken GetAuthToken(LoginRequestModel applicationUser)
{
using (var client = new HttpClient())
{
var form = new Dictionary<string, string>
{
{"grant_type", "password"},
{"username", applicationUser.UserName},
{"password", applicationUser.Password},
};
try
{
var tokenResponse = client.PostAsync(Context.ApiUrl + "Authenticate", new FormUrlEncodedContent(form)).Result;
var token = tokenResponse.Content.ReadAsAsync<AuthToken>(new[] { new JsonMediaTypeFormatter() }).Result;
return token;
}
catch (Exception e)
{
Log4Net.log.Error("Error Getting Auth token", e);
return null;
}
}
}
I have created a Google Endpoint in my App Engine Server as follows:
package com.xxxxx.gcmbackend;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.response.CollectionResponse;
import java.util.List;
import java.util.logging.Logger;
import javax.inject.Named;
import static com.xxxxxx.gcmbackend.OfyService.ofy;
#Api(
name = "register",
version = "v1",
namespace = #ApiNamespace(
ownerDomain = "gcmbackend.xxxxx.com",
ownerName = "gcmbackend.xxxxx.com",
packagePath=""
)
)
public class UserRegistrationEndpoint {
private static final Logger log = Logger.getLogger(RegistrationEndpoint.class.getName());
#ApiMethod(name = "register")
public void registerDevice(#Named("regId") String regId, #Named("username") String username, #Named("phone") String phone) {
if(findRecord(regId) != null) {
log.info("Device " + regId + " already registered, skipping register");
return;
}
RegistrationRecord record = new RegistrationRecord();
record.setRegId(regId);
record.setUsername(username);
record.setPhone(phone);
ofy().save().entity(record).now();
}
private RegistrationRecord findRecord(String regId) {
return ofy().load().type(RegistrationRecord.class).filter("regId", regId).first().now();
}
}
This works perfectly in creating new User records. The API is of the following format:
http://example.appspot.com/_ah/api/register/v1/registerDevice/<regId>/<username>/<phone>
However, I want the url to look like this:
http://example.appspot.com/_ah/api/register/v1/registerDevice/
and then send POST data as follows:
{
regId: "some_value",
username: "some_value",
phone: "some_value"
}
What do I need to change in my Endpoint in order to achieve this format?
You need to create a java bean with regId, username and phone attributes e.g. RegistrationInput.
public class RegistrationInput {
private String regId;
private String username;
private String phone;
public String getRegId() {
return regId;
}
public void setRegId(String regId) {
this.regId = regId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Then add the above java bean RegistrationInput, as a parameter to the ApiMethod
#ApiMethod(name = "register")
public void registerDevice(RegistrationInput input) {
.....
}
I have a WPF application. I am making REST calls from that.
I would like to alter the response XML/JSON of the rest service.
I am using FiddlerCore to intercept the call.
I need to listen to ALL the ports in my local machine.
List<Fiddler.Session> oAllSessions = new List<Fiddler.Session>();
FiddlerCoreStartupFlags oFCSF = FiddlerCoreStartupFlags.Default;
oFCSF = (oFCSF & ~FiddlerCoreStartupFlags.DecryptSSL);
//Fiddler.FiddlerApplication.Startup(8080, true, true);
FiddlerApplication.BeforeRequest += delegate(Fiddler.Session oS)
{
};
FiddlerApplication.BeforeResponse += delegate(Fiddler.Session oS)
{
}
};
Fiddler.FiddlerApplication.Startup(0, true, false);
This issue is resolved - Look at the below link
https://gist.githubusercontent.com/timiles/4079321/raw/268f71249f381649a06f4b48ebfb54cbaa8ee282/MockWebProxyHelper.cs
using System;
using System.Net;
// http://www.fiddler2.com/fiddler/Core/
using Fiddler;
public static class MockWebProxyHelper
{
public enum HttpMethods
{
GET, POST, PUT, Unknown
}
public class Response
{
public Response(string header = "HTTP/1.1 200 OK", string body = "", string contentType = "application/json")
{
Header = header;
Body = body;
ContentType = contentType;
}
public string Header { get; private set; }
public string Body { get; private set; }
public string ContentType { get; private set; }
}
public static Func<HttpMethods, string, Response> GetMockResponse = delegate { return new Response(); };
public static Func<HttpMethods, string, bool> InterceptRequest = delegate { return true; };
public static void SetUp(bool registerAsSystemProxy = false)
{
const int port = 18833;
FiddlerApplication.Startup(port, FiddlerCoreStartupFlags.DecryptSSL
| (registerAsSystemProxy ? FiddlerCoreStartupFlags.RegisterAsSystemProxy : FiddlerCoreStartupFlags.None));
WebRequest.DefaultWebProxy = new WebProxy("localhost", port);
FiddlerApplication.BeforeRequest += BeforeRequest;
}
private static void BeforeRequest(Session session)
{
var httpMethod = GetHttpMethod(session);
var url = session.url;
if (InterceptRequest(httpMethod, url))
{
session.utilCreateResponseAndBypassServer();
var response = GetMockResponse(httpMethod, url);
session.oResponse.headers = Parser.ParseResponse(response.Header);
session.oResponse.headers.Add("Content-Type", response.ContentType);
session.utilSetResponseBody(response.Body);
}
}
private static HttpMethods GetHttpMethod(Session session)
{
return session.HTTPMethodIs("GET") ? HttpMethods.GET
: session.HTTPMethodIs("POST") ? HttpMethods.POST
: session.HTTPMethodIs("PUT") ? HttpMethods.PUT : HttpMethods.Unknown;
}
public static void TearDown()
{
FiddlerApplication.BeforeRequest -= BeforeRequest;
FiddlerApplication.oProxy.Detach();
FiddlerApplication.Shutdown();
}
}
What is the cleanest way to map a string column to a Uri property using Dapper?
Here's the cleanest I've been able to come up with so far (using the ITypeMap functionality):
Query:
SELECT * FROM TableWithAStringAddressColumn
POCO:
public class MyPoco
{
[ColumnSetter("DapperAddress")]
public Uri Address { get; set; }
private string DapperAddress { set { this.Address = new Uri(value); } }
}
Extensions:
partial class SqlMapper
{
public static void InitializeTypeMaps()
{
SqlMapper.SetTypeMap(
typeof(MyPoco),
new CustomPropertyTypeMap(typeof(MyPoco), SqlMapper.CustomSetterMapper));
// call out every other class that needs this kind of mapping
}
public static Func<Type, string, PropertyInfo> CustomSetterMapper =
(type, columnName) =>
{
PropertyInfo prop = type
.GetProperties()
.FirstOrDefault(p => string.Equals(columnName, p.Name, StringComparison.OrdinalIgnoreCase));
if (prop != null)
{
// find out if we need to use a different setter
ColumnSetterAttribute setterAttribute = prop.GetCustomAttributes(false).OfType<ColumnSetterAttribute>().LastOrDefault();
if (setterAttribute != null)
{
PropertyInfo setterProp = type
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(p => string.Equals(setterAttribute.Setter, p.Name, StringComparison.OrdinalIgnoreCase));
if (setterProp == null)
{
throw new InvalidOperationException(string.Format("Setter property misconfigured (Property={0}, Setter={1})", prop.Name, setterAttribute.Setter));
}
else
{
prop = setterProp;
}
}
}
return prop;
};
}
Custom Attribute:
public class ColumnSetterAttribute : Attribute
{
public string Setter { get; set; }
public ColumnSetterAttribute(string setter)
{
this.Setter = setter;
}
}
[edit] I'm looking for a solution I can use without needing to call out all columns in all my queries (I'd like to find a solution where I can use SELECT *).
Seems like a lot of work...
Wouldn't this be ok?
public class MyPoco
{
private string _uriMapper;
public Uri SomeUri
{
get { return new Uri(_uriMapper); }
}
public string Mapper { set { _uriMapper = value; } }
}
Edit:
public class UriContainer
{
private string _uriMapper;
public string UriMapper { set { _uriMapper = value; } }
public int Id { get; set; }
public Uri SomeUri { get {return new Uri(_uriMapper);} }
}
public class DbTests
{
[Test]
public void Can_Get_A_Uri()
{
using (var c = new SqlConnection("hello"))
{
c.Open();
var uri = c.Query<UriContainer>("select *, someuri as urimapper from uris where id = 3").Single();
Console.WriteLine(uri.SomeUri);
}
}
}