I use a Postgres database for my backend for multiple web applications. These applications are hosted on a third party server that I have limited access to. I currently use npgsql and nhibernate for all my data connection needs, and this works quite well.
Well, now I need to write some Crystal Reports. For performance's sake (and because the results data will not fit into any entities) I can't use my nhibernate entities as a data source for the reports; likewise I can't create an ODBC connection for the Crystal Reports because there is no driver or DSN on the destination server. So, I thought, maybe I can build datasets based off of queries I write, and fill them using the Npgsql data provider, and feed these to the Crystal Reports. I have performed a proof of concept, and it works pretty well.
The problem is, I have a lot of reports, and a lot of datasets to build, and it's very time consuming to manually build the schema in each of these. The dataset automation interfaces won't let me choose my npgsql data provider in the connection selector, which is rather annoying.
I was hoping there might be a fairly straightforward way of throwing together a little code to get a dataset schema via the npgsql data provider at runtime, and then serialize the schemas into files I could then import into my reporting project in design time.
Can this practically be done? Is there an easier way? There's a bunch of reports and a lot of columns, and hand-coding the schema for them will be extremely time-consuming.
The dataset automation interfaces won't let me choose my npgsql data provider
Any .NET data provider must be registered within DDEX to be supported in Visual Studio designers (although, this is not necessary for using particular provider for building and running applications).
There was similar question about npgsql, but it almost 2 years old.
Maybe, something changed since 2011, because official documentation says:
2.2 Installing binary package
...
Note that placing Npgsql in the GAC is required for Npgsql design
time support in Visual Studio .Net.
Here's what I ended up doing.
I made a quick little WinForms App that with three multiline text boxes and a button. The button click event had the following code attached to it:
var query = txtQuery.Text;
var connectionString = ConfigurationManager.ConnectionStrings["connection"].ConnectionString;
try
{
string data;
string schema;
GetSchema(connectionString, query, out data, out schema);
txtXML.Text = data;
txtXSD.Text = schema;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
The GetSchema method looked like this:
private void GetSchema(string connectionString, string query, out string data, out string schema)
{
using (var conn = new Npgsql.NpgsqlConnection(connectionString))
using (var da = new Npgsql.NpgsqlDataAdapter(query, conn))
using (var ds = new DataSet())
using (var dataStream = new MemoryStream())
using (var schemaStream = new MemoryStream())
{
conn.Open();
da.Fill(ds);
ds.WriteXml(dataStream);
ds.WriteXmlSchema(schemaStream);
dataStream.Position = 0;
schemaStream.Position = 0;
using (var dataReader = new StreamReader(dataStream))
using (var schemaReader = new StreamReader(schemaStream))
{
data = dataReader.ReadToEnd();
schema = schemaReader.ReadToEnd();
}
}
}
When I ran it, I got my data XML and my schema XML. With this I was able to build my result sets.
Related
I'm working on a pretty special, legacy project where I need to build an app for PDA devices under Windows Mobile 6.5. The devices have a local database (SQL Server CE) which we are supposed to sync with a remote database (Microsoft Access) whenever they are docked and have network access.
So the local database using SQL Server CE works fine, but I can’t figure out a way to sync it to the Access database properly.
I read that ODBC and OLEDB are unsupported under Windows Mobile 6.5, most ressources I find are obsolete or have empty links, and the only way I found was to export the local database relevant tables in XML in the hope to build a VBA component for Access to import them properly. (and figure out backwards sync).
Update on the project and new questions
First of all, thanks to everyone who provided an useful answer, and to #josef who saved me a lot of time with the auto path on this thread.
So a remote SQL Server is a no go for security reasons (client is paranoid about security and won't provide me a server). So I'm tied to SQL Server CE on the PDA and Access on the computer.
As for the sync:
The exportation is fine: I'm using multiple dataAdapters and a WriteXML method to generate XML files transmitted by FTP when the device is plugged back in. Those files are then automatically imported into the Access database. (see code at the end).
My problem is on the importation: I can acquire data through XML readers from an Access-generated file. This data is then inserted in a dataset (In fact, I can even print the data on the PDA screen) but I can't figure out a way to do an "UPSERT" on the PDA's database. So I need a creative way to update/insert the data to the tables if they already contains data with the same id.
I tried two methods, with SQL errors (from what I understood it's SQL Server CE doesn't handle stored procedures or T-SQL). Example with a simple query that is supposed to update the "available" flag of some storage spots:
try
{
SqlCeDataAdapter dataAdapter = new SqlCeDataAdapter();
DataSet xmlDataSet = new DataSet();
xmlDataSet.ReadXml(localPath +#"\import.xml");
dataGrid1.DataSource = xmlDataSet.Tables[1];
_conn.Open();
int i = 0;
for (i = 0; i <= xmlDataSet.Tables[1].Rows.Count - 1; i++)
{
spot = xmlDataSet.Tables[1].Rows[i].ItemArray[0].ToString();
is_available = Convert.ToBoolean(xmlDataSet.Tables[1].Rows[i].ItemArray[1]);
SqlCeCommand importSpotCmd = new SqlCeCommand(#"
IF EXISTS (SELECT spot FROM spots WHERE spot=#spot)
BEGIN
UPDATE spots SET available=#available
END
ELSE
BEGIN
INSERT INTO spots(spot, available)
VALUES(#spot, #available)
END", _conn);
importSpotCmd.Parameters.Add("#spot", spot);
importSpotCmd.Parameters.Add("#available", is_available);
dataAdapter.InsertCommand = importSpotCmd;
dataAdapter.InsertCommand.ExecuteNonQuery();
}
_conn.Close();
}
catch (SqlCeException sql_ex)
{
MessageBox.Show("SQL database error: " + sql_ex.Message);
}
I also tried this query, same problem SQL server ce apparently don't handle ON DUPLICATE KEY (I think it's MySQL specific).
INSERT INTO spots (spot, available)
VALUES(#spot, #available)
ON DUPLICATE KEY UPDATE spots SET available=#available
The code of the export method, fixed so it works fine but still relevant for anybody who wants to know:
private void exportBtn_Click(object sender, EventArgs e)
{
const string sqlQuery = "SELECT * FROM storage";
const string sqlQuery2 = "SELECT * FROM spots";
string autoPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); //get the current execution directory
using (SqlCeConnection _conn = new SqlCeConnection(_connString))
{
try
{
SqlCeDataAdapter dataAdapter1 = new SqlCeDataAdapter(sqlQuery, _conn);
SqlCeDataAdapter dataAdapter2 = new SqlCeDataAdapter(sqlQuery2, _conn);
_conn.Open();
DataSet ds = new DataSet("SQLExport");
dataAdapter1.Fill(ds, "stock");
dataAdapter2.Fill(ds, "spots");
ds.WriteXml(autoPath + #"\export.xml");
}
catch (SqlCeException sql_ex)
{
MessageBox.Show("SQL database error: " + sql_ex.Message);
}
}
}
As Access is more or less a stand-alone DB solution I strongly recommend to go with a full flavored SQL Server plus IIS to setup a Merge Replication synchronisation between the SQL CE data and the SQL Server data.
This is described with full sample code and setup in the book "Programming the .Net Compact Framework" by Paul Yao and David Durant (chapter 8, Synchronizing Mobile Data).
For a working sync, all changes to defined tables and data on the server and the CE device must be tracked (done via GUIDs, unique numbers) with there timestamps and a conflict handling has to be defined.
If the data is never changed by other means on the server, you may simply track Device side changes only and then push them to the Access database. This could be done by another app that does Buld Updates like described here.
If you do not want to go the expensive way to SQL Server, there are cheaper solutions with free SQLite (available for CE and Compact Framework too) and a commercial Sync tool for SQLite to MSAccess like DBSync.
If you are experienced, you may create your own SQLite to MS ACCESS sync tool.
I have to build a software which can search and display PDF files from a folder on the local network.
My supervisor was requesting to have a database and store the path of PDF files there(not to the PDF itself because of the large amount), so I can search and open them from the software.
I would greatly appreciate if you could give me some ideas for solving it.
Haven't you already solved it? You could have an application such as a service or even a command line application that could poll/manually look into a specified folder, from there you would get the full file location and persist this into your database. When you need to search for the PDF you could perform a query against your database for PDF's that match your criteria.
You could even strip the filename from the filepath and query on that rather than the filepath and store the filepath in a different column (providing you're using a relational database).
Edit
Based on your comment on the other answer, I would stick with SQL server. From what I understand you want to automatically "catologue" the PDFs dropped in that folder, you can do this by writing a simple windows service. From that Windows service you can use an ORM (like Entity Framework) or ADO.net to persist your changes to your database. From the application you wish to display the result (be it Web app or a Win forms application, whatever), you can just query the correct column in your DB
Resources:
Entity Framework
Linq to Entities
private string[,] GetImagesFromServerFolder()
{
IntPtr token;
if (
!NativeMethods.LogonUser([Server_Login_Name], [Server_Location],
[Server_Login_Password], NativeMethods.LogonType.NewCredentials,
NativeMethods.LogonProvider.Default, out token))
{
throw new Win32Exception();
}
try
{
IntPtr tokenDuplicate;
if (!NativeMethods.DuplicateToken(
token,
NativeMethods.SecurityImpersonationLevel.Impersonation,
out tokenDuplicate))
{
throw new Win32Exception();
}
try
{
using (WindowsImpersonationContext impersonationContext =
new WindowsIdentity(tokenDuplicate).Impersonate())
{
/******************* CODE FROM HERE *******************/
List<string> files = new List<string>(Directory.GetFiles(_PHYSICAL SERVER LOCATION_));
return files;
/******************* CODE TO HERE *******************/
}
}
finally
{
if (tokenDuplicate != IntPtr.Zero)
{
if (!NativeMethods.CloseHandle(tokenDuplicate))
{
// Uncomment if you need to know this case.
////throw new Win32Exception();
}
}
}
}
finally
{
if (token != IntPtr.Zero)
{
if (!NativeMethods.CloseHandle(token))
{
// Uncomment if you need to know this case.
////throw new Win32Exception();
}
}
}
}
This will then return a list of all the .pdf files and locations
You can then sore that in a DB.
Then, run through all the files in the list (In your main running method);
List<string> file = GetImagesFromServerFolder();
foreach (var s in file)
{
const string connStr = "INSERT INTO tblPdfLocations (location) VALUES (#location)";
//Store the connection details as a string
string connstr =
String.Format(#"SERVER=[LOCATION]; UID=[USERNAME]; pwd=[PASSWORD]; Database=[DATABASE]");
//Initialise the connection to the server using the connection string.
_sqlConn = new SqlConnection(connstr);
var sqlComm = new SqlCommand(connStr, _sqlConn) {CommandType = CommandType.Text};
sqlComm.Parameters.Add(new SqlParameter("#location", SqlDbType.VarChar, 50)).Value = s;
sqlComm.ExecuteNonQuery();
_sqlConn.Close();
}
read about some ORM to work with database. For example, Entity Framework.
I would like to offer a different perspective than the other answers provided because - the storage and retrieval of such documents like PDF, Word, JPEG etc files is well into the realms of "Content Management" (CM) applications. Although relatively new in the big world of IT - these applications are mature and offer industrial strength solutions.
They use database back-ends and offer a host of facilities - the one you are interested in is the usage of CM purely as a content repository.
You don't have to look at the offerings from the big vendors (ie. Oracle Webcenter) - there are free and well supported alternatives such as Drupal and Joomla. Each of them have API's which I assume will allow you to connect into their repositories from third-party applications.
You should be looking at a solution which can grow with your company's requirements and not look to reinvent something which is already out there.
I have an application that needs to store loads of data in a table format. I want something easy to configure, which is also in built with C#.NET. I don't want to have to include additional DLL files.
Also some links to tutorials, explaining the connection process and querying would be great. I'm assuming this is just like PHP, but which database type do I need?
It needs to be able to hold a lot of data and the ability to perform backups would be nice.
I'm not sure what you mean by "built in with C#.NET", but SQL Server Express comes with Visual Studio.
If you're looking for "a self-contained, embeddable, zero-configuration SQL database engine", you could try System.Data.SQLite.
If you want an offline database you could use SQL Server CE, as its a in-process database that does not require being attached to a server instance, which is really what you want then. Here is an example in C# on how you would connect, and populate a data table to manipulate some data.
// this connectionstring can also be an absolute file path
string connectionString = "Data Source=|DataDirectory|\mydatabase.sdf";
using (SqlCeConnection connection = new SqlCeConnection(connectionString)) {
try {
connection.Open();
}
catch (SqlCeException) {
// connection failed
}
using (SqlCeDataAdapter adapter = new SqlCeDataAdapter("SELECT * FROM <table>", connection)) {
using (DataTable table = new DataTable("<table>")) {
adapter.Fill(); // Populate the table with your select statement
// do stuff with the datatable
// example:
foreach (DataRow row in table.Rows) {
row["mycolumn"] = "somedata";
}
table.AcceptChanges();
}
}
}
You can even use commands instead of data tables
using (SqlCeCommand command = new SqlCeCommand("DELETE FROM <table> WHERE id = '0'", connection)) {
command.ExecuteNonQuery(); // executes command
}
Have a look at the ease of SQL Server Compact
Not build-in but easily added, no install and free.
I'm trying to read a Paradox 5 table into a dataset or simular data structure with the view to putting it into an SQL server 2005 table. I've trawled google and SO but with not much luck. I've tried ODBC:
public void ParadoxGet()
{
string ConnectionString = #"Driver={Microsoft Paradox Driver (*.db )};DriverID=538;Fil=Paradox 5.X;DefaultDir=C:\Data\;Dbq=C:\Data\;CollatingSequence=ASCII;";
DataSet ds = new DataSet();
ds = GetDataSetFromAdapter(ds, ConnectionString, "SELECT * FROM Growth");
foreach (String s in ds.Tables[0].Rows)
{
Console.WriteLine(s);
}
}
public DataSet GetDataSetFromAdapter(DataSet dataSet, string connectionString, string queryString)
{
using (OdbcConnection connection = new OdbcConnection(connectionString))
{
OdbcDataAdapter adapter = new OdbcDataAdapter(queryString, connection);
connection.Open();
adapter.Fill(dataSet);
connection.Close();
}
return dataSet;
}
This just return the error
ERROR [HY000] [Microsoft][ODBC Paradox Driver] External table is not in the expected format.
I've also tired OELDB (Jet 4.0) but get the same External table is not in the expected format error.
I have the DB file and the PX (of the Growth table) in the Data folder... Any help would be much appriciated.
I've had the same error. It appeared when I started my C# project on Win2008 64 (previos OS was Win2003 32). Also I found out that it worked fine in console apps and gave different errors in winforms. It seems that problem comes from the specifics of 32 ODBC driver working on 64-bit systems.
My solution was:
// Program.cs
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// it is important to open paradox connection before creating
// the first form in the project
if (!Data.OpenParadoxDatabase())
return;
Application.Run(new MainForm());
}
The connectionstring is common:
string connStr = #"Driver={{Microsoft Paradox Driver (*.db )}};DriverID=538;
Fil=Paradox 7.X;DefaultDir=C:\\DB;Dbq=C:\\DB;
CollatingSequence=ASCII;";
After opening connection you may close it in any place after creating first Form (if you need to keep DB closed most of time), for example:
private void MainForm_Load(object sender, EventArgs e)
{
Data.CloseParadoxDatabase();
}
After doing that you may open and close connection every time you want during execution of your application and you willn't get any exceptions.
Maybe this will help you out,
http://support.microsoft.com/support/kb/articles/Q237/9/94.ASP?LN=EN-US&SD=SO&FR=1
http://support.microsoft.com/support/kb/articles/Q230/1/26.ASP
It appears that the latest version of the Microsoft Jet Database Engine
(JDE) does not fully support Paradox unless the Borland Database Engine
(BDE) is also installed.
Try to Run all The Applications with the "Run As Administrator" privileges especially run the VS.NET with "Run As Administrator "... and I am sure your problem get solved
This isn't an answer, but more of a question: any particular reason you're trying to use C# to do the data manipulation as opposed to using SQL Server tools to load the data directly? Something like DTS or SSIS would seem like a better tool for the job.
Thanks, I'll give that a try. I wanted to use C# so I can put it on some web pages without the extra set of putting it in SQL server.
Basically, I would like a brief explanation of how I can access a SQL database in C# code. I gather that a connection and a command is required, but what's going on? I guess what I'm asking is for someone to de-mystify the process a bit. Thanks.
For clarity, in my case I'm doing web apps, e-commerce stuff. It's all ASP.NET, C#, and SQL databases.
I'm going to go ahead and close this thread. It's a little to general and I am going to post some more pointed and tutorial-esque questions and answers on the subject.
MSDN has a pretty good writeup here:
http://msdn.microsoft.com/en-us/library/s7ee2dwt(VS.71).aspx
You should take a look at the data-reader for simple select-statements. Sample from the MSDN page:
private static void ReadOrderData(string connectionString)
{
string queryString =
"SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection = new SqlConnection(
connectionString))
{
SqlCommand command = new SqlCommand(
queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
}
It basicly first creates a SqlConnection object and then creates the SqlCommand-object that holds the actual select you are going to do, and a reference to the connection we just created. Then it opens the connection and on the next line, executes your statements and returns a SqlDataReader object.
In the while-loop it then outputs the values from the first row in the reader. Every time "reader.Read()" is called the reader will contain a new row.
Then the reader is then closed, and because we are exiting the "using"-secret, the connection is also closed.
EDIT: If you are looking for info on selecting/updating data in ASP.NET, 4GuysFromRolla has a very nice Multipart Series on ASP.NET 2.0's Data Source Controls
EDIT2: As others have pointed out, if you are using a newer version of .NET i would recommend looking into LINQ. An introduction, samples and writeup can be found on this MSDN page.
The old ADO.Net (sqlConnection, etc.) is a dinosaur with the advent of LINQ. LINQ requires .Net 3.5, but is backwards compatible with all .Net 2.0+ and Visual Studio 2005, etc.
To start with linq is ridiculously easy.
Add a new item to your project, a linq-to-sql file, this will be placed in your App_Code folder (for this example, we'll call it example.dbml)
from your server explorer, drag a table from your database into the dbml (the table will be named items in this example)
save the dbml file
You now have built a few classes. You built the exampleDataContext class, which is your linq initializer, and you built the item class which is a class for objects in the items table. This is all done automatically and you don't need to worry about it. Now say I want to get record with the itemID of 3, this is all I need to do:
exampleDataContext db = new exampleDataContext(); // initializes your linq-to-sql
item item_I_want = (from i in db.items where i.itemID == 3 select i).First(); // using the 'item' class your dbml made
And that's all it takes. Now you have a new item named item_I_want... now, if you want some information from the item you just call it like this:
int intID = item_I_want.itemID;
string itemName = item_I_want.name;
Linq is very simple to use! And this is just the tip of the iceberg.
No need to learn antiquated ADO when you have a more powerful, easier tool at your disposal :)
Reads like a beginner question. That calls for beginner video demos.
http://www.asp.net/learn/data-videos/
They are ASP.NET focused, but pay attention to the database aspects.
topics to look at:
ADO.NET basics
LINQ to SQL
Managed database providers
If it is a web application here are some good resources for getting started with data access in .NET:
http://weblogs.asp.net/scottgu/archive/2007/04/14/working-with-data-in-asp-net-2-0.aspx
To connect/perform operations on an SQL server db:
using System.Data;
using System.Data.SqlClient;
string connString = "Data Source=...";
SqlConnection conn = new SqlConnection(connString); // you can also use ConnectionStringBuilder
connection.Open();
string sql = "..."; // your SQL query
SqlCommand command = new SqlCommand(sql, conn);
// if you're interested in reading from a database use one of the following methods
// method 1
SqlDataReader reader = command.ExecuteReader();
while (reader.Read()) {
object someValue = reader.GetValue(0); // GetValue takes one parameter -- the column index
}
// make sure you close the reader when you're done
reader.Close();
// method 2
DataTable table;
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(table);
// then work with the table as you would normally
// when you're done
connection.Close();
Most other database servers like MySQL and PostgreSQL have similar interfaces for connection and manipulation.
If what you are looking for is an easy to follow tutorial, then you should head over to the www.ASP.net website.
Here is a link to the starter video page: http://www.asp.net/learn/videos/video-49.aspx
Here is the video if you want to download it: video download
and here is a link to the C# project from the video: download project
Good luck.
I would also recommend using DataSets. They are really easy to use, just few mouse clicks, without writing any code and good enough for small apps.
If you have Visual Studio 2008 I would recommend skipping ADO.NET and leaping right in to LINQ to SQL
#J D OConal is basically right, but you need to make sure that you dispose of your connections:
string connString = "Data Source=...";
string sql = "..."; // your SQL query
//this using block
using( SqlConnection conn = new SqlConnection(connString) )
using( SqlCommand command = new SqlCommand(sql, conn) )
{
connection.Open();
// if you're interested in reading from a database use one of the following methods
// method 1
SqlDataReader reader = command.ExecuteReader();
while (reader.Read()) {
object someValue = reader.GetValue(0); // GetValue takes one parameter -- the column index
}
// make sure you close the reader when you're done
reader.Close();
// method 2
DataTable table;
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(table);
// then work with the table as you would normally
// when you're done
connection.Close();
}