F#. How to pass SQLite connection string to EF - entity-framework

I am trying to make SQLite work with EF6 and F3 using System.Data.Sqlite.
I am following walkthrough described here and got edmx file and C# program to compile (I've done that before).
Walkthrough: Generating F# Types from an EDMX Schema File (F#)
Default code shown no errors during the design time and table structure is picked up by VS. Basically, edmx file is ok.
type internal edmx = EdmxFile<"ModelK.edmx">
[<EntryPoint>]
let main argv =
let context = new edmx.MyBaseModel.MyBaseEntities(strBulder)
query { for course in context.T1 do
select course.r1 }
|> Seq.iter (fun course -> printfn "%s" course)
printfn "%A" argv
0 // return an integer exit code
I am using NuGet package System.Data.SQLite EF6 1.0.94.0, deps EntityFramework 6.1.1, System.Data.SQLite Core 1.0.94.0 and have bundle installed from SQLite side (one with VS2013 support).
My problem is sql connecion string. EF requires connection string with meta and the regular sting doesn't work.
This is my original connection string from designer.
#"metadata=res://*/ModelK.csdl|res://*/ModelK.ssdl|res://*/ModelK.msl;provider=System.Data.SQLite.EF6;provider connection string="data source=D:\VS2013\FSharp940\FSharp940MyBase.db"" providerName="System.Data.EntityClient""
What I've tried so far:
Passing escaped (" to make a valid string and some other escape seqs) to MyBaseEntities(strBulder).
System.ArgumentException was unhandled Message: An unhandled exception
of type 'System.ArgumentException' occurred in System.Data.Entity.dll
Additional information: keyword not supported: data source.
Followed some SO advice and replaced " with '.
Got Error reading schema, must start with / and be divided by :.
The type provider
'Microsoft.FSharp.Data.TypeProviders.DesignTime.DataProviders'
reported an error: Error reading schema. fel 7005: Parametern
source=D:\VS2013\FSharp940\FSharp940\MyBase.db har angetts p† ett
felaktigt s„tt. Alla v„xlar m†ste b”rja med tecknet / och namnet m†ste
avgr„nsas fr†n v„rdet med kolon ':'.
Taken my C# App.Config entity connection strings and other things to my F# App.Config and reading from there.
type internal FooDb =
SqlEntityConnection<ConnectionStringName="MyBaseEntities",
ConfigFile="App.config">
Nope.
The type provider
'Microsoft.FSharp.Data.TypeProviders.DesignTime.DataProviders'
reported an error: Error reading schema. fel 7005: Parametern
source=D:\VS2013\FSharp940\FSharp940\MyBase.db
I do understand that it is complaining about filepath and done some experiments with it without any results
The type provider
'Microsoft.FSharp.Data.TypeProviders.DesignTime.DataProviders'
reported an error: Error reading schema. fel 7001: keywork st”ds not:
metadata.
Finally I tried EntityConnectionStringBuilder.
let strBulder =
let conn = new EntityConnectionStringBuilder()
conn.Metadata <- #"res://*/ModelK.csdl|res://*/ModelK.ssdl|res://*/ModelK.msl;"
conn.Provider <- #"System.Data.EntityClient"
conn.ConnectionString <- #"D:\VS2013\FSharp940\FSharp940\MyBase.db"
let ec = new EntityConnection(conn.ToString())
ec
New one...
An unhandled exception of type 'System.TypeInitializationException'
occurred in Unknown Module.
Everything I write to conn.ConnectionString throws an exception, including MyBase.db without any path.
I see that the problem is in connection string and probably my filepath, but at the moment have no idea what the correct format is. I mean... EF must understand it's own config file, right?
Any ideas? :)

Related

.NET Entity Framework Core 3.1 - Nlog leaking memory when :format=# when calling update on a big model and a db error occurs

