Трудность переписать код с c # на f #, связанный с Entity Framework - PullRequest
4 голосов
/ 28 марта 2019

У меня есть блок кода, который я хочу написать на 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

И проблема для каждой визуальной студии в том, что pr имеет тип obj.Как убедиться, что f # знает, что pr имеет тип ProductionReport в соответствии с типом возвращаемого значения builder.HasOne.

Вот полный требуемый пример:

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());

CostItem

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

ProductionReport

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 Используйте обозначение <@ с определенным типом </li>
builder
    .HasOne(<@ Func<ProductionReport,_> fun i -> i.ProductionReport @>) 
    .WithMany(<@ Func<CostItem,_> fun pr -> pr.CostItems @>)

Результат

  • 4 Факторизовать решение Expression от Натана
static member toExpr (f:'a -> 'b) = 
    <@ Func<_,_> (f) @> 
    |> LeafExpressionConverter.QuotationToExpression 
    |> unbox<Expression<Func<'a, 'b>>>

Класс факторизации

Результат

  • 5 FacТоризовать выражение с помощью обозначения типа, предложенного Натаном
    static member toExpr<'a, 'b> (f:'a -> 'b) = 
        <@ Func<_,_> (f) @> 
        |> LeafExpressionConverter.QuotationToExpression 
        |> unbox<Expression<Func<'a, 'b>>>

Результат

1 Ответ

1 голос
/ 29 марта 2019

Я думаю, что понял, но потребовалось некоторое копание, чтобы понять, как работать с выражениями. Я сослался на эту историю записи , чтобы узнать, как создать 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)

это гораздо более многословно, чем нужно, но это помогло мне понять, как типы сочетаются друг с другом.

EDIT

Для краткости вы можете создать такую ​​функцию, как

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 (см. этот пример)

...