SqlEntityConnection configured with a local schema file - f#

I want to use the SqlEntityConnection type provider in f# to query and update a db.
It works well when I use it with the connection string pointing to a live SQL Server DataBase.
type EntityConnection = SqlEntityConnection<"Data Source=myServer;Initial Catalog=myDb;...", Pluralize=true>
Now I want to get rid of the dependency with the live DB and, instead, use a local schema file. Given what I read on msdn, I gave a try to the following line:
type private EntityConnection = SqlEntityConnection<LocalSchemaFile="mySchemaFile.ssdl", Pluralize=true>
Unfortunately, it doesn't compile and the compiler's message is:
Error 46 The type provider 'Microsoft.FSharp.Data.TypeProviders.DesignTime.DataProviders' reported an error: When using this provider you must specify either a connection string or a connection string name. To specify a connection string, use SqlEntityConnection<"...connection string...">.
So what should I do? If I leave the connection string, I have the feeling that I don't really turn off the dependency to the DB. For instance, if I try to switch the Data Source with a non-existing server, it doesn't compile.

You can provide a connection string name, along with a connection string in the configuration file.
You can still provide a localschemafile that will be used to cache locally the schema.
If you put the schema file under source control, the connection string will only be used as a default when calling GetDataContext() without parameter, but not when building or editing code.
You also need to set the parameter ForceUpdate to False.

Related

Error inserting data with EF6 and Always encrypted

