web api odata: $select not working - api

Trying to get this to work. The query returns all rows even though I am specifying only one (?$select=title). On the webapiconfig I have:
var queryAttribute = new QueryableAttribute()
{
AllowedQueryOptions = AllowedQueryOptions.All
};
config.EnableQuerySupport(queryAttribute);
The controller looks like this:
public override IQueryable<_person_TITLE> Get()
{
return db.personTitle.AsQueryable();
}
protected override _person_TITLE GetEntityByKey(int key)
{
return db.personTitle.FirstOrDefault(p => p.person_TITLE_ID == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
EDIT:
After doing some more digging, I found that $select has never been officially implemented. That is why it is not turned on in the AllowedQueryOptions by default.
So, what does that mean??? having this option is critical. Putting odata web api into production would be silly without this option. Coding in your own handlers would defeat the purpose of employing web api in the first place.

We are working on it right now. Support for $select and $expand should show up in the nightly builds very soon.

Related

Using nameof() with Url.Action() and async methods in ASP.NET Core 3.x MVC

Let's say I have a ASP.NET Core 3.0 MVC application, which features a simple controller containing two actions and using attribute based routing:
[Route("home")]
public class HomeController : Controller
{
public static string ControllerName { get; } = "Home";
public HomeController()
{
}
string GenerateUrls()
{
string url1 = Url.Action(nameof(Action1), ControllerName);
string url2 = Url.Action(nameof(Action2Async), ControllerName);
return $"Action1: '{url1}'\nAction2: '{url2}'";
}
[HttpGet("a1")]
public IActionResult Action1()
{
return Ok(GenerateUrls());
}
[HttpGet("a2")]
public async Task<IActionResult> Action2Async()
{
await Task.CompletedTask;
return Ok(GenerateUrls());
}
}
So calling either action should just yield a page showing URLs for both actions.
Opening /home/a1 and /home/a2 correctly calls the respective actions, but the output is kind of unexpected:
Action1: '/home/a1'
Action2: ''
This indicates that Url.Action() returned an empty string for the second action, while it worked perfectly fine for the first action.
After debugging this for quite a while, I found a blog post tracking down this very problem to a breaking change in ASP.NET Core 3.0, where the Async suffix is somehow ignored by Url.Action().
The author fixed this problem by hard-coding strings as action names ("Action1" und "Action2" in my case). He also uploaded some example code reproducing this behavior.
However, I would really prefer to keep the nameof, to avoid later problems with renaming/refactoring.
Is there a clean way to use nameof or other type-safe constructs to supply a method with Async suffix to the Url.Action function?
The described behavior is caused by a breaking change introduced with ASP.NET Core 3.0.
You can go back to the old behaviour by disabling SuppressAsyncSuffixInActionNames:
Gets or sets a value that determines if MVC will remove the suffix "Async" applied to controller action names.
Disable this switch in your AddControllers call while configuring the application services:
services.AddControllers(options => {
options.SuppressAsyncSuffixInActionNames = false;
});
You can find more information about this change in the official announcement and in the docs.
If you change your method name Action2Async to Action2 the problem will be solved.
From the linked blog post:
In ASP.NET Core 3, if you have Action methods suffixed with Async but a route path that does not include Async, refer to them without the Async suffix when resolving a URL through like Url.Action(). This seems to be a breaking change from ASP.NET Core 2.2 which is not officially documented.
public static string ControllerName { get; } = "Home";
string GenerateUrls()
{
string url1 = Url.Action(nameof(Action1), ControllerName);
string url2 = Url.Action(nameof(Action2), ControllerName);
return $"Action1: '{url1}'\nAction2: '{url2}'";
}
[HttpGet("action1")]
public IActionResult Action1()
{
return Ok(GenerateUrls());
}
[HttpGet("action2")]
public async Task<IActionResult> Action2()
{
await Task.CompletedTask;
return Ok(GenerateUrls());
}

No parameterless constructor defined for this object in asp.netcore migrations

I am new to ASP.NET Core. learning new version of .NET Core 2.0 using VS Code. I got stuck while doing creating database using migration. First, it gives an exception of implementation of IDesignTimeDbContextFactory. After solving this, it still gives an exception of
No parameterless constructor defined for this object
Here's my code for DbContextClass:
public VegaDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<VegaDbContext>();
var connectionString =
configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
return new VegaDbContext(builder.Options);
}
I had tried a couple of ways when I was experimenting with ef core. I faced similar issues too. Finally I found services working great. First you will need to create your DBContext with the following override constructor:
public VegaDbContext(DbContextOptions<VegaDbContext> options) : base(options)
{
}
In your start up, you can add your context as a service like this:
services.AddDbContext<ApplicationDBContext>(config => {
config.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
You can read in full detail about how dependency injection works here:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
This part should help you with the migration. You can perform your migrations using the dotnet ef commands https://learn.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet.
When using your db context, do ensure that you are using dependency injection so you make full use of the AddDbContext function and keep it DRY.
https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro
If I were your in your shoes, I look at this document.
Here is the simple DbContext that you can find on this webSite
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
I just got the same error. If you are careful the error description is actually giving you the solution of the problem.
  DesignTimeFactoryObject's constructor function should not take parameters.
public class ExampleDesignTimeFactory : IDesignTimeDbContextFactory<YourDBContext>{
public ExampleDesignTimeFactory(){ no constructor or no parameter constructor }
}
I use ASP.NET CORE 3.1 to create the project and it solved

Bootstrapping TestServer with TestStartup with InMemoryDatabase fails (.Net core)

Perhaps I've missed something, or perhaps something is broken. I hope to find out what happens here.
TLDR: Bootstrapping a TestServer class with an InMemory database, gives
(No service for type 'Microsoft.Data.Entity.Storage.IRelationalConnection' has been registered). Any clues? More details below:
I have a test class, which uses a TestFixture to bootstrap:
public AccountControllerTest(TestServerFixture testServerFixture) : base(testServerFixture)
{
}
The testServerFixture looks like this:
public class TestServerFixture : IDisposable
{
public TestServer server { get; }
public HttpClient client { get; }
public TestServerFixture()
{
// Arrange
var builder = TestServer.CreateBuilder()
.UseEnvironment("Development")
.UseStartup<TestPortalStartup>()
.UseServices(services =>
{
// Change the application environment to the mvc project
var env = new TestApplicationEnvironment();
env.ApplicationBasePath =
Path.GetFullPath(Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "..", "MY_APP"));
env.ApplicationName = "MY_APP";
//SUPER IMPORTANT: Should be the real application name, else you'll get Roslyn Compiler Errors in your views
services.AddInstance<IApplicationEnvironment>(env);
});
server = new TestServer(builder);
client = server.CreateClient();
}
public void Dispose()
{
server.Dispose();
client.Dispose();
}
}
And as you can see it uses a TestPortalStartup which looks like this:
public class TestPortalStartup : Startup
{
private Mock accountRegistrationClientMock;
public TestPortalStartup(IHostingEnvironment env, IApplicationEnvironment appEnv) : base(env, appEnv)
{
}
public override void SetUpDataBaseAndMigrations(IServiceCollection services)
{
services
.AddEntityFramework()
.AddInMemoryDatabase()
.AddDbContext<CmsDbContext> (
options => options.UseInMemoryDatabase()
);
}
public override void AddFrameworkDependencies(IServiceCollection services)
{
// ... not relevant
}
}
As you can see in the SetUpDataBaseAndMigrations we bootstrap an InMemoryDatabase and a DbContext.
I have used this construct before to test a Service that deals with the Database. (but this is isolated).
Now with an integration test I end up failing to bootstrap the test with:
Result StackTrace: at
Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider
provider, Type serviceType) at
Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider
provider) at
Microsoft.Data.Entity.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1
accessor) at
Microsoft.Data.Entity.RelationalDatabaseFacadeExtensions.GetRelationalConnection(DatabaseFacade
databaseFacade) at
Microsoft.Data.Entity.RelationalDatabaseFacadeExtensions.GetDbConnection(DatabaseFacade
databaseFacade) at
MY_APP.Portal.Startup.Configure(IApplicationBuilder app,
IHostingEnvironment env, ILoggerFactory loggerFactory) in
MY_APP/Startup.cs:line 175 Result Message: One or more errors
occurred. No service for type
'Microsoft.Data.Entity.Storage.IRelationalConnection' has been
registered. The following constructor parameters did not have matching
fixture data: TestServerFixture testServerFixture
In case you wonder what happens at MY_APP/Startup.cs (line 175) that is:
logger.LogInformation($"Using SQL Connection: {dbContext.Database.GetDbConnection().DataSource}");
Running with a 'normal' database (ie, not an In memory one) will PASS the test.
So it looks like some dependencies/wiring is missing? Anyone has experience with this? Clues? etc.
After I posted this question at the AspDotNet github, the answer was that the InMemoryDatabase from EntityFramework itself is not meant to do integration testing like this.
An alternative is using SQLite - but then also in memory mode.
Since creating all this (from scratch to fully working integration tests) took me quite some time to figure out. I figured I would summerise this all in a blog post:
http://www.stefanhendriks.com/2016/04/29/integration-testing-your-dot-net-core-app-with-an-in-memory-database/

