Change connection EF at runtime in WPF app - wpf

I have a WPF app that I'd like to change the connection string programmatically when the app loads. I use the Database-First approach for EF.
I spent a lot of time implementing various solutions found online including stack overflow and can't seem to get it to work.
The most common way seems to be to modify the Entity partial class. When I do this I get the following error at runtime:
Additional information: The context is being used in Code First mode with code that was generated from an EDMX file for either Database First or Model First development. This will not work correctly. To fix this problem do not remove the line of code that throws this exception. If you wish to use Database First or Model First, then make sure that the Entity Framework connection string is included in the app.config or web.config of the start-up project. If you are creating your own DbConnection, then make sure that it is an EntityConnection and not some other type of DbConnection, and that you pass it to one of the base DbContext constructors that take a DbConnection. To learn more about Code First, Database First, and Model First see the Entity Framework documentation here: http://go.microsoft.com/fwlink/?LinkId=394715
I got the same error implementing various other ways as well. If someone could please help me implement a way to change the connection string at runtime I'd greatly appreciate it.
My current implementation is taken from this example solution:
Changing Databases at Run-time using Entity Framework
Implementation:
App.config containts the connection string and name
Partial class added with same name as Entity class:
Imports System.Data.Entity
Imports System.Data.EntityClient
Imports System.Data.SqlClient
Partial Public Class MyEntityName
Inherits DbContext
Public Sub New(ByVal connString As String)
MyBase.New(connString)
End Sub
End Class
In my Application.xaml code file I set a global string variable by calling a method that builds the EntityConnectionStringBuilder. This global string variable is then passed into an entity constructor.
Imports System.Reflection
Imports DevExpress.Xpf.Core
Imports System.Data.EntityClient
Class Application
Public Sub New()
entityConnStr = BuildConnectionString("[MyDataSource]", "[MyDatabase]")
End Sub
Private Function BuildConnectionString(ByVal DataSource As String, ByVal Database As String) As String
' Build the connection string from the provided datasource and database
Dim connString As String = "data source=" & DataSource & ";initial catalog=" & Database & ";persist security info=True;user id=[user];password=[password];trustservercertificate=True;MultipleActiveResultSets=True;App=EntityFramework""
' Build the MetaData... feel free to copy/paste it from the connection string in the config file.
Dim esb As New EntityConnectionStringBuilder()
esb.Metadata = "res://*/DB.[MyEntityName].csdl|res://*/DB.[MyEntityName].ssdl|res://*/DB.[MyEntityName].msl"
esb.Provider = "System.Data.SqlClient"
esb.ProviderConnectionString = connString
' Generate the full string and return it
Return esb.ToString()
End Function
Usage:
Using context = New MyEntity("entityConnStr")
Public connection variable string:
Public entityConnStr As String

I think you should remove the quotes when you pass the connection string to the constructor. You want to use the variable contents, not the variable name.
Use this:
Using context = New MyEntity(entityConnStr)
Instead of this:
Using context = New MyEntity("entityConnStr")

Related

VB.NET ADODB MS SQL Server Open Recordset