We are experiencing some issues with EF6 and Always encrypted feature.
I believe we need to set up something into DBContext, in order to instruct how to encrypt or decrypt columns, but I couldn't find a way to do this.
We already have an ADO access layer, and it works perfectly with encrypted fields. We would rather use EF instead of ADO.
Symptoms are:
With EF, We are able to query the data. And decryption process works fine.
Insertion process throws error below:
Operand type clash: varchar is incompatible with varchar(8000) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'Development_v2_qa') collation_name = 'SQL_Latin1_General_CP1_CI_AS'**
Query with where clause, using an encrypted field, throws same error.
Technologies used:
EF6 with Poco entities.
AzureKeyVault for storing encryp/decryp masterkey.
Using SSL Certidicate to authenticate against KeyVault
Connection string contains "Column Encryption Setting=enabled;"
AzureSqlServer
FWK4.6
ADO
We have some code which works fine with ADO. It works fine with every SqlConnection
// Instantiate our custom AKV column master key provider.
// It uses the GetToken function as the callback function to authenticate to AKV
SqlColumnEncryptionAzureKeyVaultProvider akvprov = new SqlColumnEncryptionAzureKeyVaultProvider();
akvprov.KeyVaultClient = SecureConfigurationManager.KeyVaultClient;
// Register the instance of custom provider to SqlConnection
Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
// "SqlColumnEncryptionAzureKeyVaultProvider.ProviderName" is the name of the provider. It must match the string we used when we created the column master key
providers.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, akvprov);
SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);
Yep, I just found the same problem, needed to add
[Column(TypeName = "varchar(max)")]
in the POCO type before the field for it to work.
Be nice if the error was a bit clearer (and nicer still if NVARCHAR did actually work)
I'm working through the same issue. The problem is with the datatype mapping from C# to the database. Not all the lengths matter at all for always encrypted and varchars with Entity Framework, only varchar(max) or varchar(8000). I have all the entity framework working with azure key vault for all the datatypes, same as you. This link below shows how to do the insert with inline SQL. I've only worked with entity framework and hope I never have to work in inline sql, even though I might have to, if I can't find a way to shrink down the database storage overhead needed for encryption, or look to something like Stretch Db, also another feature in SQL Server 2016. Thanks Jakub Szymaszek and Microsoft.
I have conceded and made all of my data types varchar(max) and it works just fine. So string = varchar(max). The weird thing is that there is not 8000 characters in the encryption, but there is probably 8000 allocated.
"something1" becomes this after encryption and insert:
0x0190F9D80C3F70890FB154F2123459506AD5BDA165333710D161ED80E42FCAFA882C66FF5B68E412B5F9EE11A9F308201D0AE2BD4032151398171FDBE2F3AEA20D
Interesting thing about varchar(max) is that supposidly there is a link to a table or somewhere else the data is stored, beside the table it is inserted into, so varchar(max) may only take the amount shown. (I'm a dev)
The dataType for my column and stored procedure variables:
[testVarChar] varchar COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
The data type of the parameter targeting the SSN column is set to an ANSI (non-Unicode) string, which maps to the char/varchar SQL Server data type. If the type of the parameter was set to a Unicode string (String), which maps to nchar/nvarchar, the query would fail, as Always Encrypted does not support conversions from encrypted nchar/nvarchar values to encrypted char/varchar values. See SQL Server Data Type Mappings for information about the data type mappings.
https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/develop-using-always-encrypted-with-net-framework-data-provider

SSIS Foreach Loop Datasource

I am trying to apply a sql query on different databases on several servers but I have an error when thrying to connect with dynamic connection manager datasource. Workflow :
I am able to iterate through the foreach but I am not able to apply the query to all the Server/DB configurations.
I am using a dynamic datasource set by variable in an expression.
My connection string is strctured like this : Data Source=SrvName;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;Initial Catalog=DB1;User Id=Usr;Password=Pwd;
My "Source" in connection manager is set with a ConnectionString Property with the whole connection string contained in my ConStr Variable as expression (#[User::ConStr]).
Error message is : Failed to acquire connection "Source" conenction may not be configured correctly or you may not have the right permissions ...
I don't know why I am not able to connect to apply the query to the different databases. Thanks in advance for your help.

Passing user name and password parameters to the type provider

I have created a type provider to a service that requires authentication by user name and password. The signature for creating the typed instance is something like this:
let tbl = new DemoTable<TableId="demo",User="user",Password="password">()
It all works just fine, the only problem is that I want to demonstrate this code, so I'd like the user name and password to come from some other source - a configuration file, another F# module (good approach when using scripting), etc.
What are my options for passing these parameters so they are not exposed in the course of demonstration?
One simple way is to use literals in a file that you don't show:
module MyInfo =
[<Literal>]
let user = "user name here"
[<Literal>]
let pwd = "password here"
And then in the file that you do show:
let tbl = new DemoTable<TableId="demo",User=MyInfo.user,Password=MyInfo.pwd>()
Specifying the user name and password directly as static parameters is certainly useful in interactive scripting scenarios. But as you say, sometimes it is also useful to keep those in a separate configuration file. F# does not come with any library that would make this easier, but you can implement the same pattern as, for example, the SqlDataConnection type provider which provides both options (see MSDN docs).
You can either specify the whole connection string:
type DB = SqlDataConnection<ConnectionString="Source=.; User=me; Password=mysecret">
or can either specify local config file and key name:
type DB = SqlDataConnection<ConnectionStringName="LocalDb", ConfigFile="App.config">
This is something that you'd have to implement in your type provider, but it seems like the best way to support both scenarios.
Change password on database (or make temporary user)->compile project->rechange password (or delete user).

Breeze with second controller

I need to get some data from a different database (different server, actually), so I've created a second datacontext pointing to a second Breeze controller, repository, and edmx. I also created second model and entityManagerFactory since data I'll need to be getting is substantial and wanted to separate it from the base functionality. In my new EntityManagerFactory, if I have:
breeze.NamingConvention.none.setAsDefault();
all works well, but I don't get camel casing. If I have:
breeze.NamingConvention.camelCase.setAsDefault();
or if I don't call it at all (since it's just setting the app-wide default which is already set) I get this error:
[myDatacontext] [HT Error] Error retrieving dataMetadata query failed for: breeze/Vsp/Metadata. Unable to either parse or import metadata: NamingConvention for this server property name does not roundtrip properly:name-->Name; [object Object]Error: Metadata query failed for: breeze/Vsp/Metadata. Unable to either parse or import metadata: NamingConvention for this server property name does not roundtrip properly:name-->Name; [object Object]
There's not even a "name" or "Name" property on class I'm getting.
What am I missing?
By the way, I do get the metadata from the server. The client just doesn't like it.
I suppose that since you've posted this you found a solution.
But I also had this problem, it is because I use Entity-Framework ADO .Net 6.0, it takes into account stored procedure, views or tables that are not camelcase, so you have to unselect them before generating the model / context.

Type provider: How to regenerate?

How would you make the LINQ-TO-SQL type provider, generate or/and regenerate the classes?
I've just added a new table to my database, and the type provider can't figure that out. I've tried to delete the line with the type provider, and type it once more - no luck. I've also tried to do a rebuild.. still no luck.
Edit:
I've defined the type provider like:
[<Generate>]
type dbSchema = SqlDataConnection<"conString">
and using it like:
let ctx = dbSchema.GetDataContext()
You're right - this seems to be quite tricky. I'm using SqlDataConnection type provider in a script file and the only way to update the schema that I've found so far is to make some minor (irrelevant) change in the connection string. For example, add space after = of one of the parameters:
[<Generate>]
type Northwind = TypeProviders.SqlDataConnection
<"data source=.\\sqlexpress;initial catalog=Northwind;integrated security=True">
[<Generate>]
type Northwind = TypeProviders.SqlDataConnection
<"data source=.\\sqlexpress;initial catalog=Northwind;integrated security= True">
// ^ here
The schema seems to be cached using connection string as the key, so if you change it back, you get the old schema again. I guess this is probably a bug, so adding whitespace is a possible workaround.
There is also a parameter ForceUpdate, but that doesn't seem to have any effect and the documentation doesn't say much about it.

Resources