Happens in the save below:
context.Update(myBigObject);
await context.SaveChangesAsync(); -> Hangs here and RAM goes up until all the memory is finished
It only happens when a db error occurs ("String or binary data would be truncated" to be exact, meaning trying to stuff too large string in a field that's too small).
It happens when nlog config is:
${exception:format=#}
One "fix" is to change it to:
${exception:format=toString} -> But then I lose all the inner exception logging
See nlog docs on the difference between :format=# and :format=toString:
https://github.com/NLog/NLog/wiki/Exception-Layout-Renderer
It's happening to more people than me (see bottom comment) and happening in both Serilog and Nlog (so maby it's a EF Core thing):
https://github.com/dotnet/efcore/issues/24663#issuecomment-1349965403
Any idea how to fix without using :format=toString in nlog config?
NLog 4.7 allows you to override the reflection for a specific exception-type (Ex. Microsoft.EntityFrameworkCore.DbUpdateException) like this:
LogManager.Setup().SetupSerialization(s =>
s.RegisterObjectTransformation<Microsoft.EntityFrameworkCore.DbUpdateException>(ex => new {
Type = ex.GetType().ToString(),
Message = ex.Message,
StackTrace = ex.StackTrace,
Source = ex.Source,
InnerException = ex.InnerException,
})
);
See also: https://github.com/NLog/NLog/wiki/How-to-use-structured-logging#customize-object-reflection

Data driven unit test breaking entity framework connection

I have an application that uses entity framework. I am writing a unit test in which I would like to use data driven testing from a CSV file.
However, when I run the test, I get an error that the sqlserver provider cannot be loaded:
Initialization method UnitTest.CalculationTest.MyTestInitialize threw
exception. System.InvalidOperationException:
System.InvalidOperationException: The Entity Framework provider type
'System.Data.Entity.SqlServer.SqlProviderServices,
EntityFramework.SqlServer' registered in the application config file
for the ADO.NET provider with invariant name 'System.Data.SqlClient'
could not be loaded. Make sure that the assembly-qualified name is
used and that the assembly is available to the running application.
If I remove the data driven aspects and just test a single value, then the test works.
If I just use the data driven aspects and remove the Entity Framework stuff, then the test works.
So, its only when I try to use data driven test with entity framework active at the same time do I get the error. So, where am I going wrong here?
Here's my test method:
[TestMethod, TestCategory("Calculations")
, DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV"
, "ConvertedMeanProfileDepth.csv", "ConvertedMeanProfileDepth#csv"
, Microsoft.VisualStudio.TestTools.UnitTesting.DataAccessMethod.Sequential)
, DeploymentItem("ConvertedMeanProfileDepth.csv")]
public void ConvertedMeanProfileDepthTest()
{
ConvertedMeanProfileDepth target = new ConvertedMeanProfileDepth();
Decimal mpd = decimal.Parse(this.TestContext.DataRow["mpd"].ToString());
Decimal expected = decimal.Parse(this.TestContext.DataRow["converted"].ToString());
Decimal actual;
actual = target.Calculate(mpd);
Assert.AreEqual(expected, actual);
}
So I managed to work it out in the end. For future reference, here's the solution:
Rob Lang's post, Entity Framework upgrade to 6 configuration and nuget magic, reminded me of the issue here:
When a type cannot be loaded for a DLL that is referenced in a
project, it usually means that it has not been copied to the output
bin/ directory. When you're not using a type from a referenced
library, it will not be copied.
And this will raise its ugly head the moment you use deployment items in your tests. If you use a deployment item in your test, then all of the required binaries are copied to the deployment directory. Problem is, if you are using dynamically loaded items, then the test suite does not know it has to copy those items.
With Entity Framework, this means that your providers will not be copied to the deployment location and you will receive the error as per my question.
To resolve the issue, simply ensure that your entity framework provider is also marked as a deployment item.
So, note the inclusion of DeploymentItem(#"EntityFramework.SqlServer.dll") in my test attributes. All works perfectly from here:
[TestMethod, TestCategory("Calculations")
, DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV"
, "ConvertedMeanProfileDepth.csv", "ConvertedMeanProfileDepth#csv"
, Microsoft.VisualStudio.TestTools.UnitTesting.DataAccessMethod.Sequential)
, DeploymentItem("ConvertedMeanProfileDepth.csv")
, DeploymentItem(#"EntityFramework.SqlServer.dll")]
public void ConvertedMeanProfileDepthTest()
{
ConvertedMeanProfileDepth target = new ConvertedMeanProfileDepth();
Decimal mpd = decimal.Parse(this.TestContext.DataRow["mpd"].ToString());
Decimal expected = decimal.Parse(this.TestContext.DataRow["converted"].ToString());
Decimal actual;
actual = target.Calculate(mpd);
Assert.AreEqual(expected, actual);
}

Entity Framework ConnectionString Parsing Exception

I can't find how my connectionString syntax is wrong. Can anyone suggest a way to figure this out? I am having a difficulty using EF with my connection string. I am new to EF.
I am using Sybase Anywhere 12 database.
I'm using the Table-First ObjectContext with EDMX in a separate class library refenced by a web application.
I'm using a Ninject Module in my class library to bind my repositories.
I'm using a ODBC DataStore called "Test"
Other information EF 4.3.1.0, .NET 4, VS2010
My main web application web.config has the EF connection string copied to it as:
<connectionStrings>
<add name="Entities"connectionString="metadata=res://*/MyEntities.csdl|res://*/MyEntities.ssdl|res://*/MyEntities.msl;provider=iAnywhere.Data.SQLAnywhere;provider connection string="UserID=aUser;Password=aPassword;DataSourceName=Test"" providerName="iAnywhere.Data.SQLAnywhere"/>
</connectionStrings>
When I initialize my Entity/ObjectContext in my Repository (see using statement below) it returns an error: "The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid."
using (var context = new Entities())
{
return {Linq to Entity here}
}
I turned on CLR exceptions on the debugger and found the code throws the error in the .NET Framework here:
EntityConnection.cs
effectiveConnectionOptions = new DbConnectionOptions(setting.ConnectionString, EntityConnectionStringBuilder.Synonyms, false);
edmx designer generated:
/// <summary>
/// Initializes a new Entities object using the connection string found in the 'Entities' section of the application configuration file.
/// </summary>
public Entities() : base("name=Entities", "Entities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
I can see my connection string there, so it is having a difficult time parsing the connectionString. I have tried many different permutations of syntax and haven't found anything it accepts including:
Explicitly naming the assembly for entity files instead of a wildcard(e.g. metadata=res://MyDomain/MyEntities.csdl...)
Using Sybase friendly ODBC attributes such as UID instead of UserID, PWD instead of Password, and DBN instead of DataSourceName.
Thanks.
I got everything working and the only reason I can think of is that I deleted my ASP .NET 4.0 temp files. Also, I must add I changed my process from using an integration test to test this piece to using a unit test. I did not do unit tests first, because our build server does not have a database on it.
Once I was able to prove that it was working there, I decided to delete my temp files. After that, everything started working properly. So, some sort of cache issue was occurring in my application. I used the same connectionString that I mentioned above.
Actually, I used the domain name of the Metadata "metadata=res://MyDomain/MyEntitities.csdl" rather than */MyEntities.csdl. I don't plan on changing the domain any time soon. In fact, that is just what may have caused some of the issue, because I had changed the location, name, and namespace of MyEntities.Domain where the EF was.

Entity Framework issue when creating unit tests

I am specifically getting the following error:
"The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid."
[TestMethod()]
public void salesOrderFillListTest()
{
SalesOrderController_Accessor target = new SalesOrderController_Accessor();
string orderNumber = "1954120";
SalesOrderData result;
result = target.FillingOrder(orderNumber);
Assert.AreEqual(null, result.ErrorMessage);
Assert.AreEqual(32, result.LineItems.Count);
Assert.AreEqual("WRA-24-NFL-CLEV", result.LineItems[7].ItemNumber);
Assert.AreEqual(2, result.LineItems[7].OrderQuantity);
Assert.AreEqual(1, result.LineItems[7].FillingFilledQty);
Assert.AreEqual(1, result.LineItems[7].FillingRemainQty);
}
The error is coming up on the line:
result = target.FillingOrder(orderNumber);
I'm a junior developer and haven't had much experience with the many possible causes for this error. My App.config page contains the appropriate connection strings. Any ideas where to look for this one?
Thanks!
I was able to successfully run tests found in another test project in the same solution. After looking more carefully I found that there were a few differences between the two connection strings in each project. The data in the failing test project had become antiquated. I updated the connection string and error resolved.
Thanks.

Is it possible to retrieve connection string inside DDL generation template in VS2010?

I am playing around with creating a T4 template for the "DDL Generation Template option" (model first) process in Visual Studio 2010 RC. Is it possible to retrieve the connection string that is associated with that process? If I right click on the .edmx file and choose "Generate Database from Model..." I have the option of choosing a data connection. That connection string is saved to the app.config (assuming that the option is checked). So I am wondering if it is possible to retrieve that connection string inside the T4 template. I would like to generate different information from the template based on the connection string.
More generally, is it possible to get any context information in this situation? So far, the only thing I have successfully retrieved is the .NET data provider name.
Note - I have studied the ideas provided by Craig but am only getting the name of the IDE (devenv.exe), which quite possibly means I am just doing something wrong.
In case this helps anyone else, here is a snippet I created to read the Entity Framework connection string from inside T4. You pass it the model name (which is also the name of the connection string). It finds and parses just the connection bit I need. It also throws helpful errors when it does not succeed.
To use:
A. Paste this at the top of your template if you aren't already referencing these assemblies:
<## assembly name="EnvDTE" #>
<## assembly name="System.Configuration" #>
B. Paste this ugly (but compact) code at the end of your template:
<#+
string GetEFConnectionString(string modelName)
{
string file = null, key = "provider connection string=\"";
foreach (EnvDTE.ProjectItem item in ((EnvDTE.Project)((Array)((EnvDTE.DTE)((IServiceProvider)this.Host).GetService(typeof(EnvDTE.DTE))).ActiveSolutionProjects).GetValue(0)).ProjectItems)
if (System.Text.RegularExpressions.Regex.IsMatch(item.Name, "(app|web).config", System.Text.RegularExpressions.RegexOptions.IgnoreCase)) {
file = item.get_FileNames(0); break;
}
if (file == null) throw new Exception("config file could not be found");
var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(new System.Configuration.ExeConfigurationFileMap() { ExeConfigFilename = file }, System.Configuration.ConfigurationUserLevel.None);
var cn = config.ConnectionStrings.ConnectionStrings[modelName];
if (cn == null) throw new Exception(modelName + " connection string could not be found");
string s = cn.ConnectionString;
int pos = s.IndexOf(key,StringComparison.OrdinalIgnoreCase);
if (pos<0) throw new Exception("could not find value '" + key + "' inside connection string");
pos += key.Length;
int pos2=s.IndexOf('"',pos);
if (pos2 < 0) throw new Exception("could not find ending \" in connection string");
return s.Substring(pos,pos2-pos);
}
#>
C. Use it like such:
using(var connection = new SqlConnection(GetEFConnectionString("Database"))) {
..
}
I posted my question on one of the MSDN forums and got a response from Lingzhi Sun who pointed me in the direction of a couple of links at skysanders.net. The second of these links has a very nice example of getting to the app/web.config file and, specifically the part I wanted, the connection strings. It doesn't give any information on the specific connection string for the scenario I described in the original question, but this gets me close enough.
Accessing app.config/web.config from T4 template
Accessing app.config/web.config from T4 template - Take 2
Well, the EF connection string will always have the same name as the model, right? The DB connection string will be embedded in the EF connection string. So I'd say you should be able to get it, at least indirectly, via the EF connection string.
Because you're not running in the assembly, have to specify the config file name.
So it would be something like:
var config = ConfigurationManager.OpenExeConfiguration(name);
var cs = config.ConnectoinStrings[modelName];
Note that name, here, is supposed to be an EXE name. But in the IDE, your config fine is going to be called App.config rather than MyApp.dll.config. So you may have to play around with this to get it to work -- try using "App" as the EXE name!
Worst case is open it as a file and then use the config manager.