After I open a SqlConnection, I try to open a recordset like I did it thousands times before in VBA. Yes, I know that there are differences between VBA and VB.NET, but maybe it's to simple to see it.
Public Class Form1
Const m_cstrCnnString As String = "MyCorrectCnnString"
Dim cnn As SqlConnection
Dim rcs As New ADODB.Recordset
Private Sub MyConnection()
cnn = New SqlConnection(m_cstrCnnString)
cnn.Open()
'so far it works, my cnn.State is 1
rcs.Open("SELECT * FROM dbo.myTable", cnn) 'this line doesn´t work
End Sub
I tried the rcs.Open with and without CursorTypeEnum, LockTypeEnum, but I always get the same error:
System.Runtime.InteropServices.COMException: The arguments are of the wrong type, are out of scope, or are inconsistent with each other.
Background: Win10, Connection to a MS SQL Server Express by VB.NET.
First I import System.Data.SqlClient
Microsoft ActiveX Data Objects 6.1 Library is activated.
Also I tried it with Microsoft ActiveX Data Objects Recordset 6.0 Library.
And I'm new in VB.NET
There's some high-level stuff I need to cover before we can get into the details of what this code should look like.
First, if you're using VB.Net, you should not be opening a RecordSet object. Recordset is from ADO, which is not the same as ADO.Net. You definitely can't use an ADO.Net SqlConnection to open a classic ADO RecordSet. Again: these are two completely different libraries. The classic library exists in .Net only for backwards compatibility and to aid in porting forward old code. It should not be used for new development.
Additionally, when you move forward to ADO.Net you need to be aware of a feature called Connection Pooling. This feature takes care of caching connection objects for you, such that it's counter-productive (uses more RAM and makes things slower) to try to keep a single connection option ready for use throughout the application. Instead, it really is better to create a new connection object for most queries.
Finally, classic ADO had a problem with properly closing resources, such that some of those older applications could occasionally even lock out the database. .Net provides a way to handle this such that you are sure everything is closed and disposed properly and promptly: a Using block.
The weakness of this mechanism is DataReader objects (which replace RecordSet) need the connection to remain open for the life of the reader. This makes it hard to build a good data abstraction to hide all the boiler plate code that goes with data access.
The best pattern I've seen for offering the best of both worlds requires some moderate and advanced VB.Net language features (lambda methods, iterator blocks, and IEnumerable) that are unfamiliar to many traditional VB coders. It's going to require me to go a lot deeper than normal in this answer. That said, once you wrap your head around it, the result is really nice.
To show what this might look like, I need an example that's a little more concrete. We'll pretend you have an Employee table with columns for ID, FirstName, and LastName, and a class Employee with properties of the same name:
Public Class Employee
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Shared Function FromDataRecord(record As IDataRecord) As Employee
Return New Employee() With {
ID = record("ID"),
FirstName = record("FirstName"),
LastName = record("LastName")
}
End Function
End Class
That's right: VB sometimes uses braces now. The Shared method at the end is optional, but it will help make some of what comes next easier to follow. Here is what modern ADO.Net code might look like. Pay special attention to the use of Private and Public
Public Module DB
Private Shared Property ConnectionString As String = "MyCorrectCnnString"
Private Iterator Function GetData(Of T)(SQL As String, transform As Func(Of IDataRecord, T), addParameters As Action(Of SqlParameterCollection)) As IEnumerable(Of T)
Using cn As New SqlConnection(ConnectionString)
Using cmd As New SqlCommand(SQL, cn)
If addParameters IsNot Nothing Then addParameters(cmd.Parameters)
cn.Open()
Using rdr As SqlDataReader = cmd.ExecuteReader()
While rdr.Read()
Yield transform(rdr)
End While
End Using
End Using
End Using
End Function
Public Function GetEmployees() As IEnumerable(Of Employee)
Dim SQL As String = "SELECT * FROM dbo.Employee"
Return GetData(SQL, Employee.FromDataRecord, Nothing)
End Function
Public Function GetEmployeeById(ID As Integer) As Employee
Dim SQL As String = "SELECT * FROM dbo.Employee WHERE ID= #ID"
Return GetData(SQL, Employee.FromDataRecord,
Sub(pc) pc.Add("#ID", SqlDbType.Integer).Value = ID
).FirstOrDefault()
End Function
Public Function GetEmployeesByLastName(LastName As String) As IEnumerable(Of Employee)
Dim SQL As String = "SELECT * FROM dbo.Employee WHERE LastName= #LastName"
Return GetData(SQL, Employee.FromDataRecord,
Sub(pc) pc.Add("#LastName", SqlDbType.NVarChar, 25).Value = LastName
)
End Function
End Module
Now the main part of the application NEVER deals in SQL directly, or even has to know there's a relational database involved. It's just calling methods on a regular module, such that the code in your form for reading data looks more like this:
Dim employees = DB.GetEmployeesByLastName(TextBox1.Text)
DataGridView1.DataSource = employees
You make similar methods in the new Module for writing INSERT/UPDATE/DELETE queries to save changes. These methods will end up calling the cmd.ExecuteNonQuery() method, but they should NEVER accept an SQL statement string as input.
As an application grows you might have a number of classes with FromDataRecord() methods, such that you instead refactor these methods to their own Module. You might also end up with enough Public methods in the DB Module to divide them up into a number of related Modules. At this point, these Modules would all go into a separate class library project (using Friend in some places instead of Private).

