How to force Entity Framework 6 to generate parameters of equivalent size to their corresponding database columns - entity-framework-6

I've created a database-first EDM for a table with columns encrypted using Always Encrypted. Our business prohibits direct access to tables, so I'm also using stored procedures for all database access.
When I try to insert row, I get the following error:
System.Data.SqlClient.SqlException: Operand type clash: varchar(8000)
encrypted with (encryption_type = 'RANDOMIZED',
encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256',
column_encryption_key_name = 'XXXXXX',
column_encryption_key_database_name = 'XXXXXX') collation_name =
'SQL_Latin1_General_CP1_CI_AS' is incompatible with varchar(60)
encrypted with (encryption_type = 'RANDOMIZED',
encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256',
column_encryption_key_name = 'XXXXXX',
column_encryption_key_database_name = 'XXXXXX') collation_name =
'SQL_Latin1_General_CP1_CI_AS
The stored procedure parameter is declared as varchar(60) which matches the table definition.
Because I'm using Always Encrypted, the parameter definition must match the column definition. However, EF generates a parameter declared as varchar(8000) for this property, which violates the encryption contract.
Is there any way to force EF to use the declared parameter length?

Resolved, see description here. In this particular scenario, the EDM contains two definitions of the stored procedure: one in the StorageModels element and one in the ConceptualModels element. Setting the MaxLength attribute in the StorageModels definition resolves the problem.

Related

SQLProvider Unable to cast object of type 'System.Guid' to type 'System.String'

