2016-09-15 1 views
3

Ce qui semble être une tâche facile en C# ne semble pas si facile à F # ...Inscrivez-vous avec Entity Framework et F #

Compte tenu des types en C# suivant que je veux faire une jointure avec F #:

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; } 
} 

donc mon essai à faire est:

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) 

Mais le compilateur se plaint avec quelque chose comme:

surcharge possible: (extension)

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> 

Type de non-concordance de contrainte. Le type 'd * 'e -> foo * bar n'est pas compatible avec le type System.Func<'a, 'b, 'c>

Le type 'd * 'e -> foo * bar n'est pas compatible avec le type System.Func<'a, 'b, 'c>

Je ne sais pas lire cela. Peut-être que je ne peux pas retourner un tuple à la fin? En C#, j'aurais besoin d'un type anonyme, comme new { Foo = foo, Bar = bar }, je ne sais pas comment faire ça en F #.

+4

Pourquoi ne pas utiliser une [expression de requête] (https://docs.microsoft.com/en-us/dotnet/ articles/fsharp/language-reference/expressions-de-requête)? Dans tous les cas, essayez de changer 'let joinResult (f: Foo, b: Bar) =' en laissant joinResult (f: Foo) (b: Bar) = '. – ildjarn

+1

Les joints sont une odeur * très * forte lors de l'utilisation des ORM. Ils signifient que la cartographie est fausse et que certaines relations importantes manquent. Si 'Foo' avait une propriété' Bar', vous n'auriez pas besoin de vous joindre. EF générerait le SQL approprié pour charger les entités liées. Vous pouvez contrôler si les entités liées sont chargées avec empressement ou paresseusement. –

+2

F # convertit souvent automatiquement les fonctions F # en types de délégués .NET (par exemple, 'Func <_,_,_>') lors d'invocations de méthode, mais vous devez utiliser des entrées au carrefour plutôt que des entrées (par exemple 'let joinResult (f: Foo) (b: Bar) = ... ') – kvb

Répondre

2

Solution (grâce @ildjarn) en utilisant des arguments currified pour la dernière func:

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) 

qui à la fin peut être simplifiée:

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

@PanagiotisKanavos m'a aussi donné un indice sur les jointures utiliser un ORM étant une odeur de code, et cela m'a permis de découvrir qu'il y avait effectivement une propriété Bar dans la classe Foo (de sorte que je n'ai pas besoin de jouer avec la colonne BarID, car cette propriété Bar est remplie par EntityFramewor k de cette façon de toute façon). Ceci, combiné avec la suggestion originale de @ ildjarn pour l'utilisation query expressions, m'a conduit à la meilleure réponse:

let dbContext = DatabaseManager.Instance.ProduceContext() 

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