How do I access the elmah database from code?

VS2013 update 4, MVC5, elmah.mvc (same as elmah), VB/C#
The following code is part of the standard MVC template to get started and it is part of setting up our database connection from within code to access an SQL database:
Public Class ApplicationDbContext
Inherits IdentityDbContext(Of ApplicationUser)
Public Sub New()
MyBase.New("DefaultConnection", throwIfV1Schema:=False)
Me.Configuration.LazyLoadingEnabled = True
End Sub
Public Shared Function Create() As ApplicationDbContext
Return New ApplicationDbContext()
End Function
We also need the following or something similar in web.config:
<configuration>
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=mypc\SQLEXPRESS;Initial Catalog=mydb.DefaultContext;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="ElmahConnection" connectionString="Data Source=mypc\SQLEXPRESS;Initial Catalog=mydb.elmah;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
I added the elmah connection string as it is relevant to my question.
Then in each location where needed, the following definition is added and we can access the database tables using db.< tablename>...:
Private db As New ApplicationDbContext
I want to write some methods to work with and on the elmah data table but it is a different context than ApplicationContext. I don't quite understand what I would write to setup a connection to the elmah table since I didn't really set that up, it came in the dll for elmah.
While keeping error records makes sense, during development I wanted to clear the Elmah log. While probably trivial for most, it was a good exercise to figure it out. I was so used to code first, I forgot EF also does database first.
In VS2013 update 4 the following Wizard made this task pretty easy.
1) Project > Add New Item > Data > ADO.NET Entity Data Model
2) Give the model a name (With great creativity I used 'elmah') > Click Add
3) This is where it gets pretty cool. I'm not sure when it was added but the EF framework now includes a new choice called Code First From Database. Click on that.
4) Select the elmah connection string
Note: There is an option to save connection settings in web.config. That simply creates a duplicate connection string. Leave it checked or don't, but using the existing string is fine. I didn't select the option and web.config was left untouched. However, not using that option means the connection string name will have to be updated in the context definition > Click Next
5) Click on Tables to load the Elmah table, leave the defaults as is > Click Finish
The wizard creates 2 files and places them in the project. One is named by the data table name which is ELMAH_Error; this is the model. The other is a parital class that sets up the context. If the save connection option was selected then web.config will be modified to add the new connection string.
Note: I combined the 2 Classes into a single file for convenience (may require adding some Using or Imports) and the Public Sub New() is where the connection string name must be updated if the existing connection string is used as shown below.
Partial Public Class elmah
Inherits DbContext
Public Sub New()
MyBase.New("name=ElmahConnection")
End Sub
Public Overridable Property ELMAH_Error As DbSet(Of ELMAH_Error)
Protected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)
End Sub
End Class
6) With all that automatically done (except for the connection string name if required), all that is required is to write the desired code. I wanted to empty the elmah log from time to time and used the following:
Public Class ErrorController
Inherits Controller
Private db1 As New elmah
Function DeleteElmahRecords() As ActionResult
Dim elmahTable = db1.ELMAH_Error.ToList()
db1.ELMAH_Error.RemoveRange(elmahTable)
db1.SaveChanges()
Return RedirectToAction("ErrorTesting")
End Function
…
End Class
Obviously I have a method called ErrorTesting that the function returns to and I access this method from a link on that same page.
Hope this is useful for someone. It's kind of the only way I can give back considering the awesome guru help I often get from this site.

Alternative method to save data than my.settings for VB.Net

