【发布时间】:2019-03-28 17:54:07
【问题描述】:
我想用 F# 编写一段代码,但我的示例是用 C# 编写的。我需要一些帮助来用 F# 语言编写它,并帮助理解它是如何工作的。
这是我必须模仿的 c# 代码:
builder.HasMany(r => r.Options).WithOne(o => o.Root).HasForeignKey(o => o.RootId).OnDelete(DeleteBehavior.Cascade);
在 F# 中,我正在尝试这样做:
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun pr -> pr.CostItems)
.HasForeignKey(fun pr -> pr.ProductionReportId).OnDelete(DeleteBehavior.Cascade) |> ignore
对于每个 Visual Studio,问题在于 pr 的类型为 obj。根据builder.HasOne的返回类型,如何确保f#知道pr的类型是ProductionReport。
这是要求的完整示例:
BackendDemoDbContext
namespace BackendDemo.BackendDemoContext
open Microsoft.EntityFrameworkCore
type BackendDemoContext(options: DbContextOptions<BackendDemoContext>) =
inherit DbContext(options)
override __.OnModelCreating modelbuilder =
//Todo:
//modelbuilder.ApplyConfiguration(new CostItemEntityTypeConfiguration());
//modelbuilder.ApplyConfiguration(new ProductionReportEntityTypeConfiguration());
成本项目
namespace BackendDemo.Data.Models
type CostItem() =
member val CostItemId = null with get, set
member val Paper1 = null with get, set
member val Paper2 = null with get, set
member val Cases = null with get, set
member val Boxes = null with get, set
member val Paste = null with get, set
member val Bundling = null with get, set
member val Ink = null with get, set
member val Cardboard = null with get, set
member val Wrapping = null with get, set
member val Labour = null with get, set
member val Fringe = null with get, set
member val Pallet = null with get, set
member val ProductionReportId =null with get,set
member val ProductionReport = null with get, set
生产报告
namespace BackendDemo.Data.Models
open System.Collections
open BackendDemo.Data.Models
type ProductionReport() =
//val keyword necessary for AutoProperties
member val ProductionReportId : int = 2
//Todo:
//abstract member CostItems : ICollection<CostItem> with get, set
CostItemEntityTypeConfiguration
namespace BackendDemo.Data.EntityConfigurations
open Microsoft.EntityFrameworkCore
open Microsoft.EntityFrameworkCore.Metadata.Builders
open BackendDemo.Data.Models
type CostItemEntityTypeConfiguration =
interface IEntityTypeConfiguration<CostItem> with
override this.Configure(builder: EntityTypeBuilder<CostItem>) =
builder.ToTable("CostItem") |> ignore
builder.HasKey(fun i -> i.CostItemId) |> ignore
builder.Property(fun i -> i.Paper1).IsRequired() |> ignore
builder.Property(fun i -> i.Paper2).IsRequired() |> ignore
builder.Property(fun i -> i.Cases).IsRequired() |> ignore
builder.Property(fun i -> i.Boxes).IsRequired() |> ignore
builder.Property(fun i -> i.Paste).IsRequired() |> ignore
builder.Property(fun i -> i.Bundling).IsRequired() |> ignore
builder.Property(fun i -> i.Ink).IsRequired() |> ignore
builder.Property(fun i -> i.Cardboard).IsRequired() |> ignore
builder.Property(fun i -> i.Wrapping).IsRequired() |> ignore
builder.Property(fun i -> i.Labour).IsRequired() |> ignore
builder.Property(fun i -> i.Fringe).IsRequired() |> ignore
builder.Property(fun i -> i.Pallet).IsRequired() |> ignore
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun pr -> pr.CostItems)
.HasForeignKey(fun pr -> pr.ProductionReportId).OnDelete(DeleteBehavior.Cascade) |> ignore
ProductionReportEntityTypeConfiguration
namespace BackendDemo.Data.EntityConfigurations
open Microsoft.EntityFrameworkCore
open Microsoft.EntityFrameworkCore.Metadata.Builders
open BackendDemo.Data.Models
type ProductionReportEntityTypeConfiguration =
interface IEntityTypeConfiguration<ProductionReport> with
override this.Configure(builder: EntityTypeBuilder<ProductionReport>) =
builder.ToTable("ProductionReport") |> ignore
//Todo
///builder.HasKey(fun r -> r.ProductionReportId) |> ignore
以下是以下建议的结果(顺便谢谢!):
- 1 尝试强制使用参数类型
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun (pr: ProductionReport) -> pr.CostItems)
- 2 使用替代函数语法
builder
.HasOne(<@ fun i -> i.ProductionReport @>)
.WithMany(<@ fun pr -> pr.CostItems @>)
- 3 对特定类型使用
builder
.HasOne(<@ Func<ProductionReport,_> fun i -> i.ProductionReport @>)
.WithMany(<@ Func<CostItem,_> fun pr -> pr.CostItems @>)
- 4 分解来自 Nathan 的表达式解决方案
static member toExpr (f:'a -> 'b) =
<@ Func<_,_> (f) @>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
- 5 使用 Nathan 建议的类型符号分解表达式
static member toExpr<'a, 'b> (f:'a -> 'b) =
<@ Func<_,_> (f) @>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
【问题讨论】:
-
您能否提供一个更完整的示例,以便我们查看构建器的类型以及此处涉及的 DbContext / 实体?
-
您可以在 lambda 表达式内的值上添加类型注释:
.WithMany(fun (pr : ProductionReport) -> pr.CostItems)。我觉得有必要这样做有点奇怪,但我几乎没有使用 EF 的经验,所以谁知道.... -
我假设您正在使用
HasOne和WithMany的重载,它们将Expression作为输入。在 C# 中,both 表达式和 Func/Action 使用“胖箭头”(=>) 语法。在 F# 中,表达式使用不同的语法单独处理。我没有使用过这些 EF 方法,但您可以尝试 F# 表达式语法:.HasOne(<@ fun i -> i.ProductionReport @>).WithMany(<@ fun pr -> pr.CostItems @>)看看是否可行。 -
啊,好吧,我想我们可能会有所进展。它说
Expr<'b -> 'c>与Expression<Func<_, _>>不兼容。在许多情况下,F# 函数 ('b -> 'c) 会自动转换为 C# 函数 (Func<'b, 'c>),但并非总是如此。在这种情况下,您可能必须自己进行转换。我会尝试.HasOne(<@ Func<ProductionReport,_>(fun i -> i.ProductionReport) @>).WithMany(<@ Func<CostItem,_>(fun pr -> pr.CostItems) @>)不确定是不是这样,但这至少应该使函数类型兼容。 -
@abatishchev 在这种情况下可能是正确的,要么将 EF 内容保留在 C# 中并从 F# 项目中引用它,要么围绕此 EF 代码进行某种包装可能会很好,因为一些使用这些表达式的 F# 语法变得有点乏味......
标签: entity-framework .net-core f# c#-to-f#