Skip to content

Commit

Permalink
Fix to #21402 - Test using OData
Browse files Browse the repository at this point in the history
Adding infrastructure and rudimentary tests using OData. Ported Northwind, ComplexNavigations and GearsOfWar models. Only setup for sqlserver currently.

Resolves #21402
  • Loading branch information
maumar committed Oct 28, 2020
1 parent 4f35d3f commit d9a4350
Show file tree
Hide file tree
Showing 26 changed files with 1,822 additions and 2 deletions.
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ updates:
- dependency-name: Castle.Core
- dependency-name: Humanizer.Core
- dependency-name: IdentityServer4.EntityFramework
- dependency-name: Microsoft.AspNetCore.OData
- dependency-name: Microsoft.Azure.Cosmos
- dependency-name: Microsoft.CSharp
- dependency-name: Microsoft.Data.SqlClient
Expand Down
7 changes: 7 additions & 0 deletions All.sln
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Sqlite.winsq
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Sqlite.sqlite3.Tests", "test\Microsoft.Data.Sqlite.Tests\Microsoft.Data.Sqlite.sqlite3.Tests.csproj", "{E0FF35C8-8038-4394-9C2A-AF34BE3CC61F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.OData.FunctionalTests", "test\EFCore.OData.FunctionalTests\EFCore.OData.FunctionalTests.csproj", "{7C0E5443-FE44-4436-8A7D-CE64D1F889BD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -288,6 +290,10 @@ Global
{E0FF35C8-8038-4394-9C2A-AF34BE3CC61F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0FF35C8-8038-4394-9C2A-AF34BE3CC61F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0FF35C8-8038-4394-9C2A-AF34BE3CC61F}.Release|Any CPU.Build.0 = Release|Any CPU
{7C0E5443-FE44-4436-8A7D-CE64D1F889BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C0E5443-FE44-4436-8A7D-CE64D1F889BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C0E5443-FE44-4436-8A7D-CE64D1F889BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C0E5443-FE44-4436-8A7D-CE64D1F889BD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -336,6 +342,7 @@ Global
{7B598E0C-B8E2-4F1F-B53C-ED84178E65BE} = {258D5057-81B9-40EC-A872-D21E27452749}
{B163761D-FB4A-4C80-BAB9-01905E1351EF} = {258D5057-81B9-40EC-A872-D21E27452749}
{E0FF35C8-8038-4394-9C2A-AF34BE3CC61F} = {258D5057-81B9-40EC-A872-D21E27452749}
{7C0E5443-FE44-4436-8A7D-CE64D1F889BD} = {258D5057-81B9-40EC-A872-D21E27452749}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {285A5EB4-BCF4-40EB-B9E1-DF6DBCB5E705}
Expand Down
1 change: 1 addition & 0 deletions EFCore.Relational.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"test\\EFCore.Design.Tests\\EFCore.Design.Tests.csproj",
"test\\EFCore.InMemory.FunctionalTests\\EFCore.InMemory.FunctionalTests.csproj",
"test\\EFCore.InMemory.Tests\\EFCore.InMemory.Tests.csproj",
"test\\EFCore.OData.FunctionalTests\\EFCore.OData.FunctionalTests.csproj",
"test\\EFCore.Proxies.Tests\\EFCore.Proxies.Tests.csproj",
"test\\EFCore.Relational.Specification.Tests\\EFCore.Relational.Specification.Tests.csproj",
"test\\EFCore.Relational.Tests\\EFCore.Relational.Tests.csproj",
Expand Down
3 changes: 2 additions & 1 deletion EFCore.Runtime.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"test\\EFCore.Design.Tests\\EFCore.Design.Tests.csproj",
"test\\EFCore.InMemory.FunctionalTests\\EFCore.InMemory.FunctionalTests.csproj",
"test\\EFCore.InMemory.Tests\\EFCore.InMemory.Tests.csproj",
"test\\EFCore.OData.FunctionalTests\\EFCore.OData.FunctionalTests.csproj",
"test\\EFCore.Proxies.Tests\\EFCore.Proxies.Tests.csproj",
"test\\EFCore.Relational.Specification.Tests\\EFCore.Relational.Specification.Tests.csproj",
"test\\EFCore.Relational.Tests\\EFCore.Relational.Tests.csproj",
Expand All @@ -31,7 +32,7 @@
"test\\EFCore.SqlServer.Tests\\EFCore.SqlServer.Tests.csproj",
"test\\EFCore.Sqlite.FunctionalTests\\EFCore.Sqlite.FunctionalTests.csproj",
"test\\EFCore.Sqlite.Tests\\EFCore.Sqlite.Tests.csproj",
"test\\EFCore.Tests\\EFCore.Tests.csproj"
"test\\EFCore.Tests\\EFCore.Tests.csproj",
]
}
}
1 change: 1 addition & 0 deletions EFCore.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"test\\EFCore.Design.Tests\\EFCore.Design.Tests.csproj",
"test\\EFCore.InMemory.FunctionalTests\\EFCore.InMemory.FunctionalTests.csproj",
"test\\EFCore.InMemory.Tests\\EFCore.InMemory.Tests.csproj",
"test\\EFCore.OData.FunctionalTests\\EFCore.OData.FunctionalTests.csproj",
"test\\EFCore.Proxies.Tests\\EFCore.Proxies.Tests.csproj",
"test\\EFCore.Relational.Specification.Tests\\EFCore.Relational.Specification.Tests.csproj",
"test\\EFCore.Relational.Tests\\EFCore.Relational.Tests.csproj",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<AssemblyName>Microsoft.EntityFrameworkCore.OData.FunctionalTests</AssemblyName>
<RootNamespace>Microsoft.EntityFrameworkCore</RootNamespace>
<IsPackable>true</IsPackable>
<SkipTests Condition="'$(OS)' != 'Windows_NT' AND '$(Test__SqlServer__DefaultConnection)' == ''">True</SkipTests>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.4.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EFCore.SqlServer.FunctionalTests\EFCore.SqlServer.FunctionalTests.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Microsoft.EntityFrameworkCore.Extensions
{
public static class HttpContentExtensions
{
public static async Task<T> ReadAsObject<T>(this HttpContent content)
{
var json = await content.ReadAsStringAsync();

return JsonConvert.DeserializeObject<T>(json);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.TestUtilities;

// Skip the entire assembly if not on Windows and no external SQL Server is configured
[assembly: SqlServerConfiguredCondition]
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.OData;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.TestModels.ComplexNavigationsModel;

namespace Microsoft.EntityFrameworkCore.Query
{
public class LevelOneController : TestODataController, IDisposable
{
private readonly ComplexNavigationsODataContext _context;

public LevelOneController(ComplexNavigationsODataContext context)
{
_context = context;
}

[HttpGet]
[EnableQuery]
public IEnumerable<Level1> Get()
{
return _context.LevelOne;
}

[HttpGet]
[EnableQuery]
public ITestActionResult Get([FromODataUri] int key)
{
var result = _context.LevelOne.FirstOrDefault(e => e.Id == key);

return result == null ? NotFound() : (ITestActionResult)Ok(result);
}

public void Dispose()
{
}
}

public class LevelTwoController : TestODataController, IDisposable
{
private readonly ComplexNavigationsODataContext _context;

public LevelTwoController(ComplexNavigationsODataContext context)
{
_context = context;
}

[HttpGet]
[EnableQuery]
public IEnumerable<Level2> Get()
{
return _context.LevelTwo;
}

[HttpGet]
[EnableQuery]
public ITestActionResult Get([FromODataUri] int key)
{
var result = _context.LevelTwo.FirstOrDefault(e => e.Id == key);

return result == null ? NotFound() : (ITestActionResult)Ok(result);
}

public void Dispose()
{
}
}

public class LevelThreeController : TestODataController, IDisposable
{
private readonly ComplexNavigationsODataContext _context;

public LevelThreeController(ComplexNavigationsODataContext context)
{
_context = context;
}

[HttpGet]
[EnableQuery]
public IEnumerable<Level3> Get()
{
return _context.LevelThree;
}

[HttpGet]
[EnableQuery]
public ITestActionResult Get([FromODataUri] int key)
{
var result = _context.LevelThree.FirstOrDefault(e => e.Id == key);

return result == null ? NotFound() : (ITestActionResult)Ok(result);
}

public void Dispose()
{
}
}

public class LevelFourController : TestODataController, IDisposable
{
private readonly ComplexNavigationsODataContext _context;

public LevelFourController(ComplexNavigationsODataContext context)
{
_context = context;
}

[HttpGet]
[EnableQuery]
public IEnumerable<Level4> Get()
{
return _context.LevelFour;
}

[HttpGet]
[EnableQuery]
public ITestActionResult Get([FromODataUri] int key)
{
var result = _context.LevelFour.FirstOrDefault(e => e.Id == key);

return result == null ? NotFound() : (ITestActionResult)Ok(result);
}

public void Dispose()
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.TestModels.ComplexNavigationsModel;
using Microsoft.EntityFrameworkCore.TestUtilities;

namespace Microsoft.EntityFrameworkCore.Query
{
public class ComplexNavigationsODataContext : PoolableDbContext
{
public ComplexNavigationsODataContext(DbContextOptions options)
: base(options)
{
}

public DbSet<Level1> LevelOne { get; set; }
public DbSet<Level2> LevelTwo { get; set; }
public DbSet<Level3> LevelThree { get; set; }
public DbSet<Level4> LevelFour { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Level1>().Property(e => e.Id).ValueGeneratedNever();
modelBuilder.Entity<Level2>().Property(e => e.Id).ValueGeneratedNever();
modelBuilder.Entity<Level3>().Property(e => e.Id).ValueGeneratedNever();
modelBuilder.Entity<Level4>().Property(e => e.Id).ValueGeneratedNever();

modelBuilder.Entity<Level1>().HasOne(e => e.OneToOne_Optional_Self1).WithOne();
modelBuilder.Entity<Level1>().HasOne(e => e.OneToOne_Required_PK1).WithOne(e => e.OneToOne_Required_PK_Inverse2)
.HasPrincipalKey<Level1>(e => e.Id).HasForeignKey<Level2>(e => e.Id).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level1>().HasOne(e => e.OneToOne_Optional_PK1).WithOne(e => e.OneToOne_Optional_PK_Inverse2)
.HasPrincipalKey<Level1>(e => e.Id).IsRequired(false);
modelBuilder.Entity<Level1>().HasOne(e => e.OneToOne_Required_FK1).WithOne(e => e.OneToOne_Required_FK_Inverse2)
.HasForeignKey<Level2>(e => e.Level1_Required_Id).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level1>().HasOne(e => e.OneToOne_Optional_FK1).WithOne(e => e.OneToOne_Optional_FK_Inverse2)
.HasForeignKey<Level2>(e => e.Level1_Optional_Id).IsRequired(false);
modelBuilder.Entity<Level1>().HasMany(e => e.OneToMany_Required1).WithOne(e => e.OneToMany_Required_Inverse2).IsRequired()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level1>().HasMany(e => e.OneToMany_Optional1).WithOne(e => e.OneToMany_Optional_Inverse2).IsRequired(false);
modelBuilder.Entity<Level1>().HasMany(e => e.OneToMany_Required_Self1).WithOne(e => e.OneToMany_Required_Self_Inverse1)
.IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level1>().HasMany(e => e.OneToMany_Optional_Self1).WithOne(e => e.OneToMany_Optional_Self_Inverse1)
.IsRequired(false);

modelBuilder.Entity<Level2>().HasOne(e => e.OneToOne_Optional_Self2).WithOne();
modelBuilder.Entity<Level2>().HasOne(e => e.OneToOne_Required_PK2).WithOne(e => e.OneToOne_Required_PK_Inverse3)
.HasPrincipalKey<Level2>(e => e.Id).HasForeignKey<Level3>(e => e.Id).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level2>().HasOne(e => e.OneToOne_Optional_PK2).WithOne(e => e.OneToOne_Optional_PK_Inverse3)
.HasPrincipalKey<Level2>(e => e.Id).IsRequired(false);
modelBuilder.Entity<Level2>().HasOne(e => e.OneToOne_Required_FK2).WithOne(e => e.OneToOne_Required_FK_Inverse3)
.HasForeignKey<Level3>(e => e.Level2_Required_Id).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level2>().HasOne(e => e.OneToOne_Optional_FK2).WithOne(e => e.OneToOne_Optional_FK_Inverse3)
.HasForeignKey<Level3>(e => e.Level2_Optional_Id).IsRequired(false);
modelBuilder.Entity<Level2>().HasMany(e => e.OneToMany_Required2).WithOne(e => e.OneToMany_Required_Inverse3).IsRequired()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level2>().HasMany(e => e.OneToMany_Optional2).WithOne(e => e.OneToMany_Optional_Inverse3).IsRequired(false);
modelBuilder.Entity<Level2>().HasMany(e => e.OneToMany_Required_Self2).WithOne(e => e.OneToMany_Required_Self_Inverse2)
.IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level2>().HasMany(e => e.OneToMany_Optional_Self2).WithOne(e => e.OneToMany_Optional_Self_Inverse2)
.IsRequired(false);

modelBuilder.Entity<Level3>().HasOne(e => e.OneToOne_Optional_Self3).WithOne();
modelBuilder.Entity<Level3>().HasOne(e => e.OneToOne_Required_PK3).WithOne(e => e.OneToOne_Required_PK_Inverse4)
.HasPrincipalKey<Level3>(e => e.Id).HasForeignKey<Level4>(e => e.Id).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level3>().HasOne(e => e.OneToOne_Optional_PK3).WithOne(e => e.OneToOne_Optional_PK_Inverse4)
.HasPrincipalKey<Level3>(e => e.Id).IsRequired(false);
modelBuilder.Entity<Level3>().HasOne(e => e.OneToOne_Required_FK3).WithOne(e => e.OneToOne_Required_FK_Inverse4)
.HasForeignKey<Level4>(e => e.Level3_Required_Id).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level3>().HasOne(e => e.OneToOne_Optional_FK3).WithOne(e => e.OneToOne_Optional_FK_Inverse4)
.HasForeignKey<Level4>(e => e.Level3_Optional_Id).IsRequired(false);
modelBuilder.Entity<Level3>().HasMany(e => e.OneToMany_Required3).WithOne(e => e.OneToMany_Required_Inverse4).IsRequired()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level3>().HasMany(e => e.OneToMany_Optional3).WithOne(e => e.OneToMany_Optional_Inverse4).IsRequired(false);
modelBuilder.Entity<Level3>().HasMany(e => e.OneToMany_Required_Self3).WithOne(e => e.OneToMany_Required_Self_Inverse3)
.IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level3>().HasMany(e => e.OneToMany_Optional_Self3).WithOne(e => e.OneToMany_Optional_Self_Inverse3)
.IsRequired(false);

modelBuilder.Entity<Level4>().HasOne(e => e.OneToOne_Optional_Self4).WithOne();
modelBuilder.Entity<Level4>().HasMany(e => e.OneToMany_Required_Self4).WithOne(e => e.OneToMany_Required_Self_Inverse4)
.IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Level4>().HasMany(e => e.OneToMany_Optional_Self4).WithOne(e => e.OneToMany_Optional_Self_Inverse4)
.IsRequired(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNet.OData.Builder;
using Microsoft.EntityFrameworkCore.TestModels.ComplexNavigationsModel;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OData.Edm;

namespace Microsoft.EntityFrameworkCore.Query
{
public class ComplexNavigationsODataQueryTestFixture : ComplexNavigationsQuerySqlServerFixture, IODataQueryTestFixture
{
private IHost _selfHostServer;

protected override string StoreName { get; } = "ODataComplexNavigations";

public ComplexNavigationsODataQueryTestFixture()
{
(BaseAddress, ClientFactory, _selfHostServer)
= ODataQueryTestFixtureInitializer.Initialize<ComplexNavigationsODataContext>(StoreName, GetEdmModel());
}

public void UpdateConfigureServices<TContext>(IServiceCollection services, string storeName)
where TContext : DbContext
{
services.AddDbContext<TContext>(b =>
b.UseSqlServer(
SqlServerTestStore.CreateConnectionString(storeName)));
}

private static IEdmModel GetEdmModel()
{
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Level1>("LevelOne");
modelBuilder.EntitySet<Level2>("LevelTwo");
modelBuilder.EntitySet<Level3>("LevelThree");
modelBuilder.EntitySet<Level4>("LevelFour");

return modelBuilder.GetEdmModel();
}

public string BaseAddress { get; private set; }

public IHttpClientFactory ClientFactory { get; private set; }

public override async Task DisposeAsync()
{
if (_selfHostServer != null)
{
await _selfHostServer.StopAsync();
_selfHostServer.Dispose();
_selfHostServer = null;
}
}
}
}
Loading

0 comments on commit d9a4350

Please sign in to comment.