F# - Merge modules? - entity-framework

I'm at the moment working on a Entity Framework Code First Wrapper for F#, and I've been wondering whether I should merge all my modules into just one.
Take a look at this:
module ManyNavProperty =
let withMany (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithMany()
let withSeq expr (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithSeq expr
let withList expr (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithList expr
let withArray expr (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithArray expr
let withOptional (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithOptional()
let withOptionalProperty expr (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithOptional expr
let withRequired (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithRequired()
let withRequiredProperty expr (cfg:ManyNavPropertyInfo<'a,'b>) = cfg.WithRequiredProperty expr
module DependentNavProperty =
let hasForeignKey expr (cfg:DependentNavPropertyInfo<'a>) = cfg.HasForeignKey expr
module CascadableNavProperty =
let willCascadeOnDelete b (cfg:CascadableNavPropertyInfo) = cfg.WillCascadeOnDelete b
module EF =
let entity<'a when 'a : not struct> (cfg:DbModelBuilder) = EntityInfo<'a>(cfg.Entity<'a>())
let hasKey expr (cfg:EntityInfo<'a>) = cfg.HasKey expr
let hasSeq expr (cfg:EntityInfo<'a>) = cfg.HasSeq expr
let hasList expr (cfg:EntityInfo<'a>) = cfg.HasList expr
let hasArray expr (cfg:EntityInfo<'a>) = cfg.HasArray expr
let hasOptional expr (cfg:EntityInfo<'a>) = cfg.HasOptional expr
let hasRequired expr (cfg:EntityInfo<'a>) = cfg.HasRequried expr
let toTable s (cfg:EntityInfo<'a>) = cfg.ToTable s
that would require me to call the code like:
override x.OnModelCreating (mb:DbModelBuilder) =
let finished = ignore
let entity = EF.entity<Author> mb
entity
|> EF.hasSeq <# fun z -> z.Books #>
|> ManyNavProperty.withRequiredProperty <# fun z -> z.Author #>
|> DependentNavProperty.hasForeignKey <# fun z -> z.AuthorId #>
|> CascadableNavProperty.willCascadeOnDelete true
|> finished
Is the use of so many modules confusing for the user? - Should I place them all into one module, or will that destroy the overview for the user?
An example where all functions are placed in same Module:
override x.OnModelCreating (mb:DbModelBuilder) =
let finished = ignore
let entity = EF.entity<Author> mb
entity
|> EF.hasSeq <# fun z -> z.Books #>
|> EF.withRequiredProperty <# fun z -> z.Author #>
|> EF.hasForeignKey <# fun z -> z.AuthorId #>
|> EF.willCascadeOnDelete true
|> finished

This is a tricky design question.
Generally, F# libraries tend to group functions that work with a type (e.g. list<T> or seq<T>) in a separate module with the name same as the type (e.g. List.xyz and Seq.xyz). This works well for common types, because the users learn how to find the functions.
However, for less common types (e.g. your own library), it may be a bit difficult to discover these modules (especially if the type is not created explicitly (but is obtained as a result of some call). Having one module name and then use . to explore it is quite powerful.
I would probably suggest using nested modules. Something like:
module EF =
// common functions
module Cascadable =
// CascadableNavProperty functions
module Many =
// etc.
The users could then find everything by starting with EF. They'd write something like:
entity
|> EF.hasSeq <# fun z -> z.Books #>
|> EF.Many.withRequiredProperty <# fun z -> z.Author #>
|> EF.Dependent.hasForeignKey <# fun z -> z.AuthorId #>
|> EF.Cascadable.willCascadeOnDelete true
|> finished

Related

Difficulty rewriting code from c# to f# related to Entity Framework

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!):
1 Try forcing an argument type
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun (pr: ProductionReport) -> pr.CostItems)
Result
2 Use the alternative Function syntax
builder
.HasOne(<# fun i -> i.ProductionReport #>)
.WithMany(<# fun pr -> pr.CostItems #>)
Result
3 Use the <# notation with specific type
builder
.HasOne(<# Func<ProductionReport,_> fun i -> i.ProductionReport #>)
.WithMany(<# Func<CostItem,_> fun pr -> pr.CostItems #>)
Result
4 Factorize the Expression solution from Nathan
static member toExpr (f:'a -> 'b) =
<# Func<_,_> (f) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
Factorization class
Result
5 Factorize the Expression with type notation suggested by Nathan
static member toExpr<'a, 'b> (f:'a -> 'b) =
<# Func<_,_> (f) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
Result
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)

What is the difference between .() and .{} in Reason?

I'm trying to figure out why the example for using Js.Promise uses
Js.Promise.(
...
)
Whereas the example for Json.Decode uses
Json.Decode.{
...
}
From what I understand, .() opens up Js.Promise so that I can just call any function within Js.Promise without having to prefix Js.Promise as the module.
But what does .{} do?
Both put some module declarations in scope, but .{} creates a record while .() wraps an expression.
For records:
let point = json =>
Json.Decode.{
x: json |> field("x", float),
y: json |> field("y", float)
};
is equivalent to:
let point = json =>
{
x: json |> Json.Decode.field("x", Json.Decode.float),
y: json |> Json.Decode.field("y", Json.Decode.float)
};
Similarly, for expressions:
let _ =
Js.Promise.(then_(value => resolve(Js.log(value)), okPromise));
is equivalent to:
let _ =
Js.Promise.then_(value => Js.Promise.resolve(Js.log(value)), okPromise));

How to annotate let binding as deprecated in OCaml?

I want to annotate a function from an external library as deprecated, to ensure it will not be use in my project. Let's assume the library offers the following module:
module Lib : sig
val safe_function : int -> unit
val unsafe_function : int -> int -> unit
end = struct
let safe_function _ = ()
let unsafe_function _ _ = ()
end
I have a Util.ml file in my project, which I open in every file. In it, I would like to do something like that:
open Lib
let unsafe_function = Lib.unsafe_function
[##deprecated "Use safe_function instead."]
let foo = (fun x -> x)
[##deprecated "BBB"]
type t =
| A [#deprecated]
| B [#deprecated]
[##deprecated]
Compiling the following usage.ml file
open Util
let _ = unsafe_function 0 0
let _ = foo 0
let _ = A
let f (x : t) = x
produces the following warnings:
$ ocamlc -c -w +3 usage.ml
File "usage.ml", line 6, characters 8-9:
Warning 3: deprecated: A
File "usage.ml", line 7, characters 11-12:
Warning 3: deprecated: Util.t
So the deprecated attributes on the let-bindings do not trigger, but the ones on the type definition and constructors do. The attribute syntax seems to allow both.
I found this answer but it seems to be outdated because:
It explicitly says that it "is only available for values (not on types)", which is not true (anymore?) as shown by the above example.
The documentation explicitly says the annotation "can be applied to most kind of items in signatures or structures."
I'm not sure what the exact syntax is (your suggestion sounds right and corresponds to the parser code, so it might be a bug in the compiler), but you can (ab)use the module system to do that :
include (Lib : sig
val unsafe_function : int -> int -> unit
[##ocaml.deprecated "Use safe_function instead."]
end)
let _ = unsafe_function 0 0 (* warning here *)
Just had the same issue. OCaml syntax for attributes is somewhat hairy.
This works:
let unsafe_function [#deprecated "Use safe_function instead."] =
Lib.unsafe_function
(notice the single #)

Operators as function literals

So for a very small formula interpretter someone asked me to code-golf, I wanted to do something like
val ops = Map("plus" -> + , "minus"-> -, "times"-> *, "div" -> / )
to allow quick conversion between keywords and the functions they describe. Not only did this syntax not parse, but none of the other shorthand syntaxes I tried ( _ + _, _:Int.+_)parsed. Is there a way of doing this as a function shorthand, or am I doomed to writing out the lambdas fully, ruining my golf score.
Edit: The problem in question was integers only. I understand that overloading would make this vastly more difficult otherwise.
Not sure how your score is impacted by defining an additional function. You could lift the operators, using something like this:
def lift(f:(Int,Int)=>Int) = f
val ops = Map("plus" -> lift(_+_), "minus" -> lift(_-_), "times"-> lift(_*_), "div" -> lift(_/_))
And if you want to shave some chars:
def ↑(f:(Int,Int)=>Int) = f
val ops = Map("plus" -> ↑(_+_), "minus" -> ↑(_-_), "times"-> ↑(_*_), "div" -> ↑(_/_))
And as om-nom-nom comments, this explicit lifting can be done by the compiler as well by providing the type signature to coerse the lifting:
val ops: Map[String, (Int,Int) => Int] = Map("plus" -> (_+_), "minus" -> (_-_), "times"->(_*_), "div" -> (_/_))

Defining Entity Framework Keys using Fluent API

I am trying to define a key for a model type that has two key properties and is defined like this:
type Model () =
member val IdOne = 0 with get, set
member val IdTwo = 0 with get, set
member val OtherProperty = "" with get, set
When I try to use this model in Entity Framework 5, I get the error that "Model has no key defined. Define the key for this EntityType". The model types are given, I cannot change them and add the [<Key>] attribute. So I tried the Fluent API.
In C#, you would do something like this:
modelBuilder.Entity<Model>().HasKey(m => new { m.IdOne, m.IdTwo });
It uses an anonymous type. But for the life of me I cannot figure out how to pull this off in F#. I tried Tuples, Records, even a regular type that has the properties IdOne and IdTwo:
// Regular type with properties IdOne & IdTwo.
type ModelKey (idOne, idTwo) =
member this.IdOne = idOne
member this.IdTwo = idTwo
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey (m.IdOne, m.IdTwo))
// ArgumentNullException: Value cannot be null. Parameter name: source
// Regular type with default constructor and properties IdOne & IdTwo.
type ModelKey2 () =
member val IdOne = 0 with get, set
member val IdTwo = 0 with get, set
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey2 ())
// ArgumentNullException: Value cannot be null. Parameter name: source
// Record type.
type ModelKeyRecord = { IdOne : Int32; IdTwo : Int32 }
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> { IdOne = m.IdOne; IdTwo = m.IdTwo })
// ArgumentNullException: Value cannot be null. Parameter name: source
// Tuple.
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> (m.IdOne, m.IdTwo))
// ArgumentNullException: Value cannot be null. Parameter name: source
None of these approaches work, I get an ArgumentNullException every time. I'm out of ideas...
EDIT
With the code Tomas provided below (that causes the same ArgumentNullException btw.), I did some snooping around. This is what I found:
I used the function below to analyze the Expression Tree C# is building:
static void Analyze<T>(Expression<Func<Model, T>> function)
{
}
// Call it like this:
Analyze(m => new { m.IdOne, m.IdTwo });
Then I looked at the generated lambda in the debugger. This is what C# generates:
{m => new <>f__AnonymousType0'2(IdOne = m.IdOne, IdTwo = m.IdTwo)}
Doing the same thing on the F# side using the getFuncTree function from Tomas using <# fun (m : Model) -> ModelKey(m.IdOne, m.IdTwo) #> yields:
{m => new ModelKey(m.IdOne, m.IdTwo)}
As you can see, the explicit naming of the - what is this anyway, looks like properties - arguments is missing in the F# code. I recreated the whole expression tree by hand in F# so that it would look like the C# version:
let modelKeyExpression =
Expression.Lambda<Func<Model, ModelKey>> (
body = Expression.New (
``constructor`` = typeof<ModelKey>.GetConstructor [| typeof<Int32>; typeof<Int32> |],
arguments = seq {
yield Expression.MakeMemberAccess (
expression = Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
),
``member`` = typeof<Model>.GetProperty "IdOne"
) :> Expression;
yield Expression.MakeMemberAccess (
expression = Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
),
``member`` = typeof<Model>.GetProperty "IdTwo"
) :> Expression
},
members = seq {
yield (typeof<ModelKey>.GetProperty "IdOne") :> MemberInfo
yield (typeof<ModelKey>.GetProperty "IdTwo") :> MemberInfo
}
),
parameters = [
Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
)
]
)
The part that was missing in the F# representation is the members sequence.
When I move the mouse over this expression, this representation appears:
{m => new ModelKey(IdOne = m.IdOne, IdTwo = m.IdTwo)}
As you can see, apart from the class, it looks the same. But when I try to use this expression in the HasKey method, I get the following InvalidOperationException:
The properties expression 'm => new ModelKey(IdOne = m.IdOne, IdTwo= m.IdTwo)'
is not valid. The expression should represent a property: C#: 't =>
t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple
properties use an anonymous type: C#: 't => new { t.MyProperty1,
t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1,
t.MyProperty2 }'.
So it seems to me that this anonymous class syntax does something special...
In F# 4.6, this has worked for me after much struggle.
Apperantly all you need is a tuple. Makes sense since an anon object without explicit member names is basically, a tuple.
I can't believe nobody has put this on the MS documentation tho.
modelBuilder.Entity<Entity>()
.HasKey(fun e -> (e.Key1, e.Key2) :> obj)
|> ignore
EDIT: The technique below would be needed in F# 2.0, but it should not be needed in the newer versions. There must be some other issue with the F#-generated expression tree...
I think the problem is that Entity Framework wants you to specify a lambda expression as an expression tree:
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey (m.IdOne, m.IdTwo))
This should just work in F# 3.1 in Visual Studio 2013, but it is not supported in F# 3.0. You can still do this, but you'll have to use F# quotations and write a bit of code that converts a quotation to LINQ expression tree - there is a helper that does most of the work:
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
let getFuncTree (quotation:Expr<'a -> 'b>) =
let e = LeafExpressionConverter.QuotationToExpression quotation
let call = e :?> MethodCallExpression
let lam = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'a, 'b>>(lam.Body, lam.Parameters)
getFuncTree <# fun x -> x + 1 #>
Using this, you should be able to call:
modelBuilder.Entity<Model>().HasKey(getFuncTree <# fun (m : Model) ->
ModelKey (m.IdOne, m.IdTwo) #>)
You could define an extension method like HasKeyQuot that does this behind the cover, to make the code a bit nicer.
It looks like the issue is twofold:
F#'s compiler never sets the Members collection of a LINQ NewExpression during quotation translation, but this is used to mark the construction of an anonymous type, which EF expects.
EF is really picky: even in C#, doing m => new { A = m.IdOne, B = m.IdTwo } won't work - the anonymous type's property names must match the model's property names.
One way to work around the issue (which is probably overkill, but works) is to create a new type dynamically at runtime which has fields with the right names and then just use a tuple in the F# code:
open Quotations.Patterns
open Quotations.ExprShape
open System.Reflection
open System.Linq.Expressions
module AnonymousTypeFixer =
let private mb =
let ab = System.AppDomain.CurrentDomain.DefineDynamicAssembly(AssemblyName("codeGen"), Emit.AssemblyBuilderAccess.ReflectionOnly)
ab.DefineDynamicModule("codeGen")
let transform (Lambda(v, (NewTuple exprs)) : Quotations.Expr<'a -> 'b>) =
let newV = Expression.Variable(v.Type, v.Name)
let cvtExpr (PropertyGet(Some(Var v'), p, [])) =
assert (v = v')
Expression.Property(newV, p) :> Expression, p
let ty = mb.DefineType(v.Type.Name)
let ctor = ty.DefineConstructor(MethodAttributes.Public (*||| MethodAttributes.RTSpecialName ||| MethodAttributes.SpecialName*), CallingConventions.HasThis, exprs |> List.map (fun e -> e.Type) |> List.toArray)
ctor.GetILGenerator().Emit(Emit.OpCodes.Ret)
let fields =
[for (_, p) in exprs |> List.map cvtExpr ->
ty.DefineField(p.Name, p.PropertyType, FieldAttributes.Public) :> MemberInfo]
ty.CreateType()
let newE = Expression.New(ctor, exprs |> Seq.map (cvtExpr >> fst), fields)
Expression.Lambda<System.Func<'a, obj>>(newE, newV)
let mb = System.Data.Entity.DbModelBuilder()
mb.Entity<Model>().HasKey(AnonymousTypeFixer.transform <# fun (m:Model) -> m.IdOne, m.IdTwo #>)
// F# 3.0
open Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter
// Regular type with properties IdOne & IdTwo.
type ModelKey (idOne, idTwo) =
member this.IdOne = idOne
member this.IdTwo = idTwo
modelBuilder.Entity<Model>()
.HasKey(QuotationToLambdaExpression(
<# Func<_,_>(fun m -> NewAnonymousObjectHelper<_>(ModelKey(m.IdOne, m.IdTwo))) #>
)
)