Reputation: 43
I have a block of code that I want to write in F#, but the examples I have are in C#. I would like some help to write this in the F# language, and help understanding how it works.
Here is the c# code I have to mimic:
builder.HasMany(r => r.Options).WithOne(o => o.Root).HasForeignKey(o => o.RootId).OnDelete(DeleteBehavior.Cascade);
In F#, I am trying to do this:
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun pr -> pr.CostItems)
.HasForeignKey(fun pr -> pr.ProductionReportId).OnDelete(DeleteBehavior.Cascade) |> ignore
And the issue, per visual studio, is that pr is of type obj. How do I make sure f# knows that pr is of type ProductionReport, according to the return type of builder.HasOne.
Here is the complete sample requested:
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
Here are the results of the suggestions below (thanks by the way!):
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun (pr: ProductionReport) -> pr.CostItems)
builder
.HasOne(<@ fun i -> i.ProductionReport @>)
.WithMany(<@ fun pr -> pr.CostItems @>)
builder
.HasOne(<@ Func<ProductionReport,_> fun i -> i.ProductionReport @>)
.WithMany(<@ Func<CostItem,_> fun pr -> pr.CostItems @>)
static member toExpr (f:'a -> 'b) =
<@ Func<_,_> (f) @>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
static member toExpr<'a, 'b> (f:'a -> 'b) =
<@ Func<_,_> (f) @>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
Upvotes: 4
Views: 748
Reputation: 639
I think I got it, but it took some digging to figure out how to work with the expressions. I referenced this post's history to see how to build a System.Linq.Expressions.Expression
. Here's what I have:
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)
that's a lot more verbose than it needs to be, but it helped me figure out how the types fit together.
EDIT
For brevity, you can create a function like
let toExpr (f:'a -> 'b) =
<@ Func<_,_> (f) @>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
and then use it like
builder
.HasOne(toExpr(fun (i:CostItem) -> i.ProductionReport))
.WithMany(toExpr(fun (pr:ProductionReport) -> pr.CostItems))
But you have to be careful because it looks like CostItem and ProductionReport are mutually referential (see the discussion in comments below). That means they need to be defined in the same file and use the and
keyword (see this example)
Upvotes: 1