2016-09-15 1 views
3

Was eine einfache Aufgabe in C# scheint nicht in F # so einfach zu sein scheint ...Join mit Entity Framework und F #

folgende Typen in C# Da ich eine innere Verknüpfung mit F # machen wollen:

public partial class Foo 
{ 
    public long FooID { get; set; } 
    public long BarID { get; set; } 
    public bool SomeColumn1 { get; set; } 
} 

public partial class Bar 
{ 
    public long ID { get; set; } 
    public string SomeColumn1 { get; set; } 
    public bool SomeColumn2 { get; set; } 
} 

Also mein Versuch dies zu tun ist:

let dbContext = DatabaseManager.Instance.ProduceContext() 

let barIdForeignKey(f: Foo) = 
    f.BarID 

let barIdPrimaryKey(b: Bar) = 
    b.ID 

let joinResult(f: Foo, b: Bar) = 
    (f, b) 

let joinedElements = 
    dbContext.foos.Join(dbContext.bars, barIdForeignKey, barIdPrimaryKey, joinResult) 

Aber der Compiler beschwert sich mit so etwas wie:

Mögliche Überlastung: (Erweiterung)

System.Collections.Generic.IEnumerable.Join<'TOuter, 'TInner, 'TKey, 'TResult>(
    inner:   System.Collections.Generic.IEnumerable<'TInner>, 
    outerKeySelector: System.Func<'TOuter, 'TKey>, 
    innerKeySelector: System.Func<'TInner, 'TKey>, 
    resultSelector: System.Func<'TOuter, 'TInner, 'TResult>) 
    : System.Collections.Generic.IEnumerable<'TResult> 

Typ constraint Mismatch. Der Typ 'd * 'e -> foo * bar ist nicht kompatibel mit Typ System.Func<'a, 'b, 'c>

Der Typ 'd * 'e -> foo * bar mit dem Typ ist nicht kompatibel System.Func<'a, 'b, 'c>

nicht sicher, wie dies zu lesen. Vielleicht kann ich am Ende kein Tupel zurückgeben? In C# würde ich einen anonymen Typ benötigen, wie new { Foo = foo, Bar = bar }, nicht sicher, wie man das in F # macht.

+4

Warum nicht ein [Abfrageausdruck] verwenden (https://docs.microsoft.com/en-us/dotnet/ Artikel/fsharp/Sprachreferenz/Query-Ausdrücke)? Versuchen Sie in jedem Fall, 'joinResult (f: Foo, b: Bar) =' zu ändern, um joinResult (f: Foo) (b: Bar) = 'zu lassen. – ildjarn

+1

Joins riechen * sehr * sehr stark bei der Verwendung von ORMs. Sie bedeuten, dass die Zuordnung falsch ist und einige wichtige Beziehungen fehlen. Wenn "Foo" eine "Bar" -Eigenschaft hätte, müssten Sie nicht beitreten. EF würde das richtige SQL generieren, um die zugehörigen Entitäten zu laden. Sie können steuern, ob die verbundenen Entitäten eifrig oder träge geladen werden. –

+2

F # wird bei Methodenaufrufen oft automatisch F # -Funktionen in .NET-Delegattypen (zB 'Func <_,_,_>') umwandeln, aber Sie müssen curried statt tupled-Eingaben verwenden (zB 'let joinResult (f: Foo) (b: Bar) = ... "). – kvb

Antwort

2

Solution (dank @ildjarn) currified Argumente für die letzte func mit:

let dbContext = DatabaseManager.Instance.ProduceContext() 

let barIdForeignKey(f: Foo) = 
    f.BarID 

let barIdPrimaryKey(b: Bar) = 
    b.ID 

let joinResult(f: Foo) (b: Bar) = 
    (f, b) 

let joinedElements = 
    dbContext.foos.Join(dbContext.bars, barIdForeignKey, barIdPrimaryKey, joinResult) 

die am Ende kann vereinfacht werden:

let joinedElements = 
    dbContext.foos.Join (dbContext.bars, 
         (fun f -> f.barID), 
         (fun b -> b.ID), 
         (fun f b -> (f,b)) 
         ) 

@PanagiotisKanavos gab mir auch einen Hinweis verbindet ein ORM ist ein Code-Geruch, und das ließ mich entdecken, dass es tatsächlich eine Bar Eigenschaft in der Foo Klasse (so dass ich nicht mit BarID-Spalte geigen müssen, wie diese Bar-Eigenschaft unter der Haube von EntityFramewor gefüllt ist k auf diese Weise sowieso). Dies, kombiniert mit ursprünglichen Vorschlag des @ ildjarn query expressions für die Verwendung, führte mich in die beste Antwort:

let dbContext = DatabaseManager.Instance.ProduceContext() 

let joinedElements = 
    query { 
     for foo in dbContext.foos do 
      select (foo.Bar, foo.SomeColumn1) 
    }