是否可以在F#中实现IDbSet <t>接口?</t>

时间:2014-05-14 00:58:03

标签: entity-framework f#

我正在尝试对IDbSet<T>进行模拟实现,而我恰巧在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:此构造导致代码更少   通用而不是类型注释。类型变量&#39; TD   被限制为类型&#39; T&#39;。

     

错误FS0663:此类型   参数的使用方式将其限制为始终为&#39; T.   当&t; T:不结构&#39;

     

错误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接口。任何建议都会受到欢迎。

2 个答案:

答案 0 :(得分:8)

方法Create需要2个泛型类型参数之间的子类型约束。我担心没有办法根据F#中的另一个参数向泛型类型参数添加子类型约束。它们总是被假定为相等,请参阅表单类型的spec 新约束:&gt; 'b再次被解决为type ='b。

请参阅此相关问题answer

我们应该要求在下一个F#版本中加入this功能。

答案 1 :(得分:2)

起初我对此感到非常失望。我仍然在某些方面,但EF6有一个解决方法。您可以直接继承DbSet<'TEntity>,并使用覆盖在内存中实现集合。这对大多数情况来说就足够了;如果您想要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