entity-framework - Entity Framework 相关代码从c#改写到f#的难度

标签 entity-framework .net-core f# c#-to-f#

我有一段代码想用 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

生产报表实体类型配置
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)
    

    Result
  • 2 使用替代函数语法
  • builder
        .HasOne(<@ fun i -> i.ProductionReport @>) 
        .WithMany(<@ fun pr -> pr.CostItems @>)
    

    Result
  • 3 使用具有特定类型的 <@ 符号
  • builder
        .HasOne(<@ Func<ProductionReport,_> fun i -> i.ProductionReport @>) 
        .WithMany(<@ Func<CostItem,_> fun pr -> pr.CostItems @>)
    

    Result
  • 4 分解 Nathan 的表达式解
  • static member toExpr (f:'a -> 'b) = 
        <@ Func<_,_> (f) @> 
        |> LeafExpressionConverter.QuotationToExpression 
        |> unbox<Expression<Func<'a, 'b>>>
    

    Factorization class

    Result
  • 5 使用 Nathan 建议的类型符号分解表达式
  •     static member toExpr<'a, 'b> (f:'a -> 'b) = 
            <@ Func<_,_> (f) @> 
            |> LeafExpressionConverter.QuotationToExpression 
            |> unbox<Expression<Func<'a, 'b>>>
    

    Result

    最佳答案

    我想我明白了,但花了一些时间才弄清楚如何处理这些表达式。我引用了 this发帖历史看如何搭建System.Linq.Expressions.Expression .这是我所拥有的:

    open System.Linq.Expressions
    open Microsoft.FSharp.Linq.RuntimeHelpers
    
    ...
    
    let toProdRptExpr : Expression<Func<CostItem, ProductionReport>> =
      <@ Func<_, _> (fun (i:CostItem) -> i.ProductionReport) @>
      |> LeafExpressionConverter.QuotationToExpression 
      |> unbox<Expression<Func<CostItem, ProductionReport>>>
    
    let toCostItemsExpr : Expression<Func<ProductionReport, seq<CostItem>>> = 
      <@ Func<_,_> (fun (pr:ProductionReport) -> pr.CostItems) @>
      |> LeafExpressionConverter.QuotationToExpression 
      |> unbox<Expression<Func<ProductionReport, seq<CostItem>>>>
    
    let a = builder.HasOne(toProdRptExpr)
    let b = a.WithMany(toCostItemsExpr)
    

    这比它需要的要冗长得多,但它帮助我弄清楚这些类型是如何组合在一起的。

    编辑

    为简洁起见,您可以创建一个函数,如
    let toExpr (f:'a -> 'b) = 
      <@ Func<_,_> (f) @>
      |> LeafExpressionConverter.QuotationToExpression 
      |> unbox<Expression<Func<'a, 'b>>>
    

    然后像这样使用它
    builder
      .HasOne(toExpr(fun (i:CostItem) -> i.ProductionReport))
      .WithMany(toExpr(fun (pr:ProductionReport) -> pr.CostItems))
    

    但是您必须小心,因为看起来 CostItem 和 ProductionReport 是相互引用的(请参阅下面评论中的讨论)。这意味着它们需要在同一个文件中定义并使用 and关键字(参见 this 示例)

    关于entity-framework - Entity Framework 相关代码从c#改写到f#的难度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55404045/

    相关文章:

    f# - F#中#seq和seq的区别

    c# - 将本地数据库放在 Visual Studio 解决方案中的什么位置?

    c# - 选中复选框时 ASP.NET MVC 更改数据库值

    c# - Entity Framework 7 和 BindingList

    c# - 如何在 Linux 和 MacOS 上导入 PKCS#8 - CngKey.Import 不可用

    c# - 部署到 Azure 应用服务的 WorkerService 未启动

    c# - Entity Framework 6.0 错误与 Mysql 连接器 6.8.3.0

    linux - 使用 Process.start 在 Linux 中的 .Net Core 中获取 "Permission denied"

    .net - MailboxProcessor 性能问题

    c# - 使用 F# 类型的 CLR 接口(interface)