Skip to content

Commit

Permalink
Update the database by adding a new JSON Column
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhg committed Jan 24, 2023
1 parent 9755bb1 commit c688742
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 19 deletions.
17 changes: 16 additions & 1 deletion OData/OData.WebApi/Controllers/SchoolStudentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public SchoolStudentController(ApplicationDbContext context)

[HttpGet("students")]
[HttpGet("students/$count")]
[EnableQuery]
[EnableQuery(PageSize = 2)]
public IActionResult GetStudents()
{
return Ok(_context.Students);
Expand All @@ -36,4 +36,19 @@ public IActionResult GetStudent(int id)

return Ok(student);
}

[HttpPost("schools({schoolId})/students")]
public IActionResult Post(int schoolId, [FromBody] Student student)
{
/*
School school = _context.Schools.Include(c => c.Students)
.FirstOrDefault(s => s.SchoolId == schoolId);
*/
_context.Students.Add(student);
student.SchoolId = schoolId;

//school.Students.Add(student);
_context.SaveChanges();
return Created(student);
}
}
2 changes: 1 addition & 1 deletion OData/OData.WebApi/Extensions/DbExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static void MakeSureDbCreated(this WebApplication app)
var context = services.GetRequiredService<ApplicationDbContext>();

// uncomment the following to delete it
// context.Database.EnsureDeleted();
context.Database.EnsureDeleted();

context.Database.EnsureCreated();
}
Expand Down
101 changes: 101 additions & 0 deletions OData/OData.WebApi/Extensions/MethodInfoHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System.Reflection;
using System.Text.Json;

namespace OData.WebApi.Extensions
{
public static class MethodInfoHelpers
{
private static MethodInfo _JsonDeserializeMi;
private static MethodInfo _EnumerableSelectMi;
private static MethodInfo _EnumerableLongCountMi;

public static MethodInfo JsonDeserializeMi
{
get
{
if (_JsonDeserializeMi == null)
{
_JsonDeserializeMi = GetJsonSerializer();
}

return _JsonDeserializeMi;
}
}

public static MethodInfo EnumerableSelectMi
{
get
{
if (_EnumerableSelectMi == null)
{
_EnumerableSelectMi = GetEnumerableSelect();
}

return _EnumerableSelectMi;
}
}

public static MethodInfo EnumerableLongCountMi
{
get
{
if (_EnumerableLongCountMi == null)
{
_EnumerableLongCountMi = GetLongCountMethodInfo();
}

return _EnumerableLongCountMi;
}
}

private static MethodInfo GetEnumerableSelect()
{
// public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
var selects = typeof(Enumerable).GetMethods().Where(x => x.Name == "Select");
foreach (var select in selects)
{
ParameterInfo[] parameters = select.GetParameters();
if (parameters.Length != 2)
{
continue;
}

if (parameters[1].ParameterType.GetGenericArguments().Length == 2)
{
return select;
}
}

throw new NotSupportedException();
}

private static MethodInfo GetJsonSerializer()
{
// public static TValue? Deserialize<TValue>(string json, JsonSerializerOptions? options = null);
var deses = typeof(JsonSerializer).GetMethods().Where(x => x.Name == "Deserialize");

foreach (var deserializeMethodInfo in deses)
{
ParameterInfo[] parameters = deserializeMethodInfo.GetParameters();
if (parameters.Length != 2)
{
continue;
}

if (parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(JsonSerializerOptions))
{
return deserializeMethodInfo;
}
}

throw new NotSupportedException();
}

private static MethodInfo GetLongCountMethodInfo()
{
var longCountMethods = typeof(Enumerable).GetMethods().Where(x => x.Name == "LongCount");

return longCountMethods.First(x => x.GetParameters().Length == 1);
}
}
}
61 changes: 60 additions & 1 deletion OData/OData.WebApi/Extensions/SchoolStudentFilterBinder.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,73 @@
using Microsoft.AspNetCore.OData.Query.Expressions;
using Microsoft.OData.UriParser;
using OData.WebApi.Models;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;

namespace OData.WebApi.Extensions;

