I'm getting the error Procedure or function 'getfoo2' expects parameter '@x', which was not supplied.
When I set a breakpoint on line 156 in File1.fs and examine the contents of foo2, which is my DbCommand object, the parameters collection contains both of my parameters @x and @y. They both have the correct DbType and value set. So I have no idea where to look to find my problem. Is this a bug, or am I missing something somewhere else in my code? I have posted the sql for the database, my f@ File1.fs, Program.fs, and the app.config. File1.fs comes before Program.fs in the project.
My system is:
- Microsoft SQL Server Developer Edition (64-bit) version 10.0.4000.0
- Windows 7 Professional SP1
- Visual Studio 2010 SP1
Below is the source code:
stored proc, table, and sample data:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[getfoo2]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[getfoo2]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[getfoo2]
@x int,
@y varchar(15)
AS
BEGIN
SET NOCOUNT ON;
select x,y,z from foo
where
x = @x
and
y = @y
END
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[foo]') AND type in (N'U'))
DROP TABLE [dbo].[foo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[foo](
[x] [int] NOT NULL,
[y] [varchar](15) NOT NULL,
[z] [datetime] NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
insert into foo (x,y,z) values (1,'a',NULL)
insert into foo (x,y,z) values (1,'b','Jan 1 2001 12:00AM')
insert into foo (x,y,z) values (1,'c','Jan 2 2002 12:00AM')
insert into foo (x,y,z) values (2,'a','Feb 1 2001 12:00AM')
insert into foo (x,y,z) values (2,'b',NULL)
insert into foo (x,y,z) values (2,'c','Feb 2 2001 12:00AM')
insert into foo (x,y,z) values (3,'a','Mar 1 2001 12:00AM')
insert into foo (x,y,z) values (3,'b','Mar 2 2001 12:00AM')
insert into foo (x,y,z) values (3,'c',NULL)
GO
File1.fs
module File1
open System.Configuration
open System.Data.Common
open System.Data
type Direction =
| In
| Out
| Ref
| Return
type DbType =
| AnsiString of int
| AnsiStringFixedLength of int
| Binary of int
| Boolean
| Byte
| Currency
| Date
| DateTime
| DateTime2
| DateTimeOffset
| Decimal
| Double
| Guid
| Int16
| Int32
| Int64
| Object of int
| SByte
| Single
| String of int
| StringFixedLength of int
| Time
| UInt16
| UInt32
| UInt64
| VarNumeric of int
| Xml of int
type Db(cnnName:string) =
let config = ConfigurationManager.ConnectionStrings.[cnnName]
let factory = System.Data.Common.DbProviderFactories.GetFactory(config.ProviderName)
let (|HasSize|NoSize|) p =
match p with
// no size
| Boolean -> NoSize(System.Data.DbType.Boolean)
| Byte -> NoSize(System.Data.DbType.Byte)
| Currency -> NoSize(System.Data.DbType.Currency)
| Date -> NoSize(System.Data.DbType.Date)
| DateTime -> NoSize开发者_如何学运维(System.Data.DbType.DateTime)
| DateTime2 -> NoSize(System.Data.DbType.DateTime2)
| DateTimeOffset -> NoSize(System.Data.DbType.DateTimeOffset)
| Decimal -> NoSize(System.Data.DbType.Decimal)
| Double -> NoSize(System.Data.DbType.Double)
| Guid -> NoSize(System.Data.DbType.Guid)
| Int16 -> NoSize(System.Data.DbType.Int16)
| Int32 -> NoSize(System.Data.DbType.Int32)
| Int64 -> NoSize(System.Data.DbType.Int64)
| SByte -> NoSize(System.Data.DbType.SByte)
| Single -> NoSize(System.Data.DbType.Single)
| Time -> NoSize(System.Data.DbType.Time)
| UInt16 -> NoSize(System.Data.DbType.UInt16)
| UInt32 -> NoSize(System.Data.DbType.UInt32)
| UInt64 -> NoSize(System.Data.DbType.UInt64)
// has size
| AnsiString(x) -> HasSize(System.Data.DbType.AnsiString,x)
| AnsiStringFixedLength(x) -> HasSize(System.Data.DbType.AnsiStringFixedLength,x)
| Binary(x) -> HasSize(System.Data.DbType.Binary,x)
| Object(x) -> HasSize(System.Data.DbType.Object,x)
| String(x) -> HasSize(System.Data.DbType.String,x)
| StringFixedLength(x) -> HasSize(System.Data.DbType.StringFixedLength,x)
| VarNumeric(x) -> HasSize(System.Data.DbType.VarNumeric,x)
| Xml(x) -> HasSize(System.Data.DbType.Xml,x)
let dbDir (p:Direction) =
match p with
| In -> System.Data.ParameterDirection.Input
| Out -> System.Data.ParameterDirection.Output
| Ref -> System.Data.ParameterDirection.InputOutput
| Return -> System.Data.ParameterDirection.ReturnValue
member x.CreateProcedure(name) =
let cmd = factory.CreateCommand()
let cn = factory.CreateConnection()
cn.ConnectionString <- config.ConnectionString
cmd.Connection <- cn
cmd.CommandText <- name
cmd
member x.CreateParameter(name:string,typ:DbType,dir:Direction) =
let p = factory.CreateParameter()
if name.StartsWith("@") then
p.ParameterName <- name
else
p.ParameterName <- "@" + name
p.Direction <- dbDir dir
match typ with
| HasSize(t,s) ->
p.DbType <- t
p.Size <- s
| NoSize(t) -> p.DbType <- t
p
type Foo() =
let mutable x:int = 0
let mutable y:string = ""
let mutable z:option<System.DateTime> = None
member a.X with get() = x and set n = x <- n
member a.Y with get() = y and set n = y <- n
member a.Z with get() = z and set n = z <- n
let db = Db("db")
let proc name (parameters:list<string*DbType*Direction>) =
let cmd = db.CreateProcedure(name)
let param p =
db.CreateParameter p
|> cmd.Parameters.Add
|> ignore
List.iter param parameters
cmd
let (?<-) (cmd:DbCommand) (s:string) (value:'a) =
cmd.Parameters.["@" + s].Value <- value
let (<|>) (value:option<'a>) (replacement:'a) =
match value with
| Some(x) -> x
| _ -> replacement
let (?) (r:DbDataReader) (s:string) : option<'a> =
let index = r.GetOrdinal s
match r.IsDBNull index with
| true -> None
| _ -> r.GetValue index
:?> 'a
|> Some
let foo x y =
let foo2 = proc "getfoo2"
<| [ ("x",Int32,In);
("y",String(15),In) ]
foo2?x <- x
foo2?y <- y
try
foo2.Connection.Open()
use r = foo2.ExecuteReader()
[
while r.Read() do
let item = Foo()
item.X <- (r?x) <|> 1
item.Y <- (r?y) <|> ""
item.Z <- r?z
yield item
]
finally
foo2.Connection.Close()
Program.fs
open System
open System.Data
open System.Data.Common
open System.Configuration
open File1
let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)
[<EntryPoint>]
let main (args : string[]) =
let foo1a = foo 1 "a"
let foo1b = foo 1 "b"
let foo1c = foo 1 "c"
for f in foo1a do
let mutable z = DateTime.Now
match f.Z with
| Some(x) -> z <- x
| None -> z <- DateTime.MinValue
printfn "%d : %s : %O" f.X f.Y z
// program exit code
0
app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="db" providerName="System.Data.SqlClient" connectionString="server=(local);uid=;pwd=;Trusted_Connection=yes;database=scratchPad"/>
</connectionStrings>
</configuration>
(Reposting from comment)
Your code looks generally fine to me, although you may want to explicitly set cmd.CommandType
to CommandType.StoredProcedure
inside of CreateProcedure
.
精彩评论