looking for samples on how to user services.add* in asp.vnext

I would like to know where can I find samples the explains the differences among services.AddInstance, services.AddScoped, services.AddSingleton and service.AddTransient.
I found some articles that explain the point in a generic way, but I think a source sample is much more clear.
The scope of this questions is rather large, but since it seems you are specifically looking for AddScoped information I narrowed the sample down to scoping inside a web application.
Inside a web application AddScoped will mean pretty much the scope of the request. EntityFramework is using scoping internally, but it doesn't affect the user code in most cases so I'm sticking with the user code as shown below.
If you register a DbContext as a service, and also register a scoped service, for each request you will get a single instance of the scoped service where you resolve the DbContext.
The example code below should make it clearer. In general I would recommend just trying it out the way I'm showing it below to familiarize yourself with the behavior, by stepping through the code in the debugger. Start from an empty web application. Note the code I'm showing is from Beta2 (since in Beta2 we added the [FromServices] attribute which makes it easier to demonstrate, the underlying behavior is the same regardless of version.
startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add EF services to the services container.
services.AddEntityFramework(Configuration)
.AddSqlServer()
.AddDbContext<UserDbContext>();
services.AddScoped<UserService>();
// Add MVC services to the services container.
services.AddMvc();
}
UserDbContext.cs
public class UserDbContext : DbContext
{
public UserService UserService { get; }
public UserDbContext(UserService userService)
{
_userService = userService;
}
}
HomeController.cs
public class HomeController : Controller
{
private UserDbContext _dbContext;
public HomeController(UserDbContext dbContext)
{
_dbContext = dbContext;
}
public string Index([FromServices]UserDbContext dbContext, [FromServices]UserService userService)
{
// [FromServices] is available start with Beta2, and will resolve the service from DI
// dbContext == _ctrContext
// and of course dbContext.UserService == _ctrContext.UserService;
if (dbContext != _dbContext) throw new InvalidOperationException();
if (dbContext.UserService != _dbContext.UserService) throw new InvalidOperationException();
if (dbContext.UserService != userService) throw new InvalidOperationException();
return "Match";
}
}
Alternatively if you resolve the user service from another service, this time registered as transient the transient service will have a new instance everytime it is resolved, but the scoped service will remain the same within the scope of the request.
Create the new service
public class AnotherUserService
{
public UserService UserService { get; }
public AnotherUserService(UserService userService)
{
UserService = userService;
}
}
Add the following lines to startup.cs
services.AddTransient<AnotherUserService>();
And rewrite the HomeController.cs as follows
public class HomeController : Controller
{
private AnotherUserService _anotherUserService;
public HomeController(AnotherUserService anotherUserService)
{
_anotherUserService = anotherUserService;
}
public string Index([FromServices]AnotherUserService anotherUserService,
[FromServices]UserService userService)
{
// Since another user service is tranient we expect a new instance
if (anotherUserService == _anotherUserService)
throw new InvalidOperationException();
// but the scoped service should remain the same instance
if (anotherUserService.UserService != _anotherUserService.UserService)
throw new InvalidOperationException();
if (anotherUserService.UserService != userService)
throw new InvalidOperationException();
return "Match";
}
}

