My ModelState is getting false everytime i run the code .This is simply a file upload mvc .net core code. Migration is also perfectly executed. However whenever i try to submit the form after uploading an image the form gets reset. Due to which it failed to get store in the database.
Model code (Image.cs)
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace FileUpload.Models
{
public class Image
{
[Key]
public int Iid { get; set; }
[Required]
public string Iname { get; set; }
[Required]
[NotMapped]
public IFormFile Iimg { get; set; }
}
}
Controller Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using FileUpload.Data;
using FileUpload.Models;
using Microsoft.AspNetCore.Hosting;
using System.IO;
namespace FileUpload.Controllers
{
public class ImagesController : Controller
{
private readonly AppDbContext _context;
private readonly IWebHostEnvironment _hostEnvironment;
public ImagesController(AppDbContext context, IWebHostEnvironment hostEnvironment)
{
_context = context;
_hostEnvironment = hostEnvironment;
}
// GET: Images
public async Task<IActionResult> Index()
{
return View(await _context.Images.ToListAsync());
}
// GET: Images/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var image = await _context.Images
.FirstOrDefaultAsync(m => m.Iid == id);
if (image == null)
{
return NotFound();
}
return View(image);
}
// GET: Images/Create
public IActionResult Create()
{
return View();
}
// POST: Images/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Image image)
{
if (ModelState.IsValid)
{
if(image.Iimg != null)
{
string wwwRootPath = _hostEnvironment.WebRootPath;
string fileName = Path.GetFileNameWithoutExtension(image.Iimg.FileName);
string ext = Path.GetExtension(image.Iimg.FileName);
image.Iname = fileName + DateTime.Now.ToString("yymmssfff") + ext;
string path = Path.Combine(wwwRootPath + "/Image/" + fileName);
using (var fileStream = new FileStream(path, FileMode.Create))
{
await image.Iimg.CopyToAsync(fileStream);
}
}
_context.Add(image);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(image);
}
}
I run into this problem:
Forms.context is obsolete.Context is obsolete as of version 2.5,please use a local context instead.
I m trying to login using Azure Active Directory with code below.
Please help.
using Xamarin.Forms;
using myMobile.Service;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
[assembly: Dependency(typeof(myMobile.Droid.Authenticator))]
namespace myMobile.Droid
{
class Authenticator: IAuthenticator
{
public async Task<AuthenticationResult> Authenticate(string tenantUrl, string graphResourceUri, string ApplicationID, string returnUri)
{
try
{
var authContext = new AuthenticationContext(tenantUrl);
if (authContext.TokenCache.ReadItems().Any())
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().FirstOrDefault().Authority);
var authResult = await authContext.AcquireTokenAsync(graphResourceUri, ApplicationID, new Uri(returnUri), new PlatformParameters((Activity)Forms.Context));
return authResult;
}
catch (Exception)
{
return null;
}
}
}
}
// err encountered on this line :(Activity)Forms.Context)
Forms.context is obsolete.Context is obsolete as of version2.5,please use a local context instead.
var authResult = await authContext.AcquireTokenAsync(graphResourceUri, ApplicationID, new Uri(returnUri), new PlatformParameters((Activity)Forms.Context));
//--- Update :
//-------- Login Page:
private async void Login()
{
try
{
var data = await DependencyService.Get<IAuthenticator>()
.Authenticate(AzureSettings.tenanturl, AzureSettings.GraphResourceUri, AzureSettings.ApplicationID, AzureSettings.ReturnUri);
AzureSettings.AuthenticationResult = data;
//NavigateTopage(data);
}
catch (Exception)
{ }
}
}
//--------- in Shared Project :
//-- interface: IAuthenticator
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
namespace myMobile.Service
{
public interface IAuthenticator
{
Task<AuthenticationResult> Authenticate(string tenantUrl, string graphResourceUri, string ApplicationID, string returnUri);
}
}
//-------- in Android Project: add
1) Class : Authenticator.cs
using Android.App;
using Android.Content;
using Xamarin.Forms;
using myMobile.Service;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
[assembly: Dependency(typeof(myMobile.Droid.Authenticator))]
namespace myMobile.Droid
{
class Authenticator: IAuthenticator
{
private readonly Context _context;
public static void Init(Context context)
{
_context = context; //--error
}
public async Task<AuthenticationResult> Authenticate(string tenantUrl, string graphResourceUri, string ApplicationID, string returnUri)
{
try
{
var authContext = new AuthenticationContext(tenantUrl);
if (authContext.TokenCache.ReadItems().Any())
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().FirstOrDefault().Authority);
var authResult = await authContext.AcquireTokenAsync(graphResourceUri, ApplicationID, new Uri(returnUri), new PlatformParameters((Activity) _context));
return authResult;
}
catch (Exception)
{
return null;
}
}
}
}
error :
An Object reference is required for non-static field,method or property.Authenticator._context
//------- Class: MainActivity
namespace myMobile.Droid
{
[Activity(Label = "myMobile", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
DependencyService.Get<IAuthenticator>().Init(this); //<-- Error
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
}
Error Message:
IUAthenticator does not contain a definition for Init and no extension method accepting
a first argument of type IAuthenticator
You now have to implement a custom constructor that takes a Context put that in a local variable and use that instead of this for instance new PlatformParameters((Activity)Forms.Context).
For a custom renderer, you can use the solution underneath. Do like this:
public MyControlRenderer : ControlRenderer
{
private readonly Context _context;
public MyControlRenderer(Context context) : base(context)
{
_context = context;
}
}
For a dependency service like yours, you will have to find a way to supply the Context. Since Xamarin.Forms uses a single activity, you can get away with some kind of init method.
Add this to your code:
public class MyService : IMyService
{
private static Context _context;
public static void Init(Context context)
{
_context = context;
}
}
Now call the Init from your MainActivity and you should be good after that. So do: DependencyService.Get<IMyService>().Init(this);
For other people coming across this for multiple activities, please refer to the documentation here: https://www.davidbritch.com/2017/11/xamarinforms-25-and-local-context-on.html that is what this is inspired on.
I've done this before with MVC5 using User.Identity.GetUserId() but that doesn't seem to work here.
The User.Identity doesn't have the GetUserId() method.
I am using Microsoft.AspNet.Identity.
Update in ASP.NET Core Version >= 2.0
In the Controller:
public class YourControllerNameController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public YourControllerNameController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> YourMethodName()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier) // will give the user's userId
var userName = User.FindFirstValue(ClaimTypes.Name) // will give the user's userName
// For ASP.NET Core <= 3.1
ApplicationUser applicationUser = await _userManager.GetUserAsync(User);
string userEmail = applicationUser?.Email; // will give the user's Email
// For ASP.NET Core >= 5.0
var userEmail = User.FindFirstValue(ClaimTypes.Email) // will give the user's Email
}
}
In some other class:
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
}
Then you should register IHttpContextAccessor in the Startup class as follows:
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Or you can also register as follows
services.AddHttpContextAccessor();
}
For more readability write extension methods as follows:
public static class ClaimsPrincipalExtensions
{
public static T GetLoggedInUserId<T>(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(T) == typeof(string))
{
return (T)Convert.ChangeType(loggedInUserId, typeof(T));
}
else if (typeof(T) == typeof(int) || typeof(T) == typeof(long))
{
return loggedInUserId != null ? (T)Convert.ChangeType(loggedInUserId, typeof(T)) : (T)Convert.ChangeType(0, typeof(T));
}
else
{
throw new Exception("Invalid type provided");
}
}
public static string GetLoggedInUserName(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Name);
}
public static string GetLoggedInUserEmail(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Email);
}
}
Then use as follows:
public class YourControllerNameController : Controller
{
public IActionResult YourMethodName()
{
var userId = User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
var userName = User.GetLoggedInUserName();
var userEmail = User.GetLoggedInUserEmail();
}
}
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
}
}
Until ASP.NET Core 1.0 RC1 :
It's User.GetUserId() from System.Security.Claims namespace.
Since ASP.NET Core 1.0 RC2 :
You now have to use UserManager.
You can create a method to get the current user :
private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
And get user information with the object :
var user = await GetCurrentUserAsync();
var userId = user?.Id;
string mail = user?.Email;
Note :
You can do it without using a method writing single lines like this string mail = (await _userManager.GetUserAsync(HttpContext.User))?.Email, but it doesn't respect the single responsibility principle. It's better to isolate the way you get the user because if someday you decide to change your user management system, like use another solution than Identity, it will get painful since you have to review your entire code.
you can get it in your controller:
using System.Security.Claims;
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or write an extension method like before .Core v1.0
using System;
using System.Security.Claims;
namespace Shared.Web.MvcExtensions
{
public static class ClaimsPrincipalExtensions
{
public static string GetUserId(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
}
}
and get wherever user ClaimsPrincipal is available :
using Microsoft.AspNetCore.Mvc;
using Shared.Web.MvcExtensions;
namespace Web.Site.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return Content(this.User.GetUserId());
}
}
}
I included using System.Security.Claims and I could access the GetUserId() extension method
NB: I had the using Microsoft.AspNet.Identity already but couldn't get the extension method. So I guess both of them have to be used in conjunction with one another
using Microsoft.AspNet.Identity;
using System.Security.Claims;
EDIT:
This answer is now outdated. Look at Soren's or Adrien's answer for a dated way of achieving this in CORE 1.0
For .NET Core 2.0 Only The following is required to fetch the UserID of the logged-in User in a Controller class:
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or
var userId = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
e.g.
contact.OwnerID = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
As stated somewhere in this post, the GetUserId() method has been moved to the UserManager.
private readonly UserManager<ApplicationUser> _userManager;
public YourController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public IActionResult MyAction()
{
var userId = _userManager.GetUserId(HttpContext.User);
var model = GetSomeModelByUserId(userId);
return View(model);
}
If you started an empty project you might need to add the UserManger to your services in startup.cs. Otherwise this should already be the case.
you have to import Microsoft.AspNetCore.Identity & System.Security.Claims
// to get current user ID
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
// to get current user info
var user = await _userManager.FindByIdAsync(userId);
For ASP.NET Core 2.0, Entity Framework Core 2.0, AspNetCore.Identity 2.0 api (https://github.com/kkagill/ContosoUniversity-Backend):
The Id was changed to User.Identity.Name
[Authorize, HttpGet("Profile")]
public async Task<IActionResult> GetProfile()
{
var user = await _userManager.FindByIdAsync(User.Identity.Name);
return Json(new
{
IsAuthenticated = User.Identity.IsAuthenticated,
Id = User.Identity.Name,
Name = $"{user.FirstName} {user.LastName}",
Type = User.Identity.AuthenticationType,
});
}
Response:
In .net core 3.1 (and other more recent versions), you can use:
private readonly UserManager<IdentityUser> _userManager;
public ExampleController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
Then:
string userId = _userManager.GetUserId(User);
Or async:
var user = await _userManager.GetUserAsync(User);
var userId = user.Id;
At this point, I'm trying to figure out why you'd use one over the other. I know the general benefits of async, but see both of these used frequently. Please post some comments if anyone knows.
For ASP.NET 5.0, I have an extension method as follow:
using System;
using System.ComponentModel;
using System.Security.Claims;
namespace YOUR_PROJECT.Presentation.WebUI.Extensions
{
public static class ClaimsPrincipalExtensions
{
public static TId GetId<TId>(this ClaimsPrincipal principal)
{
if (principal == null || principal.Identity == null ||
!principal.Identity.IsAuthenticated)
{
throw new ArgumentNullException(nameof(principal));
}
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(TId) == typeof(string) ||
typeof(TId) == typeof(int) ||
typeof(TId) == typeof(long) ||
typeof(TId) == typeof(Guid))
{
var converter = TypeDescriptor.GetConverter(typeof(TId));
return (TId)converter.ConvertFromInvariantString(loggedInUserId);
}
throw new InvalidOperationException("The user id type is invalid.");
}
public static Guid GetId(this ClaimsPrincipal principal)
{
return principal.GetId<Guid>();
}
}
}
So you can use it like:
using Microsoft.AspNetCore.Mvc;
using YOUR_PROJECT.Presentation.WebUI.Extensions;
namespace YOUR_PROJECT.Presentation.WebUI.Controllers
{
public class YourController :Controller
{
public IActionResult YourMethod()
{
// If it's Guid
var userId = User.GetId();
// Or
// var userId = User.GetId<int>();
return View();
}
}
}
in the APiController
User.FindFirst(ClaimTypes.NameIdentifier).Value
Something like this you will get the claims
Although Adrien's answer is correct, you can do this all in single line. No need for extra function or mess.
It works I checked it in ASP.NET Core 1.0
var user = await _userManager.GetUserAsync(HttpContext.User);
then you can get other properties of the variable like user.Email. I hope this helps someone.
For getting current user id in razor views, we can inject UserManager in the view like this:
#inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> _userManager
#{ string userId = _userManager.GetUserId(User); }
I hope you find it useful.
User.Identity.GetUserId();
does not exist in asp.net identity core 2.0. in this regard, i have managed in different way. i have created a common class for use whole application, because of getting user information.
create a common class PCommon & interface IPCommon
adding reference using System.Security.Claims
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Common.Web.Helper
{
public class PCommon: IPCommon
{
private readonly IHttpContextAccessor _context;
public PayraCommon(IHttpContextAccessor context)
{
_context = context;
}
public int GetUserId()
{
return Convert.ToInt16(_context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
}
public string GetUserName()
{
return _context.HttpContext.User.Identity.Name;
}
}
public interface IPCommon
{
int GetUserId();
string GetUserName();
}
}
Here the implementation of common class
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Pay.DataManager.Concreate;
using Pay.DataManager.Helper;
using Pay.DataManager.Models;
using Pay.Web.Helper;
using Pay.Web.Models.GeneralViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Pay.Controllers
{
[Authorize]
public class BankController : Controller
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger _logger;
private readonly IPCommon _iPCommon;
public BankController(IUnitOfWork unitOfWork, IPCommon IPCommon, ILogger logger = null)
{
_unitOfWork = unitOfWork;
_iPCommon = IPCommon;
if (logger != null) { _logger = logger; }
}
public ActionResult Create()
{
BankViewModel _bank = new BankViewModel();
CountryLoad(_bank);
return View();
}
[HttpPost, ActionName("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Insert(BankViewModel bankVM)
{
if (!ModelState.IsValid)
{
CountryLoad(bankVM);
//TempData["show-message"] = Notification.Show(CommonMessage.RequiredFieldError("bank"), "Warning", type: ToastType.Warning);
return View(bankVM);
}
try
{
bankVM.EntryBy = _iPCommon.GetUserId();
var userName = _iPCommon.GetUserName()();
//_unitOfWork.BankRepo.Add(ModelAdapter.ModelMap(new Bank(), bankVM));
//_unitOfWork.Save();
// TempData["show-message"] = Notification.Show(CommonMessage.SaveMessage(), "Success", type: ToastType.Success);
}
catch (Exception ex)
{
// TempData["show-message"] = Notification.Show(CommonMessage.SaveErrorMessage("bank"), "Error", type: ToastType.Error);
}
return RedirectToAction(nameof(Index));
}
}
}
get userId and name in insert action
_iPCommon.GetUserId();
Thanks,
Maksud
TLDR:
In the Controler add:
using System.Security.Claims;
and then you can use:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
endof TLDR;
Just an easy way in dot net 6 to test how to get the userID and test it in the default Blazor WebAssembly Core Hosted:
I added a String in WeatherForecast class named userId
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string userId { get; set; } = "nope";
}
Then in the WeatherForecastController
I add
using System.Security.Claims;
In the GET method I set WeatherForecast.userId to User.FindFirstValue(ClaimTypes.NameIdentifier):
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)],
userId = User.FindFirstValue(ClaimTypes.NameIdentifier)
})
.ToArray();
}
And finally in the FetchData.razor I modify the table to:
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
<th>User Id</th>
</tr>
</thead>
<tbody>
#foreach (var forecast in forecasts)
{
<tr>
<td>#forecast.Date.ToShortDateString()</td>
<td>#forecast.TemperatureC</td>
<td>#forecast.TemperatureF</td>
<td>#forecast.Summary</td>
<td>#forecast.userId</td>
</tr>
}
</tbody>
</table>
And then finally I get:
I hope it helps because in net core 6 sometimes it's quite difficult to find the answers
If you are using JWT tokens this code works:
User.FindFirstValue("sub");
use can use
string userid = User.FindFirst("id").Value;
for some reason NameIdentifier now retrieve the username (.net core 2.2)
Make sure that you have enable windows authentication. If you have anonymous authentication enabled you may be getting a null string.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio
I know there are many answers posted already, but maybe it will help someone as it did for me.
I mixed two solutions into one, and I am able to get the logged-in User and its Data.
I was using DotNet 5.
Following code, help to get the logged-in User.
var user = await _userManager.FindByNameAsync(HttpContext.User.Identity.Name);
I used the following package for _userManager
using Microsoft.AspNetCore.Identity;
And for HttpContext, I inherit my Controller from ControllerBase, and for ControllerBase Class I was using the following package
using Microsoft.AspNetCore.Mvc;
As an administrator working on other people's profile and you need to get the Id of the profile you are working on, you can use a ViewBag to capture the Id e.g ViewBag.UserId = userId; while userId is the string Parameter of the method you are working on.
[HttpGet]
public async Task<IActionResult> ManageUserRoles(string userId)
{
ViewBag.UserId = userId;
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"User with Id = {userId} cannot be found";
return View("NotFound");
}
var model = new List<UserRolesViewModel>();
foreach (var role in roleManager.Roles)
{
var userRolesViewModel = new UserRolesViewModel
{
RoleId = role.Id,
RoleName = role.Name
};
if (await userManager.IsInRoleAsync(user, role.Name))
{
userRolesViewModel.IsSelected = true;
}
else
{
userRolesViewModel.IsSelected = false;
}
model.Add(userRolesViewModel);
}
return View(model);
}
If you want this in ASP.NET MVC Controller, use
using Microsoft.AspNet.Identity;
User.Identity.GetUserId();
You need to add using statement because GetUserId() won't be there without it.
I'm using the latest version of WebApi and OData and everything is set up to work right. The only problem is when I try to use $select .
It throws the error bellow
Object of type 'System.Linq.EnumerableQuery`1[System.Web.OData.Query.Expressions.SelectExpandBinder+SelectAll`1[WebApplication1.Controllers.Person]]' cannot be converted to type 'System.Collections.Generic.IEnumerable`1[WebApplication1.Controllers.Person]'.
I looked at the documentation and their suggestion is to use [Queryable] on top of the Get method in the controller or the in WebApiConfig to use config.EnableQuerySupport and neither of these are available options. I'm currently using [EnableQuery]
EDIT
OdataController:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.OData;
using System.Xml.Serialization;
namespace WebApplication1.Controllers
{
public class PeopleController : ODataController
{
// GET api/values
[EnableQuery]
public IQueryable<Person> Get()
{
return new Person[] { new Person()
{
Id = 1,
FirstName = "Testing",
LastName = "2"
}, new Person()
{
Id = 2,
FirstName = "TestTest",
LastName = "3"
} }.AsQueryable();
}
// GET api/values/5
public Person Get(int id)
{
return new Person()
{
Id = 3,
FirstName = "Test",
LastName = "1"
};
}
// POST api/values
public void Post([FromBody]Person value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]Person value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
public class Person
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
WebApiConfig
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using System.Web.OData.Formatter;
using WebApplication1.Controllers;
namespace WebApplication1
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
var odataFormatters = ODataMediaTypeFormatters.Create();
config.Formatters.InsertRange(0, odataFormatters);
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Person>("People");
config.AddODataQueryFilter();
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "api",
model: builder.GetEdmModel());
}
}
}
UPDATE 2
seems to only throw an error retrieving the data in xml format. Json seems to work
This is a known limitation of the XmlMediaTypeFormatter class from the System.Net.Formatting Nuget package. The implementation of the JSON formatter does support the $select and $expand commands but these are not available when content negotiation determines that XML should be returned.
You should look into implementing OData endpoints (as opposed to WebAPI endpoints) should you need to return XML formatted responses. More information on how this can be done can be found here:
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options
Found a solution. It isn't perfect but it does work!
Maybe it will be useful for someone because I've spent on it few hours of research and trying.
Step #1 create custom xml formatter:
public class CustomXmlFormatter : MediaTypeFormatter
{
private JsonMediaTypeFormatter jFormatter = null;
public CustomXmlFormatter(JsonMediaTypeFormatter jFormatter)
{
SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml"));
this.jFormatter = jFormatter;
}
public override bool CanReadType(Type type)
{
return false;
}
public override bool CanWriteType(Type type)
{
return true;
}
public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
return Task.Factory.StartNew(() =>
{
using (MemoryStream ms = new MemoryStream())
{
var tsk = jFormatter.WriteToStreamAsync(type, value, ms, content, transportContext);
tsk.Wait();
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
var xDoc = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(ms, new XmlDictionaryReaderQuotas()));
using (XmlWriter xw = XmlWriter.Create(writeStream))
{
xDoc.WriteTo(xw);
}
}
});
}
}
Step #2 register it in startup section:
var formatters = ODataMediaTypeFormatters.Create();
var jsonFormatter = config.Formatters.JsonFormatter;
var customXmlFormatter = new CustomXmlFormatter(jsonFormatter);
customXmlFormatter.AddQueryStringMapping("$format", "cxml", "application/xml");
config.Formatters.Add(customXmlFormatter);
use it as
http://url..../actionName?$format=cxml&$select=ObjectName,ObjectId
I have a solution that i need to call a console app from asp.net and need to pass variables. one variable is a generic list of a certain class.
I have tried passing it but I got error that i cannot convert a generic list to a string which is correct.
I am not sure if there is another way to pass this.
I know webservice can solve this issue. But it there any other options?
Is this possible to do or only string are possible to pass
Here is the generic list sample.
List<person> personList = new List<person>();
person p = new person();
p.name = "test";
p.age = 12;
p.birthdate = 01/01/2014
personList.add(p)
Thanks.
Ok, Console application accepts only strings. This is defined in the Main method as
static void Main(string[] args)
Since you have a complex object list it'll be bit hard to pass this information to the console application (but not impossible). There are several options for you.
Pass your values as comma separated values as a string as long as this string is not too long.
Web Services or a Web API as you suggested.
Serialize your object to an XML file and then deserialize in your console application.
Write and read from a persistent data store
UPDATE
Sample Code for Option 3 (Write to an XML file)
I wrote this sample code out of curiosity. Hope this helps to solve your issue.
ASP.Net Website
I have a button in my web page (Default.aspx) and in it's click event it writes the Person collection/ List to an XML file. Here's the code behind.
using System;
using System.IO;
using System.Xml.Serialization;
namespace WriteToConsole
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnWriteToConsole_Click(object sender, EventArgs e)
{
PersonCollection personList = new PersonCollection();
// Person 1
Person p = new Person();
p.Name = "test 1";
p.Age = 12;
p.BirthDate = DateTime.Parse("01/01/2014");
personList.Add(p);
// Person 2
Person p2 = new Person();
p2.Name = "test 2";
p2.Age = 25;
p2.BirthDate = DateTime.Parse("01/01/2014");
personList.Add(p2);
XmlSerializer serializer = new XmlSerializer(personList.GetType());
StreamWriter file = new StreamWriter(#"D:\temp\PersonCollection.xml");
serializer.Serialize(file, personList);
file.Close();
}
}
}
And, the Person.cs looks like this.
using System;
using System.Collections.Generic;
namespace WriteToConsole
{
[Serializable]
[System.Xml.Serialization.XmlRoot("PersonCollection")]
public class PersonCollection : List<Person> {
}
[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime BirthDate { get; set; }
public Person()
{
this.Name = string.Empty;
this.Age = 0;
this.BirthDate = DateTime.MinValue;
}
}
}
Console Application
Then read the XML file in your console application and display the data in personList on the console.
using System;
using System.IO;
using System.Xml.Serialization;
namespace ReadInConsole
{
class Program
{
static void Main(string[] args)
{
XmlSerializer deserializer = new XmlSerializer(typeof(PersonCollection));
TextReader textReader = new StreamReader(#"D:\temp\PersonCollection.xml");
PersonCollection personList = new PersonCollection();
personList = (PersonCollection)deserializer.Deserialize(textReader);
textReader.Close();
if (personList != null && personList.Count > 0)
{
foreach (Person p in personList)
{
Console.WriteLine("Person name: {0}, Age: {1} and DOB: {2}", p.Name, p.Age, p.BirthDate.ToShortDateString());
}
Console.ReadLine();
}
}
}
}
In your console application you should have the same Person class as a modal (This is same as the Person class in your Web Application. Only the namespace is different).
using System;
using System.Collections.Generic;
namespace ReadInConsole
{
[Serializable]
[System.Xml.Serialization.XmlRoot("PersonCollection")]
public class PersonCollection : List<Person>
{
}
[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime BirthDate { get; set; }
public Person()
{
this.Name = string.Empty;
this.Age = 0;
this.BirthDate = DateTime.MinValue;
}
}
}
Hope you understand the code.