I am trying to follow along this tutorial to create an OData service. I am looking at this topic about navigation properties:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/entity-relations-in-odata-v4
It appears some of this code is obsolete (the article is from 2014, but I'm using Visual Studio 2017).
I have quite a few red underlines on my Helper class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Routing;
using System.Web.OData.Extensions;
using System.Web.OData.Routing;
using Microsoft.OData;
using Microsoft.OData.UriParser;
namespace ProductService
{
public static class Helpers
{
public static TKey GetFromUri<TKey>(HttpRequestMessage request, Uri uri)
{
if(uri == null)
throw new ArgumentException("uri");
var urlHelper = request.GetUrlHelper() ?? new UrlHelper(request);
string serviceRoot = urlHelper.CreateODataLink(
request.ODataProperties().RouteName,
request.ODataProperties().PathHandler, new List<ODataPathSegment>());
var odataPath = request.ODataProperties().PathHandler.Parse(
request.ODataProperties().Model,
serviceRoot, uri.LocalPath);
var keySegment = odataPath.Segments.OfType<KeyValuePathSegment>()
.FirstOrDefault();
if (keySegment == null)
throw new InvalidOperationException("The link does not contain a key.");
var value = ODataUriUtils.ConvertFromUriLiteral(keySegment.Value,
ODataVersion.V4);
return (TKey)value;
}
}
}
I have problems with three pieces of code on this class:
request.ODataProperties().PathHandler
and
request.ODataProperties().Model
I get errors:
'HttpRequestMessageProperties' does not contain a definition for 'PathHandler' and no extension method...
It is also unable to find the KeyValuePathSegment class.
Is there a way to rewrite this class to keep it current?
#Pizzor2000
Some breaking changes introduced in Web API OData library from 5.x to 6.x version. All the changes you can find from release note at :https://github.com/OData/WebApi/releases/tag/v6.0.0
for your examples:
you can call extension methods to get the original properties, for example:
https://github.com/OData/WebApi/blob/master/src/System.Web.OData/Extensions/HttpRequestMessageExtensions.cs#L307 to get the IEdmModel.
https://github.com/OData/WebApi/blob/master/src/System.Web.OData/Extensions/HttpRequestMessageExtensions.cs#L352 to get the PathHandler.
Besides, KeyValuePathSegment is removed, Web API OData uses the https://github.com/OData/odata.net/blob/master/src/Microsoft.OData.Core/UriParser/SemanticAst/KeySegment.cs#L22 instead.
Hope it can help you.
Hoping that it might save some time I post here the same piece of code corrected with the new API.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.OData;
using Microsoft.OData.UriParser;
namespace ProductService
{
public static class Helpers
{
public static TKey GetKeyFromUri<TKey>(HttpRequest request, Uri uri)
{
if (uri == null)
{
throw new ArgumentNullException("uri");
}
var oDataPathHandler = request.GetPathHandler();
var oDataLink = request.GetUrlHelper().CreateODataLink(request.ODataFeature().RouteName, oDataPathHandler, new List<ODataPathSegment>());
var odataPath = oDataPathHandler.Parse(oDataLink, uri.AbsoluteUri, request.GetRequestContainer());
var keySegment = odataPath.Segments.OfType<KeySegment>().FirstOrDefault();
if (keySegment?.Keys == null || !keySegment.Keys.Any())
{
throw new InvalidOperationException("The link does not contain a key.");
}
return (TKey)keySegment.Keys.FirstOrDefault().Value;
}
}
}
Related
I am new to C# MVC. I am trying to create a class where can I can put all the reusable functions and I want to have the ability to call those functions from my controllers and models. Is it possible to do so?
Here is my project folder structure:
So I have created a Helpers folder and inside it I have a commonFunctions class.
For now I have one function inside it.
namespace myProject.Helpers
{
public class CommonFunctions
{
public static string GenerateSHA(string sString)
{
string result = "";
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] password = sha.ComputeHash(Encoding.Default.GetBytes(sString));
result = Encoding.Default.GetString(password);
return result;
}
}
}
I want to call that function from my model. How can I do so?
Thanks in advance.
You can easily call that function like this:
var sha = Helpers.CommonFunctions.GenerateSHA("Your String");
Or you can add using statement and call the function without namespace specification every time:
using myProject.Helpers;
// And somewhere in your classes
var sha = CommonFunctions.GenerateSHA("Your String");
It is a direct call like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace myProject.Models
{
public class YourViewModel
{
public YourViewModel()
{
//This is the constructor of the class
//Call the function you need
var tVar = Helpers.CommonFunctions.GenerateSHA("String to process");
}
}
}
I have a project in VB.NET which is using asp.net membership to manage user authentication. Now I want to build android app for this project so I decided to learn WCF and I have got average hold on WCF webservices. Now the issue I am facing is that when the user login into the android app following things happen:
Request goes to the webapplication and user credentials are authenticated.
After that when the user tries submits any data or try to view data , request again goes to the web application but now the web application should authenticate the user based on the credentials he has provided in the first request for login from the membership authentication.
Now the issue I am facing how to authenticate user in asp.net membership for each WCF Request in Per-Session Service call mode from java(Android).
There's several ways to do what I think you're asking for, I've thought of (and written) a few different potential solutions, however, the one I'm sharing here is something that I think will "slot-in" to existing solutions using ASP.NET Membership/Roles Provider. Hopefully I've given you enough information to do what you need to do, but you can always comment and ask more questions if anything's still unclear.
In your problem you describe using an ASP.NET Web Application containing a WCF Service for existing clients, but you're looking to expand to using Android (java) requests? Given that the ASP.NET Membership provider uses alot of "behind the scenes" SOAP interchanges (for authentication, authorization and encryption) that seem to be built into the service reference frameworks it'd be a fairly big task to write a java implementation...
So, I've written you an example of something that will integrate to the same "backend" provider, but will also allow you to send SOAP requests from any client without needing the service reference (I tested it using SoapUI for example)... I've written my solution in C# (as it's what the WCF samples were written in), however, you can quite easily use a code-converter to switch it to VB.NET. I also haven't provided you with the method to encrypt and decrypt passwords, you'll have to research that bit yourself.
You'll need to implement a new .svc file into your existing solution and create new web.config entries accordingly (I assume you know how to create a basicHttpBinding and service endpoint already).
You'll also need to duplicate your method calls (or instead, create a new class with the method content and reference it from wherever you're implementing the ServiceContract methods) and remove the "[PrincipalPermission(SecurityAction" attributes, and add the example methods below into the new service.
e.g. (using methods from Microsoft's MembershipAndRoleProvider WCF Sample) -
// Allows all Users to call the Add method
[PrincipalPermission(SecurityAction.Demand, Role = "Users")]
public double Add(double n1, double n2)
{
double result = n1 + n2;
return result;
}
Would become:
// Allows all Users to call the Add method
public double Add(double n1, double n2, string username, string token)
{
string isAuthorized = IsAuthorized(username, "Users", token)
if (isAuthorized.Contains("Success")
double result = n1 + n2;
return result;
else
throw new Exception("Authorization Exception: " + isAuthorized);
}
Here's my implementation(s), integrated into the Microsoft WCF Sample MembershipAndRoleProvider (download from here):
IsolatedAuthService.svc
<%#ServiceHost Language="C#" Debug="true" Service="Microsoft.ServiceModel.Samples.IsolatedAuthService" CodeBehind="IsolatedAuthService.cs" %>
IIsolatedAuthService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IIsolatedAuthService
{
[OperationContract]
string IsAuthorized(string username, string roleName, string token);
[OperationContract]
string AuthenticateUser(string username, string encryptedPassword);
}
}
IsolatedAuthService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using System.Web.Configuration;
using System.Configuration;
using System.IO;
using System.Security.Permissions;
using System.Security.Principal;
using System.ServiceModel.Activation;
using System.Threading;
namespace Microsoft.ServiceModel.Samples
{
public class IsolatedAuthService : IIsolatedAuthService
{
public string IsAuthorized(string username, string roleName, string token)
{
MembershipUser user = Membership.GetAllUsers()[username];
Configuration config = ConfigurationManager.OpenExeConfiguration(HostingEnvironment.MapPath("~") + "\\web.config");
SessionStateSection sessionStateConfig = (SessionStateSection)config.SectionGroups.Get("system.web").Sections.Get("sessionState");
InMemoryInstances instance = InMemoryInstances.Instance;
// Check for session state timeout (could use a constant here instead if you don't want to rely on the config).
if (user.LastLoginDate.AddMinutes(sessionStateConfig.Timeout.TotalMinutes) < DateTime.Now)
{
// Remove token from the singleton in this instance, effectively a logout.
instance.removeTokenUserPair(username);
return "User Unauthorized - login has expired!";
}
if (!instance.checkTokenUserPair(username, token))
return "User Unauthorized - not a valid token!";
// Check for role membership.
if (!Roles.GetUsersInRole(roleName).Contains(user.UserName))
return "User Unauthorized - Does not belong in that role!";
return "Success - User is Authorized!";
}
public string AuthenticateUser(string username, string encryptedPassword)
{
if (Membership.ValidateUser(username, Decrypt(encryptedPassword)))
{
// Not sure if this is actually needed, but reading some documentation I think it's a safe bet to do here anyway.
Membership.GetAllUsers()[username].LastLoginDate = DateTime.Now;
// Send back a token!
Guid token = Guid.NewGuid();
// Store a token for this username.
InMemoryInstances instance = InMemoryInstances.Instance;
instance.removeTokenUserPair(username); //Because we don't implement a "Logout" method.
instance.addTokenUserPair(username, token.ToString());
return token.ToString();
}
return "Error - User was not able to be validated!";
}
}
}
InMemoryInstances.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.ServiceModel.Samples
{
public class InMemoryInstances
{
private static volatile InMemoryInstances instance;
private static object syncRoot = new Object();
private Dictionary<string, string> usersAndTokens = null;
private InMemoryInstances()
{
usersAndTokens = new Dictionary<string, string>();
}
public static InMemoryInstances Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new InMemoryInstances();
}
}
return instance;
}
}
public void addTokenUserPair(string username, string token)
{
usersAndTokens.Add(username, token);
}
public bool checkTokenUserPair(string username, string token)
{
if (usersAndTokens.ContainsKey(username)) {
string value = usersAndTokens[username];
if (value.Equals(token))
return true;
}
return false;
}
public void removeTokenUserPair(string username)
{
usersAndTokens.Remove(username);
}
}
}
Bare in mind, this solution will not work if you're load-balancing your WCF service across multiple servers (due to the in-memory instance class), you could change the solution to use a database table instead of the in-memory instances if this is a requirement for you.
I'm building an OData 3 service on Web API 2.2.
The service is correctly returning the metadata for my entities, but returns 406 Not Available when I query one of the actual entities. I've done quite a bit of research (I'm currently following several tutorials), but I haven't found anything that actually works.
Here's my WebApiConfig:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
namespace MyProject
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MarvelCharacter>("MarvelCharacters");
config.MapODataServiceRoute(
routeName: "Marvel",
routePrefix: "dude",
model: builder.GetEdmModel());
}
}
}
And my controller (not complete, but you get the idea):
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.OData;
using System.Web.Http.OData.Query;
using Microsoft.Data.OData;
using MyProject;
namespace MyProject.Controllers
{
public class MarvelCharactersController : ODataController
{
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
// GET: odata/MarvelCharacters
public IHttpActionResult GetMarvelCharacters(ODataQueryOptions<MarvelCharacter> queryOptions)
{
// validate the query.
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
return BadRequest(ex.Message);
}
var entities = new myEntities();
var marvelCharacters = (from c in entities.MarvelCharacters select c).ToList();
return Ok<IEnumerable<MarvelCharacter>>(marvelCharacters);
}
}
}
Turns out the answer to this one was pretty simple, but not covered well by any documentation I could find.
I was trying to implement an OData 3 endpoint on WebAPI 2.2. I was following several different tutorials, some for OData 3 and some for OData 4.
I was using OData 4 (System.Web.OData) in my WebApiConfig and OData 3 (System.Web.Http.OData) in my controller. Turns out, they don't play well together.
I decided to post the answer here in case anyone else has a similar issue.
To add a little value, and since I was mixing both anyway, I decided to go ahead and setup support for both version 3 and 4 by aliasing the namespaces in my WebApiConfig and then creating versioned controllers.
WebApiConfig:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using ODataV3 = System.Web.Http.OData;
using ODataV4 = System.Web.OData;
namespace MyProject
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// OData V3 Route
ODataV3.Builder.ODataModelBuilder builder3 = new ODataV3.Builder.ODataConventionModelBuilder();
builder3.EntitySet<MarvelCharacter>("MarvelCharactersV3");
// The MapODataRoute function is deprecated in WebAPI 2.2,
// but I haven't found an alternative for supporting OData 3.
config.Routes.MapODataRoute(
routeName: "Marvel3",
routePrefix: "dude3",
model: builder3.GetEdmModel());
// ODate V4 Route
ODataV4.Builder.ODataModelBuilder builder4 = new ODataV4.Builder.ODataConventionModelBuilder();
builder4.EntitySet<MarvelCharacter>("MarvelCharactersV4");
ODataV4.Extensions.HttpConfigurationExtensions.MapODataServiceRoute(
configuration: config,
routeName: "Marvel4",
routePrefix: "dude4",
model: builder4.GetEdmModel());
}
}
}
MarvelCharactersV3 (OData 3):
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.OData; // OData V3
using System.Web.Http.OData.Query;
using Microsoft.Data.OData;
using MyProject;
namespace MyProject.Controllers
{
public class MarvelCharactersV3Controller : ODataController
{
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
// GET: odata/MarvelCharacters
public IHttpActionResult GetMarvelCharactersV3(ODataQueryOptions<MarvelCharacter> queryOptions)
{
// validate the query.
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
return BadRequest(ex.Message);
}
var entities = new myEntities();
var marvelCharacters = (from c in entities.MarvelCharacters select c).ToList();
return Ok<IEnumerable<MarvelCharacter>>(marvelCharacters);
}
}
}
MarvelCharactersV4 (OData 4):
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.OData; // OData 4
using System.Web.OData.Query;
using Microsoft.Data.OData;
using MyProject;
namespace MyProject.Controllers
{
public class MarvelCharactersV4Controller : ODataController
{
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
// GET: odata/MarvelCharacters
public IHttpActionResult GetMarvelCharactersV4(ODataQueryOptions<MarvelCharacter> queryOptions)
{
// validate the query.
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
return BadRequest(ex.Message);
}
var entities = new myEntities();
var marvelCharacters = (from c in entities.MarvelCharacters select c).ToList();
return Ok<IEnumerable<MarvelCharacter>>(marvelCharacters);
}
}
}
It's probably not the best architecture (I will probably create a library to consolidate similar code between the controllers), but I've tested and I can successfully query via OData 3 and OData 4, so I'm happy enough with it for now.
With the below code I get thee following error:
NinjaSteps.cs(16,13): error CS0103: The name 'ninja' does not exist in the current context
The command line I use to compile is:
csc /target:library /reference:C:\Ruby193\lib\ruby\gems\1.9.1\gems\
cuke4nuke-0.4.0\dotnet\Cuke4Nuke.Framework.dll /reference:C:\Fitnesse\FitNesseRo
ot\jediwhale-fitsharp-a78d820\binary\tools\nunit\framework\nunit.framework.dll /
reference:C:\Users\Rahul\Documents\Visual~1\Projects\ConsoleApplication3\Console
Application3\Ninja.dll NinjaSteps.cs
The code I am trying to compile is from a tutorial on Cucumber automation technology:
NinjaSteps.cs:
http://cuke4ninja.com/sec_ninja_survival_net.html
using System;
using System.Collections.Generic;
using System.Text;
using Cuke4Nuke.Framework;
using NUnit.Framework;
using NinjaSurvivalRate;
namespace ConsoleApplication3
{
class NinjaSteps
{ [Given(#"^the ninja has a ([a-z]*) level black-belt$")]
public void TheNinjaHasABlackBelt(String level)
{ ninja = new Ninja(level);
}
[When(#"^attacked by [a\s]*(.*)$")]
public void AttackedBy(String opponent)
{
actions = ninja.AttackedBy(opponent);
}
[Then("^the ninja should (.*)$")]
public void TheNinjaShould(String action)
{
Assert.IsTrue(actions.Contains(action));
}
}
}
Ninja.cs is below, compiled to Ninja.dll:
using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
namespace NinjaSurvivalRate
{
public class Ninja
{
public Ninja(String beltLevel)
{
}
public List<String> AttackedBy(String opponent)
{
if ("Chuck Norris" == opponent)
return new List<string>(
new String[] { "run for his life" });
else
return new List<string>(
new String[] { "engage the opponent" });
}
}
}
Answers and feedback will be appreciated. Going through similar threads, I found that the resolution depended on a case by case basis and their was no consistent root cause, and felt I had to detail exact code details to get an understanding of the cause. You time and help will be greatly appreciated. Thanks.
You haven't defined the variable ninja. You need:
var ninja = new Ninja(level);
Do the same for actions.
EDIT:
Actually both the variables are supposed to be fields/properties in the class itself, if I understand your intentions correctly.
The tutorial is not telling you the whole history. If you go to the source code you will see that there is actually a field ninja declared that is initialized in the method TheNinjaHasABlackBelt (that you already have).
I just switched from Linq to entities framework, and I'm having problems with methods that returns "all rows". I get: "The type 'System.Data.Objects.DataClasses.EntityObject' is defined in an assembly that is not referenced" error in my "service layer" that calls the data-layer.
I get an error on:
BookingObjectRepository _repository = new BookingObjectRepository();
public IQueryable<BookingObject> GetBookingObjects()
{
return _repository.GetBookingObjects();
}
and in the "data-layer" I have:
BookingsystemEntities _entities = new BookingsystemEntities();
public IQueryable<BookingObject> GetBookingObjects()
{
return from bo in _entities.BookingObjectSet
select bo;
}
UPDATE: Filter items, they are "physically" in Filters-folder but namespace is same as the emdx file uses.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BookingSystem.Data.Models
{
public static class BookingObjectFilters
{public static IQueryable<BookingObject> ByBookingObjectID(this IQueryable<BookingObject> qry, int bookingobjectID)
{
return from bo in qry
where bo.BookingObjectID == bookingobjectID
select bo;
}
Your system must have .NET 3.5 SP 1 or better installed, and your project must reference the System.Data.Entity assembly (look at the references node in Solution Explorer).
Do you have
using System.Data;
using System.Data.Objects.DataClasses;
in your usings?
and
public IQueryable GetBookingObjects() { return _repository.GetBookingObjects(); }
should probably be
public IQueryable<BookingObject> GetBookingObjects() { return _repository.GetBookingObjects(); }
Hope that helps,
Dan