как мне исправить эти ошибки, сгенерированные моим вычислительным выражением, использующим мой пользовательский построитель рабочих процессов?

Из документации MSDN я понимаю, что если Run будет реализован, он будет называться автоматически в конце вычислительного выражения. В нем говорится, что:

builder.Run(builder.Delay(fun () -> {| cexpr |}))

будет сгенерировано для вычислительного выражения. Run и/или Delay будут опущены, если они не определены в построителе рабочего процесса. Я ожидал, что мой ReaderBuilder вернет список объектов MyItem при автоматическом вызове Run. Поэтому я не понимаю, почему я получаю ошибку несоответствия типов. Ошибки генерируются оператором return внутри procedureBuilder foo в конце моего листинга кода здесь. Может ли кто-нибудь объяснить, что я неправильно понимаю в построителях рабочих процессов и что я реализовал неправильно?

Я получаю следующие ошибки:

Тип «список» несовместим с типом «ReaderBuilder».

Несоответствие ограничения типа. Тип «список» несовместим с типом ReaderBuilder Тип «список» несовместим с типом «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
        }
    }

person Charles Lambert    schedule 14.05.2011    source источник


Ответы (1)


Проблема в вашем примере заключается в том, что блок foo.Reader { ... } имеет тип возвращаемого значения MyItem list (поскольку это то, что возвращает элемент Run типа ReaderBuilder). Однако Return член ProcBuilder ожидает аргумент типа ReaderBuilder.

Поле data в ReaderBuilder всегда будет пустым списком, так что это тоже подозрительно. Я думаю, вы, вероятно, захотите изменить Return из ProcBuilder, чтобы вместо этого принять аргумент MyItem list.

Однако я думаю, что использование собственного построителя вычислений для доступа к базе данных на самом деле не дает вам большого преимущества. В каком-то смысле вы не создаете «нестандартные вычисления». Вместо этого вам, вероятно, просто нужен хороший синтаксис для вызова команд и чтения данных. Использование динамического оператора может сделать это довольно элегантным даже без построителей вычислений - я написал об этом статью некоторое время назад.

person Tomas Petricek    schedule 14.05.2011
comment
Я знаю, что список будет пуст. Есть много других недостающих частей. Я просто пытался добраться до точки компиляции. Я собираюсь добавить всю реализацию после того, как смогу ее скомпилировать. - person Charles Lambert; 15.05.2011
comment
Я понимаю, что вы сказали, но я думаю, что мне понадобится немного времени, чтобы найти лучший способ обойти проблему. - person Charles Lambert; 15.05.2011
comment
возможно, вы правы в том, что это не лучшее использование вычислительного выражения, но я многому учусь :) - person Charles Lambert; 15.05.2011
comment
Кстати: построитель вычислений обычно не имеет частного состояния. Попытка спроектировать его таким образом, что частное (изменяемое) состояние не требуется, является хорошим упражнением, которое поможет вам понять, как должны работать компоновщики вычислений, а также поможет понять, какой дизайн лучше всего. Как правило, вы должны сначала разработать тип, созданный компоновщиками вычислений. Я писал об этом в главе 12 своей книги. Эта глава доступна в виде бесплатного образца: manning.com/petricek/SampleChapter12.pdf - person Tomas Petricek; 15.05.2011
comment
Эта книга уже есть в моем списке пожеланий на Amazon. Надеюсь, у меня будут деньги, чтобы купить его в июне. Очень приятно получить совет от человека с твоим уровнем знаний. Я ценю все это. - person Charles Lambert; 15.05.2011