I need to save approximately 1600 different pieces of information for a windows forms application.
What is the best way to go about this? For example some of the user settings would look like this measuringItem1Name, measuringItem1Equation, measuringItem1Enabled, measuringItem1Offset, measuringItem2Name...
I have looked into my.settings, however, I found that it would be very tedious and error prone to type out every user setting.
It seems like the best solution would be to have a type of table that can access each individual cell and edit that information.
Does something like this exist? If so, how would it be implemented with VB.Net?
I'd recommend creating a class with properties to hold your settings. Mark the class as serializable. Then serialize and deserialize an instance of this class as application startup / shutdown to the user's profile directory. This will allow you to have strongly typed settings that save separately per user if you ever need that option. It will also allow you to easily set default values for the settings.
You can make the "settings" class as deep as you want, with properties made up of other sub setting classes or lists of settings by index. It's a very powerful pattern.
For example:
settings.MeasuringItem(1).Enabled = True
settings.MeasuringItem(1).Equation = "1+1"
settings.MeasuringItem(1).Offset = 15
settings.MeasuringItem(2).Enabled = True
settings.MeasuringItem(2).Equation = "1+1"
settings.MeasuringItem(2).Offset = 15
Settings is the main settings class with a Generic.List(of MeasuringItem) property on it. MeasuingItem is another class with .Enabled As Boolean, .Equation as string , etc properties on it.
I haven't really checked this for errors. You will want to do a lot more safety checking, but this should get you started.
<Serializable>
Public Class Settings
Public Sub New()
_measuringItems = New List(Of MeasuringItem)
End Sub
Private _measuringItems As List(Of MeasuringItem)
Public ReadOnly Property MeasuringItems As List(Of MeasuringItem)
Get
Return _measuringItems
End Get
End Property
Public Sub Save()
Using s As New IO.FileStream("Path to save", IO.FileMode.Create)
Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
formatter.Serialize(s, Me)
End Using
End Sub
Public Shared Function Load() As Settings
Dim settings As Settings = Nothing
Using s As New IO.FileStream("Path to load", IO.FileMode.Open)
Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
settings = CType(formatter.Deserialize(s), ServerHost.Settings)
End Using
Return settings
End Function
End Class
<Serializable>
Public Class MeasuringItem
Public Property Enabled As Boolean = True 'These are the default values
Public Property Equation As String = "1+1"
Public Property Offset As Integer = 15
End Class
A quick example of usage:
'Create a completely new settings instance with 100 measuring items
Dim settings1 As Settings = New Settings
For i As Integer = 1 To 100
settings1.MeasuringItems.Add(New MeasuringItem)
Next
settings1.MeasuringItems(0).Enabled = False
settings1.MeasuringItems(5).Equation = "testing"
'Save it
settings1.Save()
'load the saved settings
Dim settings2 As Settings = Settings.Load
I would recommend using a database to store the values in. I don't know your operating environment (for example, I have several enterprise SQL servers available for my use.) You could go with SQL Express Edition, which is free. SQL Lite is an option as well. You could even use Access, but that requires MS Office be installed.
If you don't want to go the database route, you can create a settings class, and make the class serializable . This will serialize the object to either a binary file or a XML file (depending on how you set it up.) If you want to go this approach, I'll post the code for you.

Querying Access database vb.net

