linq with Msaccess [duplicate] - asp.net

I have a *.MDB database file, and I am wondering if it is possible or recommended to work against it using LINQ in C#. I am also wondering what some simple examples would look like.
I don't know a lot about LINQ, but my requirements for this task are pretty simple (I believe). The user will be passing me a file path to Microsoft Access MDB database and I would like to use LINQ to add rows to one of the tables within the database.

What you want is a LINQ to ODBC provider, or a LINQ to JET/OLEDB provider.
Out of the box, MS doesn't make one. There may be a 3rd party who does.

Actually I recently (today) discovered that you can access an Access database with LinqToSql. It must be in the 2002 or newer format, you will not be able to drag and drop the tables to your datacontext so either manually create the objects in your dbml or you can use SQL Server Migration for Access to move it to a sql server and then drag and drop all you want. When you want to actually create the context pass it an OleDbConnection. Use your standard Jet.OLEDB.4.0 connection string on the OleDbConnection and you are good to go. Not sure of the limitation this may incurr though. I just did a quick sample and did an OrderBy without issue.

I wrote a small sample program to test this out with David's answer. You'll need to make an access database and manually create the DBML for Linq-to-SQL, as you cannot drag 'n drop them.
Inserts fail, citing Missing semicolon (;) at end of SQL statement. but queries seem to work alright.
using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using Linq2Access.Data;
namespace Linq2Access
{
class Program
{
static readonly string AppPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
static readonly string DbPath = Path.Combine(AppPath, "Data", "database.accdb");
static readonly string DbConnString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + DbPath + "';Persist Security Info=False;";
static void Main(string[] args)
{
if (!File.Exists(DbPath))
throw new Exception("Database file does not exist!");
using (OleDbConnection connection = new OleDbConnection(DbConnString))
using (DataRepositoryDataContext db = new DataRepositoryDataContext(connection))
{
List<dbProject> projects = new List<dbProject>();
for (int i = 1; i <= 10; i++)
{
dbProject p = new dbProject() { Title = "Project #" + i };
for (int j = 1; j <= 10; j++)
{
dbTask t = new dbTask() { Title = "Task #" + (i * j) };
p.dbTasks.Add(t);
}
projects.Add(p);
}
try
{
//This will fail to submit
db.dbProjects.InsertAllOnSubmit(projects);
db.SubmitChanges();
Console.WriteLine("Write succeeded! {0} projects, {1} tasks inserted",
projects.Count,
projects.Sum(x => x.dbTasks.Count));
}
catch(Exception ex)
{
Console.WriteLine("Write FAILED. Details:");
Console.WriteLine(ex);
Console.WriteLine();
}
try
{
//However, if you create the items manually in Access they seem to query fine
var projectsFromDb = db.dbProjects.Where(x => x.Title.Contains("#1"))
.OrderBy(x => x.ProjectID)
.ToList();
Console.WriteLine("Query succeeded! {0} Projects, {1} Tasks",
projectsFromDb.Count,
projectsFromDb.Sum(x => x.dbTasks.Count));
}
catch (Exception ex)
{
Console.WriteLine("Query FAILED. Details:");
Console.WriteLine(ex);
Console.WriteLine();
}
Console.WriteLine();
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}
}

You can use a DataSet. There are linq extensions that will allow you to query the data with all that LINQ goodness we have become use to :)
eICATDataSet.ICSWSbuDataTable tbl = new eICATDataSet.ICSWSbuDataTable();
ICSWSbuTableAdapter ta = new ICSWSbuTableAdapter();
ta.Fill(tbl);
var res = tbl.Select(x => x.ProcedureDate.Year == 2010);

I have seen this question a lot and in several fora. I made a go at it and here is a complete answer for those who have been looking at it.
LinQ was not made for Access. However, many of the queries will work with Access, including delete procedure. So, according to me, there are only 2 crucial deficiencies when working with Access, which are:
not being able to save data.
not being able to drag and drop objects onto the dbml
Insert will fail with the error "missing semicolon (;)". This is because LinQ save procedure was made to save data and retrieve the primary key ID of the record saved in one go. We know that you cannot execute multiple SQL statements in Access, so that is the reason for that failure.
Update will fail with the error "record not found". An update procedure will of cause look for the record to be updated then update it. I cannot tell why it wouldn't find it, when normal LinQ query to find a record works fine.
Because there is so much benefit to use LinQ, I figured out how to work around the deficiency, while enjoy the other benefits throughout my application. This is how (NB: My codes are in VB.net, but you can convert if required):
Create the LinQ to SQL (.dbml) class to manage your LinQ against the access database, and a way to manager your save procedure. Below is the full procedures of what I created and I now work with LinQ to Access without any problems:
Add a DataGridView on a form. Add buttons for Add, Edit & Delete
Code to fill the grid:
Private Sub ResetForm()
Try
Using db As New AccessDataClassesDataContext(ACCCon)
Dim rows = (From row In db.AccountTypes
Where row.AccountTypeID > 1
Order By row.AccountTypeID Ascending
Select row).ToList()
Me.DataGridView1.DataSource = rows
End Using
Catch ex As Exception
MessageBox.Show("Error: " & vbCr & ex.ToString, "Data Error", MessageBoxButtons.OK)
End Try
End Sub
DetailForm
Code to set control values
Private Sub ResetForm()
Try
If _accountTypeID = 0 Then
Exit Sub
End If
Using db As New AccessDataClassesDataContext(ACCCon)
'Dim rows = (From row In db.AccountTypes
' Where row.AccountTypeID = _accountTypeID
' Order By row.AccountTypeID Ascending
' Select row.AccountTypeID, row.AccountType, row.LastUpdated).ToList()
Dim rows = (From row In db.AccountTypes
Where row.AccountTypeID = _accountTypeID
Select row).ToList()
For Each s In rows
Me.AccountTypeIDTextBox.Text = s.AccountTypeID
Me.myGuidTextBox.Text = s.myGuid
Me.AccountTypeTextBox.Text = s.AccountType
Me.AcHeadIDTextBox.Text = s.AcHeadID
Me.DescriptionTextBox.Text = s.Description
Me.LastUpdatedDateTimePicker.Value = s.LastUpdated
Next
End Using
Catch ex As Exception
End Try
End Sub
LinQToSQLClass
You will have to add the data objects to the dbml manually since you cannot drag and drop when using Access. Also note that you will have to set all the properties of the fields correctly in the properties windows. Several properties are not set when you add the fields.
Code to Save
Public Function SaveAccountType(Optional ByVal type As String =
"Close") As Boolean
Dim success As Boolean = False
Dim row As New AccountType
Using db As New AccessDataClassesDataContext(ACCCon)
If _accountTypeID > 0 Then
row = (From r In db.AccountTypes
Where r.AccountTypeID = _accountTypeID).ToList()(0)
If String.IsNullOrEmpty(row.AccountTypeID) Then
MessageBox.Show("Requested record not found", "Update Customer Error")
Return success
End If
End If
Try
With row
.myGuid = Me.myGuidTextBox.Text
.AccountType = Me.AccountTypeTextBox.Text
.Description = Me.DescriptionTextBox.Text
.AcHeadID = Me.AcHeadIDTextBox.Text
.LastUpdated = Date.Parse(Date.Now())
End With
If _accountTypeID = 0 Then db.AccountTypes.InsertOnSubmit(row)
db.SubmitChanges()
success = True
Catch ex As Exception
MessageBox.Show("Error saving to Customer: " & vbCr & ex.ToString, "Save Data Error")
End Try
End Using
Return success
End Function
Now replace these two lines:
If _accountTypeID = 0 Then db.AccountTypes.InsertOnSubmit(row)
db.SubmitChanges()
with something like this:
Dim cmd As IDbCommand
cmd = Me.Connection.CreateCommand()
cmd.Transaction = Me.Transaction
cmd.CommandText = query
If myGuid.Trim.Length < 36 Then myGuid = UCase(System.Guid.NewGuid.ToString())
cmd.Parameters.Add(New OleDbParameter("myGuid", row.myGuid))
cmd.Parameters.Add(New OleDbParameter("AccountType", row.AccountType))
cmd.Parameters.Add(New OleDbParameter("Description", row.Description))
cmd.Parameters.Add(New OleDbParameter("AcHeadID", row.AcHeadID))
cmd.Parameters.Add(New OleDbParameter("LastUpdated", Date.Now))
If AccountTypeID > 0 Then cmd.Parameters.Add(New OleDbParameter("AccountTypeID", row.AccountTypeID))
If Connection.State = ConnectionState.Closed Then Connection.Open()
result = cmd.ExecuteNonQuery()
cmd = Me.Connection.CreateCommand()
cmd.Transaction = Me.Transaction
cmd.CommandText = "SELECT ##IDENTITY"
result = Convert.ToInt32(cmd.ExecuteScalar())
The last part of the code above is what gets you the ID of the record saved. Personally, I usually make that an option, because I don't need it in most of the cases, so I don't need to add that overhead of fetching back data every time a record is saved, I am happy just to know a record was saved.
That is the overhead added to LinQ, which causes Insert to fail with Access. Is it really necessary to have it? I don't think so.
You may have noted that I normally put my Update and Insert procedures together, so that saves me time and has address both the Insert & Update procedures in one go.
Code for Delete:
Private Sub DelButton_Click(sender As Object, e As EventArgs) Handles DelButton.Click
Using db As New AccessDataClassesDataContext(ACCCon)
Dim AccountTypeID As Integer = Me.DataGridView1.CurrentRow.Cells(0).Value
Dim row = From r In db.AccountTypes Where r.AccountTypeID = AccountTypeID
For Each detail In row
db.AccountTypes.DeleteOnSubmit(detail)
Next
Try
db.SubmitChanges()
Catch ex As Exception
' Provide for exceptions.
MsgBox(ex)
End Try
End Using
End Sub
Now you can enjoy LinQ to Access! Happy coding :)

