加入 Entity Framework 和 F#
Join with Entity Framework and F#
在 C# 中看似简单的任务在 F# 中似乎并不容易...
鉴于 C# 中的以下类型,我想使用 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; }
}
所以我的尝试是:
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)
但是编译器会报错:
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 type System.Func<'a, 'b, 'c>
The type 'd * 'e -> foo * bar
is not compatible with the type System.Func<'a, 'b, 'c>
不确定如何阅读。也许是我不能 return 最后一个元组?在 C# 中,我需要一个匿名类型,比如 new { Foo = foo, Bar = bar }
,不知道如何在 F# 中做到这一点。
解决方案(感谢@ildjarn)对最后一个函数使用柯里化参数:
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)
这到底可以简化为:
let joinedElements =
dbContext.foos.Join (dbContext.bars,
(fun f -> f.barID),
(fun b -> b.ID),
(fun f b -> (f,b))
)
@PanagiotisKanavos 也给了我关于使用 ORM 的连接是一种代码味道的提示,这让我发现 Foo
中实际上有一个 Bar
属性 class(这样我就不需要 fiddle 与 BarID 列,因为这个 Bar 属性 无论如何都是通过 EntityFramework 在幕后填充的)。结合@ildjarn 最初关于使用 query expressions 的建议,我得出了最佳答案:
let dbContext = DatabaseManager.Instance.ProduceContext()
let joinedElements =
query {
for foo in dbContext.foos do
select (foo.Bar, foo.SomeColumn1)
}
在 C# 中看似简单的任务在 F# 中似乎并不容易...
鉴于 C# 中的以下类型,我想使用 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; }
}
所以我的尝试是:
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)
但是编译器会报错:
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>
不确定如何阅读。也许是我不能 return 最后一个元组?在 C# 中,我需要一个匿名类型,比如 new { Foo = foo, Bar = bar }
,不知道如何在 F# 中做到这一点。
解决方案(感谢@ildjarn)对最后一个函数使用柯里化参数:
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)
这到底可以简化为:
let joinedElements =
dbContext.foos.Join (dbContext.bars,
(fun f -> f.barID),
(fun b -> b.ID),
(fun f b -> (f,b))
)
@PanagiotisKanavos 也给了我关于使用 ORM 的连接是一种代码味道的提示,这让我发现 Foo
中实际上有一个 Bar
属性 class(这样我就不需要 fiddle 与 BarID 列,因为这个 Bar 属性 无论如何都是通过 EntityFramework 在幕后填充的)。结合@ildjarn 最初关于使用 query expressions 的建议,我得出了最佳答案:
let dbContext = DatabaseManager.Instance.ProduceContext()
let joinedElements =
query {
for foo in dbContext.foos do
select (foo.Bar, foo.SomeColumn1)
}