public class SchoolStudentFilterBinder : FilterBinder
{
/*
public override Expression BindCountNode(CountNode node, QueryBinderContext context)
{
// $filter=Branches/$count eq 1
if (node.Source is CollectionComplexNode collectionComplexNode &&
string.Equals(collectionComplexNode.Property.Name, "Branches", StringComparison.OrdinalIgnoreCase))
{
Expression source = context.CurrentParameter;
// $it.BranchAddresses
PropertyInfo emailsProperty = context.ElementClrType.GetProperty("BranchAddresses");
source = Expression.Property(source, emailsProperty);
source.Type.IsCollection(out Type elementType);
MethodInfo countMethod = GetLongCountMethodInfo(elementType);
Expression countExpression = Expression.Call(null, countMethod, new[] { source });
return countExpression;
}
return base.BindCountNode(node, context);
}
public override Expression BindCountNode(CountNode node, QueryBinderContext context)
{
// $filter=Branches/$count eq 1
if (node.Source is CollectionComplexNode collectionComplexNode &&
string.Equals(collectionComplexNode.Property.Name, "Branches", StringComparison.OrdinalIgnoreCase))
{
Expression source = context.CurrentParameter;
// $it.Branches
PropertyInfo branchesProperty = context.ElementClrType.GetProperty("Branches");
Expression branchesSource = Expression.Property(source, branchesProperty);
MethodInfo deserializeMethod = MethodInfoHelpers.JsonDeserializeMi.MakeGenericMethod(typeof(IList<Address>));
Expression jsonSerializerOptions = Expression.New(typeof(JsonSerializerOptions));
// JsonSerializer.Deserialize<IList<Address>>(it.Branches, new JsonSerializerOptions())
Expression deserializeCallExpression = Expression.Call(null, deserializeMethod, new[] { branchesSource, jsonSerializerOptions });
// MethodInfo enumerableSelectMethod = MethodInfoHelpers.EnumerableSelectMi.MakeGenericMethod(typeof(School), typeof(IList<Address>));
// Select($it, ($it) => JsonSerializer.Deserialize<IList<Address>>(it.Branches, new JsonSerializerOptions()))
//Expression selectCall = Expression.Call(null, enumerableSelectMethod, new[] { context.CurrentParameter, deserializeCallExpression });
MethodInfo countMethod = MethodInfoHelpers.EnumerableLongCountMi.MakeGenericMethod(typeof(Address));
Expression countExpression = Expression.Call(null, countMethod, new[] { deserializeCallExpression });
return countExpression;
}
return base.BindCountNode(node, context);
}
*/

public override Expression BindAnyNode(AnyNode anyNode, QueryBinderContext context)
{
// ?$filter=ContactEmails/any(a: a eq 'help@mercury.com')
Expand Down
35 changes: 35 additions & 0 deletions OData/OData.WebApi/Extensions/TypeHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace OData.WebApi.Extensions
{
public static class TypeHelpers
{
public static bool IsCollection(this Type clrType, out Type elementType)
{
elementType = clrType;
if (clrType == null)
{
return false;
}

// see if this type should be ignored.
if (clrType == typeof(string))
{
return false;
}

Type collectionInterface
= clrType.GetInterfaces()
.Union(new[] { clrType })
.FirstOrDefault(
t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>));

if (collectionInterface != null)
{
elementType = collectionInterface.GetGenericArguments().Single();
return true;
}

return false;
}
}
}
19 changes: 10 additions & 9 deletions OData/OData.WebApi/Models/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<School>().Ignore(s => s.ContactEmails);
modelBuilder.Entity<School>().Ignore(s => s.BranchAddresses);
modelBuilder.Entity<School>().HasKey(x => x.SchoolId);

modelBuilder.Entity<School>()
.HasData(
new { SchoolId = 1, SchoolName = "Mercury Middle School", Emails = "[\"info@mercury.com, help@mercury.com\"]" },
new { SchoolId = 2, SchoolName = "Venus High School", Emails = "[\"water@venus.com\"]" },
new { SchoolId = 3, SchoolName = "Earth Univerity", Emails = "[\"humen@earth.com,animals@earth.com,plants@earth.com\"]" },
new { SchoolId = 4, SchoolName = "Mars Elementary School ", Emails = "[\"weather@mars.com\"]" },
new { SchoolId = 5, SchoolName = "Jupiter College", Emails = "[\"news@jupiter.org\"]" },
new { SchoolId = 6, SchoolName = "Saturn Middle School", Emails = "[\"support@saturn.com\"]" },
new { SchoolId = 7, SchoolName = "Uranus High School", Emails = "[\"info@uranus.com\"]" },
new { SchoolId = 8, SchoolName = "Neptune Elementary School", Emails = "[\"tech@neptune.org, support@neptune.edu\"]" },
new { SchoolId = 9, SchoolName = "Pluto University", Emails = "[\"priciple@pluto.edu, support@pluto.com\"]" }
new { SchoolId = 1, SchoolName = "Mercury Middle School", Emails = "[\"info@mercury.com, help@mercury.com\"]", Branches = "[{\"Street\":\"1615 Anzac Avenue\",\"City\":\"Kallangur\",\"ZipCode\":\"4503\",\"AptNo\":27},{\"Street\":\"712 NE Avenue\",\"City\":\"LasVegs\",\"ZipCode\":\"9872\",\"AptNo\":127}]" },
new { SchoolId = 2, SchoolName = "Venus High School", Emails = "[\"water@venus.com\"]", Branches = "[{\"Street\":\"987 venus Avenue\",\"City\":\"Venus\",\"ZipCode\":\"9871\",\"AptNo\":7}]" },
new { SchoolId = 3, SchoolName = "Earth Univerity", Emails = "[\"humen@earth.com,animals@earth.com,plants@earth.com\"]", Branches = "[{\"Street\":\"156TH AVE NE\",\"City\":\"Redmond\",\"ZipCode\":\"98004\",\"AptNo\":23},{\"Street\":\"148TH NE Avenue\",\"City\":\"Bellevue\",\"ZipCode\":\"98006\",\"AptNo\":101}]" },
new { SchoolId = 4, SchoolName = "Mars Elementary School ", Emails = "[\"weather@mars.com\"]", Branches = "[{\"Street\":\"119TH Rd\",\"City\":\"Langur\",\"ZipCode\":\"4503\",\"AptNo\":27}]" },
new { SchoolId = 5, SchoolName = "Jupiter College", Emails = "[\"news@jupiter.org\"]", Branches = "[{\"Street\":\"87TH Avenue\",\"City\":\"Gur\",\"ZipCode\":\"4503\",\"AptNo\":27},{\"Street\":\"19TH NE Avenue\",\"City\":\"Legs\",\"ZipCode\":\"42\",\"AptNo\":13}]" },
new { SchoolId = 6, SchoolName = "Saturn Middle School", Emails = "[\"support@saturn.com\"]", Branches = "[{\"Street\":\"242 Avenue\",\"City\":\"Sammm\",\"ZipCode\":\"503\",\"AptNo\":27},{\"Street\":\"228 Avenue\",\"City\":\"Seatle\",\"ZipCode\":\"172\",\"AptNo\":17}]" },
new { SchoolId = 7, SchoolName = "Uranus High School", Emails = "[\"info@uranus.com\"]", Branches = "[{\"Street\":\"242 SE 37TH\",\"City\":\"Issaqur\",\"ZipCode\":\"029\",\"AptNo\":10}]" },
new { SchoolId = 8, SchoolName = "Neptune Elementary School", Emails = "[\"tech@neptune.org, support@neptune.edu\"]", Branches = "[{\"Street\":\"Avenue NE\",\"City\":\"Angur\",\"ZipCode\":\"503\",\"AptNo\":2},{\"Street\":\"11 NE SE\",\"City\":\"Lgs\",\"ZipCode\":\"902\",\"AptNo\":111}]" },
new { SchoolId = 9, SchoolName = "Pluto University", Emails = "[\"priciple@pluto.edu, support@pluto.com\"]", Branches = "[{\"Street\":\"Main ST\",\"City\":\"Kallr\",\"ZipCode\":\"112\",\"AptNo\":27},{\"Street\":\"Sub NE\",\"City\":\"Veglas\",\"ZipCode\":\"134\",\"AptNo\":32}]" }
);

// config the complex value for MailAddress of each School
Expand Down
9 changes: 6 additions & 3 deletions OData/OData.WebApi/Models/EdmModelBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ public static IEdmModel GetEdmModel()
buildler.EntitySet<School>("Schools");
buildler.EntitySet<Student>("Students");

buildler.EntityType<School>().Ignore(c => c.Emails);
buildler.EntityType<School>()
.CollectionProperty(c => c.ContactEmails);
var schoolConf = buildler.EntityType<School>();
schoolConf.Ignore(c => c.Emails);
schoolConf.CollectionProperty(c => c.ContactEmails);

schoolConf.Ignore(c => c.Branches);
schoolConf.CollectionProperty(c => c.BranchAddresses).Name = "Branches"; // Change the name same as the name in DB

return buildler.GetEdmModel();
}
Expand Down
15 changes: 15 additions & 0 deletions OData/OData.WebApi/Models/School.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,20 @@ public IList<string> ContactEmails
}
}

// It's not for Edm model, let's ignore when Edm model building
public string Branches { get; set; }

public IList<Address> BranchAddresses
{
get
{
return Branches is null ? new List<Address>() : JsonSerializer.Deserialize<IList<Address>>(Branches);
}
set
{
Branches = value is null ? string.Empty : JsonSerializer.Serialize(value);
}
}

public IList<Student> Students { get; set; }
}
8 changes: 4 additions & 4 deletions OData/OData.WebApi/OData.WebApi.csproj
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="8.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>

Expand Down
Binary file modified OData/OData.WebApi/app.db
Binary file not shown.
Binary file removed OData/OData.WebApi/app.db-shm
Binary file not shown.
Empty file removed OData/OData.WebApi/app.db-wal
Empty file.

0 comments on commit c688742

Please sign in to comment.