F# 如何修复由使用自定义工作流生成器的计算表达式生成的这些错误?

F# 如何修复由使用自定义工作流生成器的计算表达式生成的这些错误?,f#,computation-expression,F#,Computation Expression,从文档中我了解到,如果Run被实现,它将在计算表达式的末尾被自动调用。它说: builder.Run(builder.Delay(fun () -> {| cexpr |})) 将为计算表达式生成。如果工作流生成器中未定义运行和/或延迟,则将忽略它们。我希望我的ReaderBuilder在自动调用Run时返回MyItem对象的列表。所以我不明白为什么会出现类型不匹配错误。错误是由我在这里列出的代码末尾的ProcedureBuilder foo中的return语句生成的。有人能解释一下我对

从文档中我了解到,如果Run被实现,它将在计算表达式的末尾被自动调用。它说:

builder.Run(builder.Delay(fun () -> {| cexpr |}))
将为计算表达式生成。如果工作流生成器中未定义运行和/或延迟,则将忽略它们。我希望我的ReaderBuilder在自动调用Run时返回MyItem对象的列表。所以我不明白为什么会出现类型不匹配错误。错误是由我在这里列出的代码末尾的ProcedureBuilder foo中的return语句生成的。有人能解释一下我对工作流构建器的误解以及我的错误实现吗

我得到以下错误:

类型“a列表”与类型“ReaderBuilder”不兼容

open System
open System.Data
open System.Data.Common
open System.Configuration

let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)

type Direction =
    | In
    | Out
    | Ref
    | Return

type dbType =
    | Int32
    | String of int


type ReaderBuilder(cmd) =
    let mutable items = []
    member x.Foo = 2

    member x.YieldFrom item =
        items <- item::items
        item

    member x.Run item =
        items


type ProcBuilder(procedureName:string) =
    let name = procedureName
    let mutable parameters = []
    let mutable cmd:DbCommand = null
    let mutable data = []

    member x.Command with get() = cmd

    member x.CreateCommand() =
        factory.CreateCommand()
    member x.AddParameter(p:string*dbType*Direction) =
        parameters <- p::parameters

    member x.Bind(v,f) =
        f v

    member x.Reader = ReaderBuilder(cmd)

    member x.Return(rBuilder:ReaderBuilder) =
        data


let (?<-) (builder:ProcBuilder) (prop:string) (value:'t) =
    builder.Command.Parameters.[prop].Value <- value


type MyItem() =
    let mutable _a = 0
    let mutable _b = String.Empty
    let mutable _c = DateTime.Now

    member x.a
        with get() = _a
        and set n = _a <- n
    member x.b
        with get() = _b
        and set n  = _b <- n
    member x.c
        with get() = _c
        and set n = _c <- n


let proc name = ProcBuilder(name)

let (%) (builder:ProcBuilder) (p:string*dbType*Direction) =
    builder.AddParameter(p)
    builder

let (?) (r:DbDataReader) (s:string) = r.GetOrdinal(s)
let foo x y = 
    let foo = proc "foo" % ("x", Int32, In) % ("y", String(15), In)
    foo?x <- x
    foo?y <- y

    foo {
        do! foo?x <- x
        do! foo?y <- y
        return foo.Reader {
            let item = MyItem()
            item.a <- r.GetInt32("a")
            item.b <- r.GetString("b")
            item.c <- r.GetDateTime("c")
            yield! item
        }
    }
类型约束不匹配。类型“a列表”与类型ReaderBuilder不兼容类型“a列表”与类型“ReaderBuilder”不兼容

open System
open System.Data
open System.Data.Common
open System.Configuration

let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)

type Direction =
    | In
    | Out
    | Ref
    | Return

type dbType =
    | Int32
    | String of int


type ReaderBuilder(cmd) =
    let mutable items = []
    member x.Foo = 2

    member x.YieldFrom item =
        items <- item::items
        item

    member x.Run item =
        items


type ProcBuilder(procedureName:string) =
    let name = procedureName
    let mutable parameters = []
    let mutable cmd:DbCommand = null
    let mutable data = []

    member x.Command with get() = cmd

    member x.CreateCommand() =
        factory.CreateCommand()
    member x.AddParameter(p:string*dbType*Direction) =
        parameters <- p::parameters

    member x.Bind(v,f) =
        f v

    member x.Reader = ReaderBuilder(cmd)

    member x.Return(rBuilder:ReaderBuilder) =
        data


let (?<-) (builder:ProcBuilder) (prop:string) (value:'t) =
    builder.Command.Parameters.[prop].Value <- value


type MyItem() =
    let mutable _a = 0
    let mutable _b = String.Empty
    let mutable _c = DateTime.Now

    member x.a
        with get() = _a
        and set n = _a <- n
    member x.b
        with get() = _b
        and set n  = _b <- n
    member x.c
        with get() = _c
        and set n = _c <- n


let proc name = ProcBuilder(name)

let (%) (builder:ProcBuilder) (p:string*dbType*Direction) =
    builder.AddParameter(p)
    builder

let (?) (r:DbDataReader) (s:string) = r.GetOrdinal(s)
let foo x y = 
    let foo = proc "foo" % ("x", Int32, In) % ("y", String(15), In)
    foo?x <- x
    foo?y <- y

    foo {
        do! foo?x <- x
        do! foo?y <- y
        return foo.Reader {
            let item = MyItem()
            item.a <- r.GetInt32("a")
            item.b <- r.GetString("b")
            item.c <- r.GetDateTime("c")
            yield! item
        }
    }
开放系统
开放系统.数据
开放系统.Data.Common
开放系统配置
让config=ConfigurationManager.ConnectionString.Item(“db”)
let factory=dbProviderFactorys.GetFactory(config.ProviderName)
类型方向=
|在
|出去
|参考号
|返回
类型dbType=
|Int32
|整型字符串
类型ReaderBuilder(cmd)=
让可变项=[]
成员x.Foo=2
成员x.YieldFrom项目=

items示例中的问题是
foo.Reader{…}
块有一个返回类型
MyItem list
(因为这是
ReaderBuilder
类型的
Run
成员返回的)。但是,
ProcBuilder
Return
成员需要类型为
ReaderBuilder
的参数

ReaderBuilder
data
字段将始终是一个空列表,因此这也是可疑的。我想您可能想更改
ProcBuilder
返回值
,改为使用参数
MyItem list


然而,我认为使用自定义计算生成器进行数据库访问并没有给您带来多少好处。从某种意义上讲,您并不是在创建“非标准计算”。相反,您可能只需要一个好的语法来调用命令和读取数据。使用动态操作符可以使它变得非常优雅,即使没有计算构建器——我不久前写过一篇文章

我知道名单将是空的。还有很多其他缺失的部分。我只是想让它达到编译的目的。我将在编译后添加所有实现。我理解你所说的,但我认为我需要一点时间来找出解决这个问题的最佳方法。你可能是对的,这不是计算表达式的最佳用途,但我确实学到了很多:)顺便说一句:计算生成器通常没有任何私有状态。尝试将其设计为不需要私有(可变)状态是一个很好的练习,它可以帮助您理解计算构建器应该如何工作,也有助于找出最佳设计是什么。通常,您应该首先设计由计算生成器创建的“类型”。我在我的书的第12章中写到了这一点。这一章是免费提供的:我的亚马逊愿望清单上已经有这本书了。希望我能在六月份有钱买它。能够从具有你知识水平的人那里得到建议真是太好了。我很感激。