I am trying to figure out how to query an access database to take info from the database and put it into a class array. I know how to do some querying using basic LINQ, but not sure how to do it the way I want it to. I have a character class I am using, I am storing all the character traits in a database. I want to query the database and add that info to a class array. Any help would be greatly appreciated.
Here at the class private variables which are the same traits in the database:
Public Class Character
Private _strName As String
Private _intLevel As Integer
Private _intHealth As Integer
Private _intDamage As Integer
Private _intScore As Integer
First of all. I have to say this is not a good question.
Actually what you are saying is...: Please build me the dataconnections for this acces database and a visual studio form.
But because I'm in a good mood, That's what I did for you.
I created a little database file in acces.I saved it into a folder called database. Which is located in the project directory.
I made 2 classes. 1 CharactersForm, and 2 Gateway
the gateway is to make an connection to your Acces database.
The CharactersForm is the form what you see when you debug
Here is the CharactersForm class.
Make sure you drag and drop a listbox and a button
Call the listbox ListBoxCharacters.
Call the button InladenButton. Or call it whatever you want, But make sure it will fit the code given.
Option Explicit On
Option Strict On
Option Infer On
Public Class CharactersForm
Dim _CharactersGateway As New Gateway
Private Sub InladenButton_Click(sender As Object, e As EventArgs) Handles InladenButton.Click
_CharactersGateway.LoadDataTableCharacters()
ListBoxCharacters.DataSource = _CharactersGateway.CharactersDataTable
ListBoxCharacters.DisplayMember = "Name"
End Sub
End Class
The Gateway Class ( to make connection to your acces file)
If somehow visual studio cannot reach the database then it means the connectionstring is not correct. Make sure the connection string is exactly as where your accesfile.accdb is located
Option Explicit On
Option Strict On
Option Infer On
Imports System.IO
Imports System.Data.OleDb
Public Class Gateway
Public Shared AppPath As String = Application.StartupPath()
'\Debug
Public Shared DirectoryUp1 As String = Path.GetDirectoryName(AppPath)
'\bin
Public Shared DirectoryUp2 As String = Path.GetDirectoryName(DirectoryUp1)
'\CharactersStackOverFlow
Public Shared DirectoryUp3 As String = Path.GetDirectoryName(DirectoryUp2)
'\CharactersStackOverFlow
Public Shared AccesDatabaseFilePath As String = DirectoryUp3 & "\DataBase\Characters.accdb"
Dim ConnectionString As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source= " & AccesDatabaseFilePath)
Public Property DataSetCharacters As New DataSet
Public ReadOnly Property CharactersDataTable As DataTable
Get
CharactersDataTable = DataSetCharacters.Tables("Characters")
End Get
End Property
Public CharactersDataAdapter As OleDbDataAdapter
Public Sub LoadDataTableCharacters()
CharactersDataAdapter = New OleDbDataAdapter("SELECT * FROM Characters", ConnectionString)
CharactersDataAdapter.Fill(DataSetCharacters, "Characters")
Dim kamersCommandBuilder As New OleDbCommandBuilder(CharactersDataAdapter)
End Sub
End Class
This is as Yuriy Galanter said. You need the OleDbConnection.
If you are using office 2010 and you get thsi warning ::
'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine
You need to download the 2007 Office System Driver. It works also for 2010 don't ask me why.
Type '2007 Office System Driver' in google. And from microsoft you can download and install it.
ALL DONE!
if you run the program and press the load button. The names of your characters will come in the listbox.
If you want to put values of the fields of the database in variables. Then it's just about making a for loop and put them in dimmed variables. That is something I'm sure you can do yourself. The connection to the database and the loading in the program is already sorted out now!
Good luck
Jonathan

VB.NET: WPF Single Instance

Has anyone using VB.NET 2010 been able to create a single instance application?
I've followed the MSDN sample but it does not have an Application.xaml file.
Converting any C# samples to VB doesn't work as I cannot override the Main sub in Application.xaml (C# calls it App.xaml).
You can try using a Mutex. In the projects properties, disable the application framework and set Sub Main as the startup object. Then add a Module to your project:
Imports System.Threading
Module EntryPoint
Sub Main()
Dim noPreviousInstance As Boolean
Using m As New Mutex(True, "Some Unique Identifier String", noPreviousInstance)
If Not noPreviousInstance Then
MessageBox.Show("Application is already started!")
Else
Dim mainWindow As New MainWindow()
Dim app As New Application()
app.Run(mainWindow)
End If
End Using
End Sub
End Module
With this method, you will have to take care of your app's shutdown by calling the Shutdown method of the application.
Here's a few possible solutions. I'd look through the whole thread here before deciding on one. Make sure you backup your code before trying any of these, so if one doesn't work, you can try another.
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/6c15b837-9149-4b07-8a25-3266949621a7/

Resources