我在 Dotnet core 2.2.0 和 OData 7.2.1 中实现了以下实体。
模块.cs:
using System.Collections.Generic;
namespace Models
{
public abstract class Module
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual ICollection<ModuleDefinition> ModuleDefinitions { get; set; }
}
}
发票.cs:
namespace Models
{
public class Invoice : Module
{
}
}
模块定义:
namespace Models
{
public class ModuleDefinition
{
public int ModuleId { get; set; }
public Module Module { get; set; }
public int DefinitionId { get; set; }
public Definition Definition { get; set; }
}
}
定义.cs
using System.Collections.Generic;
namespace Models
{
public class Definition
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ModuleDefinition> ModuleDefinitions { get; set; }
}
}
上下文.cs:
using Microsoft.EntityFrameworkCore;
namespace Models
{
public class Context : DbContext
{
public Context(DbContextOptions<Context> options) : base(options) { }
public DbSet<Module> Modules { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<Definition> Definitions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ModuleDefinition>()
.HasKey(md => new { md.ModuleId, md.DefinitionId });
modelBuilder.Entity<ModuleDefinition>()
.HasOne(md => md.Module)
.WithMany(m => m.ModuleDefinitions)
.HasForeignKey(md => md.ModuleId);
modelBuilder.Entity<ModuleDefinition>()
.HasOne(md => md.Definition)
.WithMany(d => d.ModuleDefinitions)
.HasForeignKey(md => md.DefinitionId);
}
}
}
启动.cs:
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Invoice>("Invoices");
builder.EntitySet<Module>("Modules");
builder.EntitySet<ModuleDefinition>("ModuleDefinitions").EntityType.HasKey(t => new { t.ModuleId, t.DefinitionId }); ;
builder.EnableLowerCamelCase();
app.UseOData(routeName: "ODataRoute", routePrefix: "odata", model: builder.GetEdmModel());
调用此网址https://localhost:5001/odata/modules?$expand=moduleDefinitions
没关系,我得到了这个回复:
{"@odata.context":"https://localhost:5001/odata/$metadata#Modules(moduleDefinitions())","value":[{"@odata.type":"#Models.Invoice","id":1,"title":"t","description":"d","moduleDefinitions":[{"moduleId":1,"definitionId":1}]}]}
但是,当尝试通过以下方式通过 OData 扩展发票实体的模块定义时:https://localhost:5001/odata/invoices?$expand=moduleDefinitions
我期待这样的事情:
{"@odata.context":"https://localhost:5001/odata/$metadata#Invoice(moduleDefinitions(definition()))","value":[{"@odata.type":"#Models.Invoice","id":1,"title":"t","description":"d","test":"","moduleDefinitions":[{"moduleId":1,"definitionId":1,"definition":{"id":1,"name":"n"}}]}]}
但我在 dotnet 中遇到了一个错误:
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [m].[Id], [m].[Description], [m].[Discriminator], [m].[Title], [m].[test]
FROM [Modules] AS [m]
WHERE [m].[Discriminator] = N'Invoice'
fail: Microsoft.EntityFrameworkCore.Query[10100]
An exception occurred while iterating over the results of a query for context type 'Models.Context'.
System.ArgumentNullException: Value cannot be null.
Parameter name: source
at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector)
at lambda_method(Closure , QueryContext , Invoice )
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.IShaper<TOut>.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities[TOut,TIn](IEnumerable`1 results, QueryContext queryContext, IList`1 entityTrackingInfos, IList`1 entityAccessors)+MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
System.ArgumentNullException: Value cannot be null.
Parameter name: source
at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector)
at lambda_method(Closure , QueryContext , Invoice )
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.IShaper<TOut>.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities[TOut,TIn](IEnumerable`1 results, QueryContext queryContext, IList`1 entityTrackingInfos, IList`1 entityAccessors)+MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
问题出在哪里?
我根据杰瑟的回答解决了这个问题
我刚刚编辑了启动.cs:
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Invoice>("Invoices").EntityType.DerivesFromNothing(); //Edited Code
builder.EntitySet<Module>("Modules");
builder.EntitySet<ModuleDefinition>("ModuleDefinitions").EntityType.HasKey(t => new { t.ModuleId, t.DefinitionId }); ;
builder.EnableLowerCamelCase();
app.UseOData(routeName: "ODataRoute", routePrefix: "odata", model: builder.GetEdmModel());