I have an old application that I wrote in Access VBA, the time has come to upgrade the code and the company decided to go with C# since we use it the most. My question is following, I have this code in VBA that works great,
Set RS2 = Db.OpenRecordset("Select * FROM TTable WHERE ID="&Forms![test]![SifraFirme]&")
su = RS2.RecordCount
RS2.MoveFirst
Do While Not RS2.EOF
//lines of code
RS3.MoveNext
Loop
RS3.Close
Now my question is, is there a C# command similar to Do While Not RS.EOF, any literature or examples would be highly appreciated. Just a nudge in the right direction because it has become frustrating. The main point of code above is to go through the table and filter the data and write it to XML (predefined structure) based on ID once he is done with first, move on to the second, and ...
Thank you,
Answering to:
The main point of code above is to go through the table and filter the
data and write it to XML
You can read database table to some DataSet, using OleDbDataAdapter from System.Data namespace. Then easily work with filled DataSet or instantly get its XML representation by GetXml method:
static void Main(string[] args)
{
// Note about set Prefer 32-bit app version of your C# app to use Jet.OLEDB provider
var connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=YourDBPath";
var query = "Select * FROM TTable";
// Introducing our DataSet
var dataSet = new System.Data.DataSet();
using (var connection = new System.Data.OleDb.OleDbConnection(connectionString))
{
var command = new System.Data.OleDb.OleDbCommand(query, connection);
try
{
connection.Open();
using (var dataAdapter = new System.Data.OleDb.OleDbDataAdapter(command))
{
// Fill DataSet
dataAdapter.Fill(dataSet);
}
// Get XML representation of DataSet and save to XML file
System.IO.File.WriteAllText(#"TTable.xml", dataSet.GetXml());
// Or if need to filter data before save - read through DataSet
var TTable = dataSet.Tables["TTable"];
foreach (var row in TTable.Rows.Cast<System.Data.DataRow>().ToArray()) // using System.Linq needed
{
}
}
catch (System.Exception ex)
{
// Handle exception in some way
System.Console.WriteLine(ex.Message);
}
}
System.Console.ReadKey();
}
C# has the XMLWriter class and you can use the SQL classes for querying and reading the information.
The while loop in C# would be something like this:
while (!RS2.EOF)
{
//lines of code
RS2.MoveNext();
}
The ! is the Logical negation operator.
ADO.NET has the DataSet class which works with data in a way that is similar to a RecordSet in VBA.
See Microsoft's documentation on DataSet
Related
I've been trying to piece together how other users have finished their projects, but my understanding is still limited.
I want to take any given XML source, make a Data Flow Task, and pass its data to an OLE DB destination matching the table name of the XML file. Running it with the visual tool means I cannot do dynamic data flow tasks because the Metadata does not refresh.
I have created a script that creates a package, but when I open the package in Visual Studio, it has a red-x saying that there cannot be zero Input Columns. When I drill down and look at the mappings of the OLE DB Destination, then click OK - it corrects it for me. I cannot figure out how to do that programmatically.
I've seen others solve it by using foreach loops and going through the Input columns, but I cannot seem to figure it out.
I also have a separate script that I tried to mimic several people's scripts with, and it has different issues. Not sure how to post it as an attachment
Thank you in advance for the help :)
EDIT
I've been getting positive feedback for trying out BIML, and I will...but I want to know if in the short term anyone can help me figure out why this doesn't fill in ExternalMetaDataColumnId for my input. I've posted my updated code below with foreach loops that aren't doing what I expect them to.
Thank you
#region
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using System.Xml;
#endregion
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
#region Initial Setup
Application a = new Application();
Package p = new Package();
TaskHost t = p.Executables.Add("DTS.Pipeline") as TaskHost;
t.Name = "DataFlow Task";
t.Description = "Flat File to Database";
MainPipe mp = t.InnerObject as MainPipe;
#endregion
#region Flat File Source in Dataflow Task
IDTSComponentMetaData100 md = mp.ComponentMetaDataCollection.New();
md.ComponentClassID = "Microsoft.XmlSourceAdapter";
md.Name = "XML Source";
CManagedComponentWrapper wrp = md.Instantiate();
wrp.ProvideComponentProperties();
#endregion
#region Add connection manager to OLE DB
ConnectionManager conn = p.Connections.Add("OLEDB");
conn.Name = "westcoastuserDBO";
conn.ConnectionString = "Data Source=SERVER;Initial Catalog=DBO;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;";
#endregion
#region XML Source Properties
wrp.SetComponentProperty("XMLData", #"C:\Users\file.xml");
wrp.SetComponentProperty("XMLSchemaDefinition", #"C:\Users\file.xsd");
wrp.SetComponentProperty("AccessMode", 0);
wrp.SetComponentProperty("UseInlineSchema", false);
//below does not work
//wrp.SetComponentProperty("XMLIntegerMapping", 0).TypeConverter = "Microsoft.SqlServer.Dts.Pipeline.XmlSourceAdapter + XMLIntegerMappingConverter";
wrp.ReinitializeMetaData();
wrp.ReleaseConnections();
IDTSComponentMetaData100 md2 = mp.ComponentMetaDataCollection.New();
md2.ComponentClassID = "Microsoft.OLEDBDestination";
CManagedComponentWrapper wrp2 = md2.Instantiate();
wrp2.ProvideComponentProperties();
md2.Name = "OLE DB Connection";
md2.UsesDispositions = true;
md2.Version = 4;
wrp2.SetComponentProperty("OpenRowset", "dbo.authorizations");
#endregion
IDTSPath100 path = mp.PathCollection.New();
path.AttachPathAndPropagateNotifications(md.OutputCollection[0], md2.InputCollection[0]);
IDTSInput100 input = md2.InputCollection[0];
IDTSVirtualInput100 vInput = input.GetVirtualInput();
//below taken from https://stackoverflow.com/questions/12587709/c-sharp-ssis-data-flow-component-creating-custom-input-columns
IDTSExternalMetadataColumnCollection100 externalColumnCollection = input.ExternalMetadataColumnCollection;
// Iterate through the virtual input column collection.
foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
{
// Call the SetUsageType method of the destination
// to add each available virtual input column as an input column.
wrp2.SetUsageType(
input.ID, vInput, vColumn.LineageID, DTSUsageType.UT_READONLY);
}
// Get the destination's default output collection
IDTSOutputCollection100 outColl = md2.OutputCollection;
// Iterate through the outputs in default output collection
foreach (IDTSOutput100 output in outColl)
{
// Iterate through the default output columns in the output
int count = output.OutputColumnCollection.Count;
foreach (IDTSOutputColumn100 outputColumn in output.OutputColumnCollection)
{
// Get the output's external metadata column collection
IDTSExternalMetadataColumnCollection100 extMetadataColumnColl = output.ExternalMetadataColumnCollection;
// Iterate through the external metadata column collection's external metadata columns
foreach (IDTSExternalMetadataColumn100 extMetadataColumn in extMetadataColumnColl)
{
// Call the MapOutPutColumn method of the destination to map
// each available output column to an external metadata column
wrp2.MapOutputColumn(
output.ID, outputColumn.ID, extMetadataColumn.ID, true);
}
}
}
md2.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface(conn);
md2.RuntimeConnectionCollection[0].ConnectionManagerID = conn.ID;
conn.AcquireConnection(null);
#region Save Package to FileSystem
string packageXml = #"C:\Users\test.dtsx";
XmlDocument myPkgDocument = new XmlDocument();
p.SaveToXML(ref myPkgDocument, null, null);
a.SaveToXml(packageXml, p, null);
#endregion
}
}
}
I think the problem that you are not mapping the input columns to the OLEDB Destination, and after opening the package, if you click on the OLEDB Destination and go to the Mapping section, it will automatically map the columns based on their names. The Foreach loop that is used by others are to loop over columns and map them to the related Destination columns.
There are many articles talking about creating SSIS package dynamically, you can refer to them for more information:
Dynamic Data Flow in SSIS using .NET/C#
Programmatically map the columns of a flat file destination?
Building Packages Programmatically
Samples for creating SSIS packages programmatically
Generating SSIS Packages Programmatically (Part I)
In my application, there is a need to create an excel file which should have an auto-completion feature. Suppose I am fetching some values "Data 1","Data 2" from the database, after fetching I want to bind those values to a particular column of excel. So when a user types something it would prompt "Data 1","Data 2"...
How to achieve this? help needed
You are describing a feature called Data Validation. It's main purpose is to limit the possible values of a cell to a certain range but, as you have spotted, this has the handy side-effect of having Excel show a drop down of the available values.
You can do something like this using various openXML libraries which allow you to create .xlsx files in .NET code. I like EPPlus which would let you do something like this:
static void Main(string[] args)
{
var fInfo = new FileInfo("output.xlsx");
using (var excel = new ExcelPackage())
{
var sht1 = excel.Workbook.Worksheets.Add("DataSheet1");
sht1.Cells[1,1].Value = "Occupation:";
var validation = sht1.DataValidations.AddListValidation("A2");
foreach(var allowedValue in GetAllowedValues())
{
validation.Formula.Values.Add(allowedValue);
}
excel.SaveAs(fInfo);
}
}
private static IEnumerable<string> GetAllowedValues()
{
return new string []{"Doctor","Baker","Candlestick Maker"};
}
This gives the dropdown list you are referring to (my screengrab is from LibreOffice but you will see the same effect in Excel):
Im sorry if this is a duplicate question, but i did a search and was unable to find info on what i was looking for. If you know of a qusetion to refer too, please link me!
But anyways, i have a function creating a class
private Item CreateItem(string name, bool stackable, int amount, string discription)
{
Item item = new Item(name, stackable, amount, discription);
return item;
}
Then i have another function that finds the stats
private Item findItemStats(string name)
{
if (name == "Gold")
return CreateItem(name, false, 0, "Gold Bar");
return null;
}
This is what im using to add the item too the inventory
internal void addItem(string name)
{
var item = findItemStats(name);
if (item == null)
Debug.LogError("Item not found!");
Instance.itemsToAdd.Add(item);
if (!inventory())
return;
if (inventory().activeInHierarchy)
{
placeItemsOnInventory();
sortItems();
}
My question is, whats a better way to store and retrieve the data of item stats. I at one point hosted a private server and on that, the item stats were stored in a .txt (or json w/e) and then would have a class for taking that data and placing it to the item that was being called. Was just curious of a way to either do that, or a way to store the data in a separate class/file with easy access and placement of the item data.
This can be a fairly wide open topic and depends on your needs. The simplest option if you are just saving something locally is using PlayerPrefs
PlayerPrefs Example:
PlayerPrefs.SetInt("Player Score", 10);
PlayerPrefs.Save();
//And to fetch:
var playerScore = PlayerPrefs.GetInt("Player Score");
More on using PlayerPrefs
Serialization Example Snippet.
For something more complex you can serialize your data to a data format such as XML, JSON, binary, CSV or any data that you want to import.This is an example of binary.
public void SaveData()
{
if (!Directory.Exists("Saves"))
Directory.CreateDirectory("Saves");
BinaryFormatter formatter = new BinaryFormatter();
FileStream saveFile = File.Create("Saves/save.binary");
LocalCopyOfData = PlayerState.Instance.localPlayerData;
formatter.Serialize(saveFile, LocalCopyOfData);
saveFile.Close();
}
public void LoadData()
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream saveFile = File.Open("Saves/save.binary", FileMode.Open);
LocalCopyOfData = (PlayerStatistics)formatter.Deserialize(saveFile);
saveFile.Close();
}
More on Saving and Loading player data
SqlLite
Alternatively you can use tooling for integrating a sqlite db into your project. The code for this looks like a standard db connection in .net.
string conn = "URI=file:" + Application.dataPath + "/PickAndPlaceDatabase.s3db"; //Path to database.
IDbConnection dbconn;
dbconn = (IDbConnection) new SqliteConnection(conn);
dbconn.Open(); //Open connection to the database.
IDbCommand dbcmd = dbconn.CreateCommand();
string sqlQuery = "SELECT value,name, randomSequence " + "FROM PlaceSequence";
dbcmd.CommandText = sqlQuery;
IDataReader reader = dbcmd.ExecuteReader();
How to Setup Sqlite withn Unity3d.
Cloud Hosting
For data that needs to be persist and be made available across multiple machines. You may want to consider hosting your data on a proper database or cloud hosted data store service. Some examples:
Unity Cloud Data is in alpha(As of 7/10/2016)
Firebase(Fun fact:Firebase was originally concieved to be a chat server tool for mmo's)
Play Fab
Game Sparks
Amazon RDS
Google Cloud Datastore(MySql)
Google Cloud Database(NoSql)
Azure Db
back4app (thanks #Joe Blow)
Other Data Storage options
Googling Backend as a service yields lots of other goodies as well. Sky's the limit!~
Unity has Scriptable objects that can be used to store data, and the objects get stored within the assets folder so easily accessible.
https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/scriptable-objects
So i have this project that needs to print data from the database.
I do this with a simple foreach loop:
public void LoadDatabase()
{
_connection.Open();
_dataAdapter.Fill(_dataTable);
try
{
foreach (DataRow row in _dataTable.Rows)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to LoadDatabase()" + ex.Message);
}
_connection.Close();
}
The problem is that it will loop anywhere from 2 times to 6 times meaning that it prints everything at least twice.
So at the moment my database contains Animal_Id's id:1 and id:2.
Now i get in my listbox (1, 2, 1, 2) or more depending on the amount of loops. I have no idea why this is happening and how to fix this. So all help would be highly appreciated
ps: if more code or information is needed please let me know.
pss: this is for a windows mobile 6.5 device with .net 3.5 build in Visual studio 2008. also im useing sqlite (not the newest version)
EDIT: After some testing it looks like my other 2 foreach loops in this project have the same problem.
Edit: So with al you guy's help i was able to fix it.
public void GetData()
{
try
{
SQLiteConnection Connection = new SQLiteConnection(#"Data Source = \Program Files\Mobile\Resources\Database\PPP_DB");
Connection.Open();
SQLiteCommand Command = new SQLiteCommand(Query, Connection);
Command.ExecuteNonQuery();
Data_Adapter = new SQLiteDataAdapter("SELECT * FROM Animal_Info", Connection);
Data_Set.Reset();
Data_Adapter.Fill(Data_Set);
Data_Table = Data_Set.Tables[0];
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
foreach (DataRow row in Data_Table.Rows)
{
if (row.RowState != DataRowState.Deleted)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
Connection.Close();
Program.AnimalInfo.Refresh();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
So it seems that it messed up with the DataTable somewhere, and that made it loop multiple times. ps: i tried to set all items with stuff like Databound, but didn't really work out for me, thats the reason im still doing it this way.
Add the following two lines at the top of your function.
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
_dataTable.Clear();
This will ensure that you do not double up any row data.
Assuming that there is no unique constraint on "Animal_ID" field in the database (you din't answer to my comment), check for items duplication:
if (!Program.AnimalInfo.Info_ID_ListBox.Items.Contains(row["Animal_ID"].ToString())
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
Well first, LoadDatabase() should return a DataTable (or DataSet), this make sthe LoadDatabase() function useful for other data requests, Also you should never need to use a 'for loop' to add items to a ListBox. you can bind the ListBox directly to the source.... something like this should do it
listBox1.DataSource = _dataTable;
listBox1.ValueMember = "Animal_ID";
listBox1.DisplayMember = "Animal_ID";
There are some examples using SqlDataReader and SqlDataAdapter here http://gsidev.somee.com/#2&2AD97ECBE2AE41D08191F6E4C773D8A9&cs
Unique contraints are not needed here. But the listbox.Items collection might have have to be cleared.
Possibly you are (or the system, depends on where the code is, for example in a paint event handler) calling the 'LoadDatabase()' function twice!
Just place a break point in the _dataAdapter.fill() and press F11 to let the app run over this and then right click the _dataTable to inspect it's contents.
Although you can explictely set the datasource of the ListBox, that is not needed and may lead to other side effects you are currently not aware of. It is OK to start with simple code before letting the system do unknwon stuff in the background.
Try that:
public void LoadDatabase()
{
_connection.Open();
_dataAdapter.Fill(_dataTable);
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
try
{
foreach (DataRow row in _dataTable.Rows)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to LoadDatabase()" + ex.Message);
}
_connection.Close();
}
I'll start by asking am I right in thinking that in the image below:
the 'TABLE=CLOASEUCDBA.T_BASIC_POLICY' is not part of the connection string? in fact it is the source table name?
I'm looking to alter this to another linked table on the same database. The connection string should there be the same and the name that appears in ACCESS should be the same. The only difference should be under the hood it is actually referencing another table and of course if you open the table it will contain different fields and data.
my code for far to do this is:
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\Users\xxxx\Documents\Test.accdb");
foreach (TableDef tbd in db.TableDefs)
{
if (tbd.Name.Contains("CLOASEUCDBA_T_BASIC_POLICY"))
{
tbd.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
}
}
db.Close();
However I'm getting a big fat COMException "Cannot set this property once the object is part of a collection.". I'm not sure exactly why and all the examples I can find online are all written in VB/VBA and I only have very very limited exposure to this. Any help is appreciated.
EDIT:
I have tried to go a different route with no futher success using the code:
if (tbd.Name.Contains("CLOASEUCDBA_T_BASIC_POLICY"))
{
var newtable = db.CreateTableDef("this is a new table");
newtable.Name = "new table";
newtable.Connect = tbd.Connect;
newtable.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
db.TableDefs.Append(newtable);
//tbd.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
}
In this case I get the error "ODBC--call failed."
Since we're not allowed to change the SourceTableName of a TableDef object that already exists in the TableDefs collection we need to create a new TableDef object, .Delete the old one, and then .Append the new one:
// This code requires the following COM reference in your project:
//
// Microsoft Office 14.0 Access Database Engine Object Library
//
// and the declaration
//
// using Microsoft.Office.Interop.Access.Dao;
//
// at the top of the class file
string tableDefName = "CLOASEUCDBA_T_BASIC_POLICY";
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\Users\xxxx\Documents\Test.accdb");
var tbdOld = db.TableDefs[tableDefName];
var tbdNew = db.CreateTableDef(tableDefName);
tbdNew.Connect = tbdOld.Connect;
tbdNew.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
db.TableDefs.Delete(tableDefName); // remove the old TableDef ...
db.TableDefs.Append(tbdNew); // ... and append the new one
db.Close();