【问题标题】:Is it possible to implement the IDbSet<T> interface in F#?是否可以在 F# 中实现 IDbSet<T> 接口?
【发布时间】:2018-02-22 21:57:42
【问题描述】:

我正在尝试对IDbSet&lt;T&gt; 进行模拟实现,而我恰好是在 F# 中进行的。

type MockDbSet<'T when 'T : not struct>(items:seq<'T>) =
    let collection = ResizeArray(items)

    new () = MockDbSet(Seq.empty)

    interface IDbSet<'T> with
        member x.Add entity = collection.Add entity; entity
        member x.Attach entity = collection.Add entity; entity
        member x.Remove entity = collection.Remove entity |> ignore; entity
        member x.Create() = Unchecked.defaultof<'T>
        member x.Create<'TD when 'TD : not struct and 'TD :> 'T>() = Unchecked.defaultof<'TD>
        member x.Find([<ParamArray>] values) = raise <| NotImplementedException()
        member x.Local = Collections.ObjectModel.ObservableCollection(collection)

    interface System.Collections.Generic.IEnumerable<'T> with
        member x.GetEnumerator() = 
            collection.GetEnumerator() :> System.Collections.Generic.IEnumerator<_>

    interface System.Collections.IEnumerable with
        member x.GetEnumerator() =
            collection.GetEnumerator() :> System.Collections.IEnumerator

    interface IQueryable<'T> with
        member x.ElementType = typeof<'T>
        member x.Expression =
            collection.AsQueryable().Expression
        member x.Provider =
            collection.AsQueryable().Provider

一切都很好,除了这一行:

member x.Create<'TD when 'TD : not struct and 'TD :> 'T>() = Unchecked.defaultof<'TD>

...这给了我这些编译器错误:

错误 FS0698:无效约束:用于约束的类型是 密封,这意味着约束最多只能满足 一种解决方案

警告 FS0064:此构造会导致代码变少 比类型注释所指示的泛型。类型变量 'TD 已被限制为“T”类型。

错误 FS0663:此类型 参数的使用方式将其限制为始终为 ''T 当'T:不是结构'

错误 FS0661:一个或多个显式类 或此绑定的函数类型变量无法泛化, 因为它们被限制为其他类型

此行正在尝试实现this method,根据该页面,它在 C# 中具有以下签名:

TDerivedEntity Create<TDerivedEntity>()
where TDerivedEntity : class, TEntity

F# 中的这个签名:

abstract Create : unit -> 'TDerivedEntity  when 'TDerivedEntity : not struct and 'TEntity

当我尝试使用示例 F# 签名时,我遇到了各种语法错误,这并不让我感到惊讶,因为该签名甚至看起来不像是有效的 F#。

我不确定如何处理这些错误消息,或者如何编写约束来满足接口和 F# 编译器的要求。我开始怀疑是否有可能用这种特殊的 Microsoft 编程语言来实现这个特殊的 Microsoft 界面。欢迎提出任何建议。

【问题讨论】:

    标签: entity-framework f#


    【解决方案1】:

    方法Create 需要两个泛型类型参数之间的子类型约束。恐怕没有办法将子类型约束添加到基于 F# 中另一个的泛型类型参数。它们总是被假定为相等的,请参阅spec 表单类型的新约束:> 'b 再次解决为 type = 'b。

    看到这个与answer相关的类似问题。

    我们应该请求在下一个 F# 版本中包含 this 功能。

    【讨论】:

    • 我不认为 UserVoice 的建议是完全相关的 - 虽然裸约束将是解决 F# 缺乏通用 {co,contra}-variance 的一种方法,但它们确实是一个不同的功能,并且添加通用方差而不添加裸约束无法解决此问题。
    • @kvb 我同意,它是相关的,但我们应该重新制定它或单独建议它。即使我们没有协方差/反方差,我也希望能够添加这种约束。你怎么看?
    • 是的,在这里创建一个涵盖实际场景的新功能请求绝对有意义。这种情况很少见,但有些库无法从 F# 中使用,这是一个巨大的痛苦,并且没有解决方法。
    【解决方案2】:

    起初我对此感到非常失望。在某些方面我仍然存在,但 EF6 中有一种解决方法。您可以直接继承DbSet&lt;'TEntity&gt;,并使用覆盖来实现内存中的集合。这对大多数情况来说就足够了;如果你想要Find 的具体实现,你可以继承这种类型。

    type FakeDbSet<'TEntity when 'TEntity : not struct>(items: seq<'TEntity>) =
        inherit DbSet<'TEntity>()
        let data = ObservableCollection<'TEntity>(items)
        let query = data.AsQueryable()
        new() = FakeDbSet(Seq.empty)
        override __.Add(item: 'TEntity) = data.Add(item); item
        override __.Remove(item: 'TEntity) = data.Remove(item) |> ignore; item
        override __.Attach(item: 'TEntity) = data.Add(item); item
        override __.Create() = Activator.CreateInstance<'TEntity>()
        override __.Local with get() = data
        interface System.Collections.Generic.IEnumerable<'TEntity> with
            member __.GetEnumerator() = data.GetEnumerator() :> System.Collections.Generic.IEnumerator<_>
        interface System.Collections.IEnumerable with
            member __.GetEnumerator() = data.GetEnumerator() :> System.Collections.IEnumerator
        interface IQueryable<'TEntity> with
            member __.ElementType = typeof<'TEntity>
            member __.Expression = query.Expression
            member __.Provider = query.Provider
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-26
      • 1970-01-01
      • 1970-01-01
      • 2015-02-23
      • 2015-12-23
      • 1970-01-01
      • 2021-11-20
      相关资源
      最近更新 更多