Reputation: 17939
What seems to be an easy task in C# doesn't seem so easy in F#...
Given the following types in C# I want to make an inner join with 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; }
}
So my try at doing this is:
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)
But the compiler complains with something like:
Possible overload: (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 constraint mismatch. The type
'd * 'e -> foo * bar
is not compatible with typeSystem.Func<'a, 'b, 'c>
The type
'd * 'e -> foo * bar
is not compatible with the typeSystem.Func<'a, 'b, 'c>
Not sure how to read this. Maybe it's that I cannot return a tuple at the end? In C# I would need an anonymous type, like new { Foo = foo, Bar = bar }
, not sure how to do that in F#.
Upvotes: 3
Views: 780
Reputation: 17939
Solution (thanks @ildjarn) using currified arguments for the last 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)
Which in the end can be simplified to:
let joinedElements =
dbContext.foos.Join (dbContext.bars,
(fun f -> f.barID),
(fun b -> b.ID),
(fun f b -> (f,b))
)
@PanagiotisKanavos also gave me a hint about joins using an ORM being a code-smell, and that let me discover that there was actually a Bar
property in the Foo
class (so that I don't need to fiddle with BarID column, as this Bar property is populated under the hood by EntityFramework this way anyway). This, combined with @ildjarn's original suggestion for using query expressions, led me to the best answer:
let dbContext = DatabaseManager.Instance.ProduceContext()
let joinedElements =
query {
for foo in dbContext.foos do
select (foo.Bar, foo.SomeColumn1)
}
Upvotes: 3