fsprojects / SQLProvider

A general F# SQL database erasing type provider, supporting LINQ queries, schema exploration, individuals, CRUD operations and much more besides.
https://fsprojects.github.io/SQLProvider
Other
572 stars 146 forks source link

Unable to read connection string from app.config #625

Open allistercsmith opened 5 years ago

allistercsmith commented 5 years ago

Description

I'm attempting to specify the connection string to my DB in an app.config file in the root of my project, instead of hardcoding it in source. The type provider throws the following error:

No connection string specified or could not find a connection string with name DefaultConnectionString

I've read through the Utils.fs file, specifically the getConStringFromConfig function. I can see that line 235 reads match config.ConnectionStrings.ConnectionStrings.[connectionStringName]. Not sure if the double ConnectionStrings is intended? That said, I've tried a few variations of nesting in my app.config file with no luck.

Repro steps

  1. Download and extract the attached example
  2. cd to root directory of example
  3. docker-compose up
  4. dotnet restore
  5. dotnet build

Expected behavior

Successful connection to DB using connection string defined in app.config.

Actual behavior

The compiler throws the following error:

No connection string specified or could not find a connection string with name DefaultConnectionString

Known workarounds

  1. Hardcode the connection string in source
  2. Provide a connection string at runtime using .GetDataContext connString (though doesn’t help where compilation needs to happen across differing environments)

Related information

ConnectionStringTest.zip

Thorium commented 5 years ago

If you hard-code the connection string in source, you can still override it at runtime with .GetDataContext parameter. So e.g. if you want to encrypt your connection string in the app.config, you read it manually, decrypt it first and then pass to GetDataContext.

allistercsmith commented 5 years ago

Hey @Thorium 👋

Thanks for the feedback - I’ve added that to the list of known workarounds for reference.

Whilst that does somewhat help, it doesn’t solve for cases where your local environment differs from, for example, your build server. It may be difficult to enforce the same connection details everywhere the source needs to be compiled.

Do you have any thoughts on what the underlying issue may be? Otherwise, I’ll try clone and debug locally when I have some time this week.

Thorium commented 5 years ago

Yes, the whole app.config thing and ConfigurationManager etc weren't part of .NET Core at the time when I did the conversion to .NET Core (1.6 or so), so I just excluded the part with conditional compilation #if NETSTANDARD or something.

allistercsmith commented 5 years ago

Thanks for the feedback, @Thorium 🙂

Assuming this now works against .NET Core 2 (which I'm assuming is the minimum supported version based on the target frameworks here), would it be as simple as removing the conditional check around that code block then? Looks like it's around here. Please excuse any naivety - I'm fairly new to the F#/C#/.NET landscape.

If that sounds about right, I'll give this a go and report back.

Thorium commented 5 years ago

That is correct and would probably work. I don't use .NET Core on daily basis and the people who do, don't seem to use that feature, so no-one has done it. :-)

allistercsmith commented 5 years ago

Noted - thanks for the help! Will give it a go in the next few days 😎

retendo commented 5 years ago

Any news on this? @allistercsmith

allistercsmith commented 5 years ago

Hey @retendo 👋

I gave it a brief look back when I opened the issue, but struggled with build-related issues, namely with the inclusion of System.Configuration.ConfigurationManager in the .NET Core build.

I unfortunately don't have much time to investigate further, as I don't actively code in F# at the moment. I will try pick it up again when I have some time, but if someone else with a working build environment wants to give this a go, it may be a bit quicker 🙂

pawelwicher commented 4 years ago

Hi

This condtion IF !NETSATNDARD breaks using SQLProvider in .NET Core.

Please add possibility of using ConnectionStringName parameter, which value will be taken from appsettings.json

BR

Thorium commented 4 years ago

@pawelwicher PRs accepted. :-)

pawelwicher commented 4 years ago

Thanks

Thorium commented 4 years ago

.NET Core configuration seems to vary per framework version, with no standard values, what a mess: https://stackoverflow.com/questions/31453495/how-to-read-appsettings-values-from-json-file-in-asp-net-core

pawelwicher commented 4 years ago

yep, it's messy. I'm wandering if AppSettings type provider can read appsettings.json.

Anyway, as for now SqlProvider used in net core app / project is quite useless (if we want it to work outside local machines).

I posted a request (add connection string reading from appsettins.json) becouse after writeing some itegration tests i realized i can't set them on CI server.

I suggest to do it not using net core settings reading mechanism but simply by parsing this json and grabbing given connections string by ConnectionStringName

Thorium commented 4 years ago

GetDataContext still takes the connection string as a parameter.

So you can build as complex workflows to manage your connections as you ever want!

For example, I don't use AppSettings because I have my live environment connection strings encrypted with a key that is not stored at all in the development machine.

Another thing, you can write e.g. #if debug or other condition if you want to compile your application on CI. Some SQLProvider users use the off-line compilation in CI (ContextSchemaPath parameter), and don't have database access there at all.

pawelwicher commented 4 years ago

Ok, it can be done by preprocessor commands but its not a nice solution - we still hardcode something.

How to handle such scenario that on some another machine the code is compiled for the first time?

When working on machine and connectionString (expressed as constant - Literal attribute) that target existing and online db its obvious that type provider will discover db schema and will create types.

TheJayMann commented 4 years ago

Not sure if this would work for you, but, what I've started doing is hard coding a connection string for a development SQL server (one which would always be valid for any developer machine or CI build system), as that connection string is only used for building, while using the app settings to load the correct runtime connection string, passing in to the GetDataContext method.