I'm using SQLProvider for making a query in F# project. In my DB I have a column which is storing GUID - 'Id' char(36) NOT NULL. And when query is made I got an error:
Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.InvalidCastException: Unable to cast object of type 'System.Guid' to type 'System.String'.
I'm using MySqlConnector as a driver.
Here is how table looks in DB
CREATE TABLE `Comics` (
`Id` char(36) NOT NULL,
`Price` double DEFAULT NULL,
`Title` char(255) DEFAULT NULL,
`Image` char(255) DEFAULT NULL,
`Description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Here is types which was generated based on DB
Here is a query
let ctx = sql.GetDataContext()
let ComicsTable = ctx.ComicsShop.Comics;
let getById (id: string) =
query {
for c in ComicsTable do
where (c.Id = id)
select c.Id
} |> Seq.head
And usage
let result =
getById "0e49d94e-76c4-44be-b2ea-38382b297c78"
Console.WriteLine(result)
Thanks for your help.
After quite some time of investigation the issue was with DB.
My Id field has type as char(36) which is converted into string. But since that column contains GUID, SQLProvider will make a query assuming that field is Guid, and it will throw an error in Runtime.
The solutions is simple enough Id columns which is containing Guid should be typed as varchar(36) then all types will be converted correctly.

Can you run code before creating a type provider? (F#)

Say:
let x = // some operation
type t = SomeTypeProvider<x>
Is this valid?
No.
Since the types must be generated at compile-time, the parameter to the type provider needs to be a constant.
In other words, the code you marked // some operation can evaluate to a literal, but cannot be a value returned by a runnable function:
let arg = "foo"
type t = SomeTypeProvider<arg> // okay
let [<Literal>] arg = """{"name":"foo","value":42}"""
type t = SomeTypeProvider<arg> // okay
let arg = x.ToString()
type t = SomeTypeProvider<arg> // Boom! arg is not a Literal
It depends on your application, but one of the most common cases is the following:
You have a database-related Type Provider, and the connection string needs to be retrieved in runtime, from some sort of config file or something. So a developer mistakenly thinks they need a runnable code to retrieve the connection string first and then pass it to the Type Provider.
The correct approach is the following:
Keep two databases: one locally stored in a constant location (just for schema), and another one for the runtime purposes.
Pass the first one (a constant!) to your Type Provider. Don't worry about the hardcoded paths; it is only used for schema retrieval.
// Use a fixed sample file for schema generation only
type MyCSVData = CsvProvider<"dummy.csv">
// Load the actual data at runtime
let data = MyCSVData.Load(RetrieveFileNameFromConfig())

F# constructor with <"string", str>

In this article it shows how to use the SqlCommandProvider type. The sample code has this:
use cmd = new SqlCommandProvider<"
SELECT TOP(#topN) FirstName, LastName, SalesYTD
FROM Sales.vSalesPerson
WHERE CountryRegionName = #regionName AND SalesYTD > #salesMoreThan
ORDER BY SalesYTD
" , connectionString>(connectionString)
what does the <... ,...> before the type constructor name mean and why the the
first parameter have to be a string literal? It looks like a generic but it's taking variables not types. The constructor seems to be taking in a connection string already in the <> section.
The angle brackets are the configuration for a type.
In your example, you are defining a type and creating an instance at the same type. It's clearer when the steps are separated.
Define a type.
type SalesPersonQuery = SqlCommandProvider<query, connectionString>
But to actually have an instance of the type you have to create it:
let command = new SalesPersonQuery()
Now you can use the command.Execute() rather then SalesPersonQuery.Execute().
The reason there is a constructor is because later on (at run-time) you can change the connection string to a different then the one provided in the definition, so for instance:
let command = new SalesPersonQuery(differentConnectionString)
You can find that in the documentation in configuration section:
Connection string can be overridden at run-time via constructor optional parameter
First parameter can be a path to a SQL script or a SQL query. I suppose that's the reason it's a string: how else would you like to define a SQL query?
Again, from the documentation:
Command text (sql script) can be either literal or path to *.sql file

getting error updating the sqlite

I am doing the project and I am structed in database path. I am using the sqlite database for storing. In this my problems is when I updating the table it showing the error. For database part I am using prewritten classes. I am calling that class method whenever I need. See below you can understand.
This below code is working fine
[DataCachingHelper updateTable:#"sendertable" data:dic3 where:#"MESSAGE_ID='1234'"];
but when I am sending the object to the "where", It showing some error.
[DataCachingHelper updateTable:#"sendertable" data:dic3 where:#"MESSAGE_ID=%#",#"hai"];
i am getting the error:
"too many arguments to methods call expected 3,have 4".
here MESSAGE_ID is VARCHAR TYPE
The issue is clear here. You can not pass string format because compiler compiles parameter before converting into string format. From your method declaration allowed parameters must be 3.
So compiler detect 4 parameter as you pass string with format.
Also in sqlite database for VARCHAR type field use double quotes for field values.
So your string should be like this:
NSString *whereClauseString = [NSString stringWithFormat:#"MESSAGE_ID = \"%#\"",#"hai"];
Or if value is pre known simply create string like this:
NSString *whereClauseString = #"MESSAGE_ID = \"hai\"";
And then use this string as third parameter for updateTable method:
[DataCachingHelper updateTable:#"sendertable" data:dic3 where:whereClauseString];
make this in two step.
NSString *strWhere=[NSString stringWithFormat:#"MESSAGE_ID='%#'",#"hai"];
[DataCachingHelper updateTable:#"sendertable" data:dic3 where:strWhere];

F# Sql Type Provider: How to access

I am using a TypeProvider to access some data like this:
type internal SqlConnection =
SqlEntityConnection<ConnectionString =
#"XXXXXXXXXX">
type FooRepository () =
member x.GetFoo (Id) =
let context = SqlConnection.GetDataContext()
query {for foo in context.Foos do
where (foo.Id = Id)
select foo}
The problem is I am getting this:
The type 'FooRepository' is less accessible than the value, member or type 'member System.Linq.IQueryable' it is used in
I see the same problem asked on SO here.
Since I want to expose this type and I don't like the solutions on SO, I tried doing this:
SqlDataConnection<ConnectionString =
#"XXXXXXX">
The problem is that the database is on Azure Sql Server so I am getting this:
The type provider 'Microsoft.FSharp.Data.TypeProviders.DesignTime.DataProviders' reported an error: Error reading schema. Warning : SQM1012: Unable to extract table 'dbo.Foos from SqlServer.
Does anyone have an idea about how to get around this? One of the best things about using TPs is that I don't need to explicitly defines the types - but it looks like I am going to have to? Ugh
Are you sure the error you are getting is the one you posted here? My impression is that
the type which is less accessible should be the one from the generated database schema
(because you mark that as internal).
I tried reproducing your error by writing a simple library that uses the Northwind database:
open Microsoft.FSharp.Data.TypeProviders
type internal Nwind = SqlDataConnection<"Data Source=.\sqlexpress;Initial Catalog=Northwind;Integrated Security=True">
type NwindLibrary() =
let nw = Nwind.GetDataContext()
member this.Products = nw.Products
And I get the following message:
The type 'Products' is less accessible than the value, member or type 'member Class1.Products : System.Data.Linq.Table' it is used in C:\temp\Library1\Library1\Library1.fs 9 17 Library1
This is what I was expecting - because the Nwind type (generated by a provider) is marked as internal. However, the NwindLibrary type is public and the property Products (that I defined) returns a value of type IQueryable<Table<Nwind.ServiceTypes.Product>>. The compiler error is not particularly useful here (because it says that the type returns just Table), but the problem is that Nwind.ServiceTypes.Product is internal and so you cannot return it in a public property of a public type.
If you make the Nwind type public, then it should work fine. If you do not want to make the generated types public, then you'll have to wrap the returned values in your custom types. For example, even if you leave the type internal, the following works fine (returning a tuple instead of generated type):
member this.Products =
query { for p in mys.Products do
select (p.ProductID, p.ProductName) }

Resources