I'm building a Web API in F# to test some things out. I come from a C# background and I've structured things how I do there.
You can find the repo here: https://github.com/MrGodlike6/WebApiTest
I've referenced the WebApiTest.Repositories.SqlServer project in WebApiTest.WebApi but for some reason Visual studio says it can't find the WebApiTest.Repositories.SqlServer namespace.
The thing is that the solution compiles fine and I can run the application, it's just that I don't have IntelliSense for anything coming from the SqlServer project (e.g. in the CompositionRoot module).
I've tried a lot of things, from different DBs, to making a .NET Framework 4.7.2 project for the DB project. So far nothing works. Tried both Nuget & Paket and no difference. Tried VS 2019. In VS 2022 it gives some compile error that is unrelated.
I used dotPeek from JetBrains to look at the resulting .dll and the namespace & module are there as expected. Don't know what could be causing this.
Also can you indicate if the project/folder structure that I use is good for F#? Any other suggestions are also welcome.
EDIT: The SQL part is based of https://github.com/isaacabraham/get-programming-fsharp/tree/master/src/code-listings/lesson-35. Here everything works, although the SQL code is not put in a separate project.
I've commented the SqlEnumProvider and I no longer have problems in the composition root. I'll look a little closer at this provider and see if I can come up with something.
EDIT2: Created a issue on GitHub: https://github.com/fsprojects/FSharp.Data.SqlClient/issues/410
-
1Do you have SQL to populate the database? I can't compile your solution because the type providers in the WebApiTest.Repositories.SqlServer don't have anything to work on.Piotr Rodak– Piotr Rodak09/16/2021 19:46:24Commented Sep 16, 2021 at 19:46
-
Ok, so the type of OperationId in the relevant tables in the repo is integer. Can you check if you have correct types in your setup?Piotr Rodak– Piotr Rodak09/17/2021 21:30:17Commented Sep 17, 2021 at 21:30
-
I tried both the DB created by the scripts in the book's repo (localdb) and creating a new DB in docker that has the same schema and no luck. And the SqlEnumProvider code is the same as in the book repo.say-toshi– say-toshi09/19/2021 11:20:57Commented Sep 19, 2021 at 11:20
2 Answers 2
I've reproduced what you're seeing. The problem appears to be caused by a design-time error in the FSharp.Data type provider. In particular, SqlEnumProvider
seems to be the source. When I remove the DbOperations
type from AccountRepository.fs (using plain strings instead of enum values), I'm able to get Intellisense for WebApiTest.Repositories.SqlServer
in CompositionRoot.fs.
Here's my kludgy workaround:
[<AutoOpen>]
module private DB =
let [<Literal>] Conn = @"Data Source=.;Database=BankAccountDb;Integrated Security=True;Connect Timeout=60"
type AccountsDb = SqlProgrammabilityProvider<Conn>
type GetAccountId = SqlCommandProvider<"SELECT TOP 1 AccountId FROM dbo.Account WHERE Owner = @owner", Conn, SingleRow = true>
type FindTransactions = SqlCommandProvider<"SELECT Timestamp, OperationId, Amount FROM dbo.AccountTransaction WHERE AccountId = @accountId", Conn>
type FindTransactionsByOwner = SqlCommandProvider<"SELECT a.AccountId, at.Timestamp, at.OperationId, at.Amount FROM dbo.Account a LEFT JOIN dbo.AccountTransaction at on a.AccountId = at.AccountId WHERE Owner = @owner", Conn>
// type DbOperations = SqlEnumProvider<"SELECT Description, OperationId FROM dbo.Operation", Conn>
let toBankOperation operationId =
match operationId with
| "Deposit" -> Deposit
| "Withdraw" -> Withdraw
| _ -> failwith "Unknown operation!"
let fromBankOperation bankOperation =
match bankOperation with
| Deposit -> "Deposit"
| Withdraw -> "Withdraw"
I searched for an existing issue on GitHub, but didn't find one, so you might want to open a new one yourself.
If anyone else wants to try this, here's the dummy database schema I used to get things to compile:
USE [BankAccountDb]
GO
/****** Object: Table [dbo].[Account] Script Date: 9/16/2021 11:40:48 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Account](
[Owner] [nchar](10) NOT NULL,
[AccountId] [uniqueidentifier] NOT NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[AccountTransaction] Script Date: 9/16/2021 11:40:48 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AccountTransaction](
[AccountId] [uniqueidentifier] NOT NULL,
[Timestamp] [datetime] NOT NULL,
[OperationId] [nchar](10) NOT NULL,
[Amount] [money] NOT NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Operation] Script Date: 9/16/2021 11:40:48 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Operation](
[Description] [nvarchar](100) NULL,
[OperationId] [nvarchar](100) NULL
) ON [PRIMARY]
GO
Thank you Brian for the script.
As the documentation says,
"An enumeration type (or enum type) is a value type defined by a set of named constants of the underlying integral numeric type."
I changed the OperationId
column data type to int in dbo.Operation
and dbo.AccountTransaction
and populated the table dbo.Operation, because the SqlEnumProvider
complained about lack of data during the compilation.
CREATE TABLE [dbo].[Operation] (
[Description] NVARCHAR (100) NULL,
[OperationId] INT NOT NULL
);
CREATE TABLE [dbo].[AccountTransaction] (
[AccountId] UNIQUEIDENTIFIER NOT NULL,
[Timestamp] DATETIME NOT NULL,
[OperationId] INT NOT NULL,
[Amount] MONEY NOT NULL
);
insert dbo.Operation (Description, OperationId) values ('Deposit', 1), ('Withdraw', 2)
Now I can compile the solution.