LINQ to SQL only works for SQL Server databases. What you need is the Microsoft Entity Framework. This makes object oriented access to your mdb. From this you can run LINQ queries.
http://msdn.microsoft.com/en-us/library/aa697427(vs.80).aspx

Related

VB. NET Add multiple ChildNodes in a Treeview

I have many records in a database and I need to populate my treeview dynamically like this: Below is just an example of what I need:
TreeView1.Nodes(a).ChildNodes.Add(New TreeNode("ChildNode " & b))
TreeView1.Nodes(a).ChildNodes(b).ChildNodes.Add(New TreeNode("ChildNode 2 lvl " & b))
I'm getting the records from a MySQL Db and I need to know how can I add multilevel ChildNodes into a loop For ... Next etc...
Do you have any suggestion or idea???
if you want to work with various levels of Treenodes you can use Find function
Dim TempNode As TreeNode = TreeView1.Nodes.Find("Node where I want to add SubNode", True).FirstOrDefault
TempNode.Nodes.Add("SubNode", "SubNode")
This way you can add SubNode to any Node you pick.
.Find("key",True)finds treenodes with following key and .FirstOrDefault picks first. Finally you just add new SubNode to Tempnode.
You considered you are getting it dynamically and from MySql. It may cause error like "Action beeing preformed on this control is being called from the wrong thread. Marshal to the correct thread using Contol.Invoke or Control.BeginInvoke to perform this action." Simply change TempNode.Nodes.Add("SubNode", "SubNode") to TreeView1.Invoke(Sub() TempNode.Nodes.Add("SubNode", "SubNode"))
EXAMPLE:
Dim comm As String = "SELECT * FROM YourTableName"
Dim SqlCmnd as SqlCommand = New SqlCommand(comm, YourMySqlConnection)
Dim READER As SqlDataReader
READER = SqlCmnd.ExecuteReader
While READER.Read
Dim NewNode As TreeNode = New TreeNode(READER.Item("origCategoryID"))
TreeView1.Nodes.Add(NewNode)
NewNode.Nodes.Add(READER.Item("categoryOrderID"))
End While
READER.Close()
EXAMPLE 2:
While READER.Read
If TreeView1.Nodes.Find(READER.Item("OrigCatOrderID"), True).Length > 0 Then
Dim NewNode As TreeNode = TreeView1.Nodes.Find(READER.Item("OrigCatOrderID"), True).FirstOrDefault
NewNode.Nodes.Add(READER.Item("CatOrderID"), READER.Item("CatOrderID"))
Else
TreeView1.Nodes.Add(READER.Item("OrigCatOrderID"), READER.Item("OrigCatOrderID"))
TreeView1.Nodes(READER.Item("OrigCatOrderID")).Nodes.Add(READER.Item("CatOrderID"), READER.Item("CatOrderID"))
End While