ASP.NET WebAPI with MVC - Unity Controller Injection Fails for HTTPPOST

I have a simple Login Controller that looks like this:
public class LoginController : Controller
{
private IAccountService _accountService = null;
private IFormsAuthenticationService _formsAuthenticationService = null;
public LoginController() {}
[InjectionConstructor]
public LoginController(IAccountService accountService, IFormsAuthenticationService formsAuthenticationService)
{
_accountService = accountService;
_formsAuthenticationService = formsAuthenticationService;
}
//
// GET: /Login/
public ActionResult Index()
{
return View();
}
//
// POST: /Login/
[HttpPost]
public ActionResult Index(Credentials credentials)
{
try
{
if (_accountService.ValidateUser(credentials) != null)
{
_formsAuthenticationService.SignIn(credentials.UserName, true);
return RedirectToAction("Index", "Home");
}
else
return RedirectToAction("Index", new { failed = true });
}
catch
{
throw;
}
}
}
Locally, everything works fine. When I put the application on my web server (shared hosting, ASP.NET 4.0) though, constructor injection works fine for the GET, but does not fire for the POST.
Essentially for POST, the default constructor is fired instead of the injection constructor. I'm not sure if Unity is even kicking in for the POST verb (how can I test this?).
I am using Boostrapper.cs and as I say, everything works fine on my development PC, so presumably there is an issue at the web server. I might be able to get some server settings changed. Any suggestions?
Cheers -
Bollocks - looks like the problem was due to me trying to load the wrong version of the EntityFramework DLL (v6 instead of 4.1). That wasn't coming up in the stack trace, but when I discovered this and replaced the DLL things started working OK again.