Convert date in SQLite?

I created an scheduler application with SQL server and now i want to make another one using SQLite. I have a convert query in SQL and it does not work in SQLite. Can anyone help?
try
{
ObservableCollection<Classes.EventClass> listEvents = new ObservableCollection<EventClass>();
SQLiteConnection conn = new SQLiteConnection(#"Data Source=Scheduler.db;Version=3;");
string query= "Select * from Sche_Event where CONVERT(DATE,Event_TimeFrom) = CONVERT(DATE,'" +d.ToString("yyyy-MM-dd HH:mm:ss") + "') ORDER BY Event_TimeFrom ASC";
SQLiteCommand command= new SQLiteCommand(query, conn);
conn.Open();
SQLiteDataReader dr = command.ExecuteReader();
while (dr.Read())
{
EventClass dog = new EventClass();
dog.DogID = dr.GetInt32(0);
dog.DogName = dr.GetString(1);
dog.DogText = dr.GetString(2);
dog.DogPriority = dr.GetInt32(3);
dog.DogTimeFrom = dr.GetDateTime(4);
dog.DogTimeTo = dr.GetDateTime(5);
dog.KliID = dr.GetInt32(6);
listEvents .Add(dog);
}
return listEvents ;
}
catch (Exception)
{
return null;
}
I expect that my code goes to While() and read the information about the Event but all it does it goes to Catch() and returns nothing.
The query in SQL works just fine but i dont not work with SQLite :(
Of course the statement doesn't work in SQLite, because convert() is not a known function there. But if you're lucky you don't even need it, depending on the format in which the timestamp is stored in your SQLite table. As you didn't provide any sample data nor described what you actually want to do, you could either read the SQLite doc about date and time functions or rephrase your question to "How do I do X in SQLite?".

How to check fields for null

I know how to do this, but am wondering about best practices...
I go get a row of data from a table. Some of these fields can be NULL. I am currently using an if statement for each field and if it is NOT NULL, populate text boxes or labels as appropriate.
This seems cumbersome to me, but I couldn't think of a better method to check for nulls and act accordingly.
Does this make sense? Is there a better way?
Since vb.net 14 best way is to use ?
With Visual Basic 14 you can elegantly handle the possibility of a
null like this, using the new ?. operator:
Console.WriteLine("{0} ({1})",
customer.Name,
customer.Address?.Country)
Link to vb.net article.
Link to c# article.
If you are using a SqlDataReader to process a SqlCommand, then you can inspect the SqlDataReader.IsDBNull property. Here's a real world example:
Try
Using con = New SqlConnection(dbConnectString)
Using cmd = New SqlCommand("usp_GetValue", con)
cmd.Parameters.Add("#nvcKey", SqlDbType.VarChar).Size = key.Length
cmd.Parameters("#nvcKey").Value = key
con.Open()
Using reader As SqlDataReader = cmd.ExecuteReader()
If reader.Read() Then
If Not reader.IsDBNull(1) Then ExpriryDateUTC = reader.GetDateTime(1)
AllowMemoryCache = reader.GetBoolean(2)
If reader.IsDBNull(0) Then
value = Nothing
Return False
Else
value = DeserializeDataContractOjectFromXML(Of T)(reader.GetString(0))
Return True
End If
Else
Return False
End If
End Using
End Using
End Using
Catch ex As Exception
Return False
End Try

User details stored in separate table ASP.NET Identity

I am a complete beginner at ASP.net(and this forum) i am using Visual studio 2013 and have created created another table in the created database using the package manager console.
How do i go about placing the information into this new table? (I am looking to store firstname and last name in a separate table)
The create account button is below:
Protected Sub CreateUser_Click(sender As Object, e As EventArgs)
Dim userName As String = UserNameCtrl.Text
Dim Firstnane As String = firstnamectrl.Text
Dim manager = New UserManager
Dim User = New ApplicationUser() With {.UserName = userName}
Dim result = manager.Create(User, Password.Text)
If result.Succeeded Then
IdentityHelper.SignIn(manager, User, isPersistent:=False)
IdentityHelper.RedirectToReturnUrl(Request.QueryString("ReturnUrl"), Response)
Else
ErrorMessage.Text = result.Errors.FirstOrDefault()
End If
End Sub
Any pointers in the right direction, hints or suggested reading would be very helpful.
If I understand correctly, this link may be of some help:
http://www.codeguru.com/vb/gen/vb_database/adonet/article.php/c15033/A-Basic-VBNET-ADONET-Tutorial-Adding-Deleting-and-Updating.htm
It is for a windows form application, but it should translate pretty well if you're using web forms. Basically, you just want to make a connection to the database during the button click event (the simplest way I know of to make this connection is using ADO.NET), and pass the values of the first and last name in a SQL query to the sql server.
You would be building the sql query as a string, and concatenating your vb variables into that string. Something like; "Insert into table xxx(firstname, LastName) values " & Firstname & ", " & Lastname...

Save changes to Entity model to the database

I'm new to Entity Framework and am expanding an existing codebase. I'm using jQuery to pass the needed info back to the server ajaxy style, so I can't use TryUpdateModel(). Here's the code:
<HttpPost()>
Function UpdateRoster() As JsonResult
Dim model As New Models.ViewModels.PlayerAdmin
Dim jsonString As String = Request.Form("json")
model = Deserialise(Of Models.ViewModels.PlayerAdmin)(jsonString)
For Each playerAdminPlayer As Models.ViewModels.PlayerAdminPlayer In model.Roster
Dim playerToTeam As New DAL.PlayersToTeam
Dim player As DAL.Player = PlayerAdminManager.GetPlayerById(playerAdminPlayer.PlayerId)
player.FirstName = playerAdminPlayer.FirstName
PlayerAdminManager.SaveChanges()
Next playerAdminPlayer
Dim playerAfter As DAL.Player = PlayerAdminManager.GetPlayerById(model.Roster.First.PlayerId)
Return Json(New With {.success = False, .message = playerAfter.FirstName})
End Function
Deserialise is a helper function that converts the incoming JSON string to a vb object.
Things seem to work fine in that player successfully loads from the DB and playerAdminPlayer is the correct object from the JSON string. However, when I call PlayerAdminManager.SaveChanges() (which just passes the call the db.SaveChanges() the result is always 0, even if there is a change (not sure if that is expected).
playerAfter was my attempt to see if changes were actually being saved. It seems to work correctly, in that playerAfter.FirstName is the newly updated first name.
PlayerAdminManager.GetPlayerById(integer) pulls from the DB, so I would think that, since changes are observed in playerAfter, that those changes were saved to the DB. However, when I reload the web page (which pulls from the DB), the old values are there.
Any ideas?
Here are some of the functions I mention:
Function GetPlayerById(ByVal Id As Integer) As DAL.Player
Return Container.Players.Where(Function(o) o.PlayerId = Id And o.IsVisible = True).SingleOrDefault
End Function
Sub SaveChanges()
Dim numberOfChanges As Integer = Container.SaveChanges()
Debug.WriteLine("No conflicts. " & numberOfChanges.ToString() & " updates saved.")
End Sub
EDIT
Container code:
Private _Container As DAL.LateralSportsContainer
Protected ReadOnly Property Container As DAL.LateralSportsContainer
Get
If _Container Is Nothing Then
Dim connStr As New System.Data.EntityClient.EntityConnectionStringBuilder
connStr.ProviderConnectionString = Web.Configuration.WebConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString
connStr.Metadata = "res://*/Lateral.csdl|res://*/Lateral.ssdl|res://*/Lateral.msl"
connStr.Provider = "System.Data.SqlClient"
_Container = New DAL.LateralSportsContainer(connStr.ConnectionString)
End If
Return _Container
End Get
End Property
Turns out I was using a non static (shared) Container. I had 2 Manager classes that both inherited from a BaseManager class were the Container was defined. I was executing the query command in one Manager and saving in another.
Doh!

Resources