I have custom collection which contains student_id,student_name,student_mark.And having the table with same columns in the database as well. The design form have some controls for updating the existing student.
Temporarily all the updating operations are done with that custom collection.Lets assume we have 100 students data in collection and database. Any updating operation should reflect in the collection. But what my doubt is how do i update these values with the database before i close the application??
But when i open the application the collection should have all the values which have stored in the database.
But what my doubt is how do i update these values with the database
Firstly, you need to know how to do CRUD operations on MySQL database with uwp app. For this, please reference this sample.
Secondly, according to your description, you have built up a MVVM project to bind a collection data to the view. But you didn't have a data layer for this MVVM structure. For this, you need to create a class for data layer to do GRUD operations, and establish contact with this data service from ViewModel. More details please reference this article.
The class for data layer I wrote according to your description which contains how to read, update and delete data from mysql database is as follows:
public class Student
{
public int Student_id { get; set; }
public string Student_name { get; set; }
public string Student_mark { get; set; }
}
public class DataService
{
static string connectionString;
public static String Name = "Data Service.";
private static ObservableCollection<Student> _allStudents = new ObservableCollection<Student>();
public static ObservableCollection<Student> GetStudents()
{
try
{
string server = "127.0.0.1";
string database = "sakila";
string user = "root";
string pswd = "!QAZ2wsx";
connectionString = "Server = " + server + ";database = " + database + ";uid = " + user + ";password = " + pswd + ";SslMode=None;";
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
MySqlCommand getCommand = connection.CreateCommand();
getCommand.CommandText = "SELECT * FROM student";
using (MySqlDataReader reader = getCommand.ExecuteReader())
{
while (reader.Read())
{
_allStudents.Add(new Student() { Student_id = reader.GetInt32(0), Student_name = reader.GetString(1), Student_mark = reader.GetString(2) });
}
}
}
}
catch (MySqlException sqlex)
{
// Handle it :)
}
return _allStudents;
}
public static bool InsertNewStudent(Student newStudent)
{
// Insert to the collection and update DB
try
{
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
MySqlCommand insertCommand = connection.CreateCommand();
insertCommand.CommandText = "INSERT INTO student(student_id, student_name, student_mark)VALUES(#student_id, #student_name,#student_mark)";
insertCommand.Parameters.AddWithValue("#student_id", newStudent.Student_id);
insertCommand.Parameters.AddWithValue("#student_name", newStudent.Student_name);
insertCommand.Parameters.AddWithValue("#student_mark", newStudent.Student_mark);
insertCommand.ExecuteNonQuery();
return true;
}
}
catch (MySqlException sqlex)
{
return false;
}
}
public static bool UpdateStudent(Student Student)
{
try
{
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
MySqlCommand insertCommand = connection.CreateCommand();
insertCommand.CommandText = "Update student Set student_name= #student_name, student_mark=#student_mark Where student_id =#student_id";
insertCommand.Parameters.AddWithValue("#student_id", Student.Student_id);
insertCommand.Parameters.AddWithValue("#student_name", Student.Student_name);
insertCommand.Parameters.AddWithValue("#student_mark", Student.Student_mark);
insertCommand.ExecuteNonQuery();
return true;
}
}
catch (MySqlException sqlex)
{
// Don't forget to handle it
return false;
}
}
public static bool Delete(Student Student)
{
try
{
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
MySqlCommand insertCommand = connection.CreateCommand();
insertCommand.CommandText = "Delete from sakila.student where student_id =#student_id";
insertCommand.Parameters.AddWithValue("#student_id", Student.Student_id);
insertCommand.ExecuteNonQuery();
return true;
}
}
catch (MySqlException sqlex)
{
return false;
}
}
}
For updating the database in TwoWay binding way, we can implement is by invoking the data updating method in
PropertyChanged event as follows:
void Person_OnNotifyPropertyChanged(Object sender, PropertyChangedEventArgs e)
{
organization.Update((StudentViewModel)sender);
}
For the completed demo you can download here.
Related
I have methods to get data from database as below. The problem is if I have a number of classes, I need to write a number of get-data methods to retrieve the list of corresponding objects although the the logic is the same, only the classes, properties and table name are different.
I want to know if it is possible to write only 1 method to use for all classes. The challenge part is I don't know to how to cast the reader data to the data type of the properties.
I will pass in the object, the columns and the table name. For example:
// I want to write this method so it can be used for all classes
public List<Object> getData(string className, string[] columns, string tableName) {...}
public List<Client> GetClients()
{
List<Client> list = new List<Client>();
try
{
conn.Open();
string sql = "SELECT id, clientName, info, hidden from clients";
MySqlCommand cmd = new MySqlCommand(sql, conn);
MySqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Client p = new Client((int)rdr[0], (string)rdr[1], (string)rdr[2], Convert.ToBoolean(rdr["hidden"]));
list.Add(p);
}
rdr.Close();
return list;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return null;
}
finally
{
conn.Close();
}
}
public List<RepPrefix> GetRepPrefixes()
{
List<RepPrefix> list = new List<RepPrefix>();
try
{
conn.Open();
string sql = "SELECT id, prefixName, hidden from repPrefix";
MySqlCommand cmd = new MySqlCommand(sql, conn);
MySqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
RepPrefix p = new RepPrefix((int)rdr[0], (string)rdr[1], Convert.ToBoolean(rdr["hidden"]));
list.Add(p);
}
rdr.Close();
return list;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return null;
}
finally
{
conn.Close();
}
}
If your Client and RepPrefix have settable properties with names that match the database columns, you can avoid the need to write a lot of boilerplate code by using a library like Dapper:
var clients = conn.Query<Client>("SELECT id, clientName, info, hidden from clients;").ToList();
var repPrefixes = conn.Query<RepPrefix>("SELECT id, prefixName, hidden from repPrefix;").ToList();
// ...
class Client
{
public int Id { get; set; }
public string ClientName { get; set; }
public string Info { get; set; }
public bool Hidden { get; set; }
}
It will map from DB column names to C# property names, perform the appropriate type conversions, etc.
If you don't want to use an external library, but want to write the code yourself, I would modify the signature of getData to take a function that takes a DbDataReader and returns an initialized object of type T:
// method that executes the query and invokes a callback to read the data
public List<T> getData<T>(string[] columns, string tableName, Func<DbDataReader, T> readData)
{
var list = new List<T>();
try
{
conn.Open();
string sql = "SELECT " + string.Join(",", columns) + " from " + tableName;
using var cmd = new MySqlCommand(sql, conn);
using var rdr = cmd.ExecuteReader();
while (rdr.Read())
{
// read the data from this row and construct a new T
T t = readData(rdr);
list.Add(p);
}
return list;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return null;
}
finally
{
conn.Close();
}
}
// an example of calling getData with a custom callback that creates Client objects
List<Client> GetClients() =>
getData(new[] { "id", "clientName", "info", "hidden" }, "clients",
rdr => new Client(rdr.GetInt32(0), rdr.GetString(1), rdr.GetString(2), rdr.GetBoolean(3)));
First, I highly recommend Dapper. Dapper will map objects to SQL statements for you and is performant.
If you have classes that align with your db tables, you could use reflection or some function on each class to generate SELECT, INSERT, and UPDATE statements and then use those same objects as your parameters into the Dapper functions.
I want to save images into my mysql database.
This is my code:
The class:
public async void SIE()
{
TrialClass trialClass = new TrialClass(ImagesPaths1);
BlogRestClient<TrialClass> restClient = new BlogRestClient<TrialClass>();
await restClient.PostAsync(trialClass);
}
public class TrialClass
{
public List<ImageFormatClass> ImagesPath2;
public TrialClass(List<ImageFormatClass> imagespath)
{
ImagesPath2 = imagespath;
}
}
public class ImageFormatClass
{
public Image SavedImage;
public int Format;
public ImageFormatClass()
{
}
}
private void Post_Clicked(object sender, EventArgs e)
{
SIE();
}
Web API controller:
public void Post([FromBody] TrialClass value)
{
foreach (ImageFormatClass s in value.ImagesPath2)
{
string sqlstring = "server=; port= ; user id =;Password=;Database=;";
MySqlConnection conn = new MySqlConnection(sqlstring);
try
{
conn.Open();
}
catch (MySqlException ex)
{
throw ex;
}
string Query = "INSERT INTO test.blogimagestable (ImagesId)values( ?str);";
MySqlCommand cmd = new MySqlCommand(Query, conn);
cmd.Parameters.Add("?str", MySqlDbType.VarString, 256).Value = s.SavedImage;
cmd.ExecuteReader();
conn.Close();
}
}
Anytime I press the post button to fire the Post_Clicked event, I get this error
Newtonsoft.Json.JsonSerializationException: 'Self referencing loop detected for property 'ManifestModule' with type 'System.Reflection.RuntimeModule'. Path 'ImagesPath2[0].SavedImage.Source.Stream.Method.Module.Assembly'.'
at `await restClient.PostAsync(trialClass);
Good day!
Using visual studio 2012, I have created a Student class with get and set codes, and i need to complete the StudentDAO class to create insert coding that will use to store data to database student table. this action is perform by a windows form button click event.
what i need to create a button click code and then insert into database code,
//Student.cs class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SRSJason
{
class Student
{
private string S_Student_id;
private string S_Full_name;
private DateTime S_Dob;
private string S_Address;
private int S_Contact;
private string S_Username;
private string S_Password;
public Student() //Default constructor
{
}
public Student(string Student_id, string Full_name, DateTime Dob, string Address, int Contact, string Username, string Password) //Overloadign
{
S_Student_id = Student_id;
S_Full_name = Full_name;
S_Dob = Dob;
S_Address = Address;
S_Contact = Contact;
S_Username = Username;
S_Password = Password;
}
public void setID(string Student_id)
{
S_Student_id = Student_id;
}
public string getID()
{
return S_Student_id;
}
public void setName(string Full_name)
{
S_Full_name = Full_name;
}
public string getName()
{
return S_Full_name;
}
public void setDob(DateTime Dob)
{
S_Dob = Dob;
}
public DateTime getDob()
{
return S_Dob;
}
public void setAddress(string Address)
{
S_Address = Address;
}
public string getAddress()
{
return S_Address;
}
public void setContact(int Contact)
{
S_Contact = Contact;
}
public int getContact()
{
return S_Contact;
}
public void setUsername(string Username)
{
S_Username = Username;
}
public string getUsername()
{
return S_Username;
}
public void setPassword(string Password)
{
S_Password = Password;
}
public string getPassword()
{
return S_Password;
}
}
}`
//StudentDAO class (please help me to complete this code)
`class StudentDAO
{
static string constring = "Data Source=JAZE;Initial Catalog=srsjason;Integrated Security=True";
SqlConnection m_con = new SqlConnection(constring);
}`
//button click from the form (please help me to complete this code as well)
private void submitstudent(object sender, EventArgs e)
{
}
please help me to complete this coding
First of all when you create private properties u cant access them in your forms, you have to create a method instead inside the same class then use it in your form. second you should know about the ORM - Object Relational Mapping that you are using.
Here I'll list them:
ADO.NET
LINQ to SQL
ADO.NET Entity Framework
When you picked one of those. next step would be learning about how they work and whats the syntax.
However knowing that you kinda showed a syntax of ADO.NET Here is an Example how you can insert the data in your data base using ADO.NET. If you want to add data directly from code-behind without a method. so basically on click event of your button.
private void btn_Click(object sender, EventArgs e)
{
try
{
//create object of Connection Class..................
SqlConnection con = new SqlConnection();
// Set Connection String property of Connection object..................
con.ConnectionString = "Data Source=KUSH-PC;Initial Catalog=test;Integrated Security=True";
// Open Connection..................
con.Open();
//Create object of Command Class................
SqlCommand cmd = new SqlCommand();
//set Connection Property of Command object.............
cmd.Connection = con;
//Set Command type of command object
//1.StoredProcedure
//2.TableDirect
//3.Text (By Default)
cmd.CommandType = CommandType.Text;
//Set Command text Property of command object.........
cmd.CommandText = "Insert into Registration (Username, password) values ('#user','#pass')";
//Assign values as `parameter`. It avoids `SQL Injection`
cmd.Parameters.AddWithValue("user", TextBox1.text);
cmd.Parameters.AddWithValue("pass", TextBox2.text);
//Execute command by calling following method................
//1.ExecuteNonQuery()
//This is used for insert,delete,update command...........
//2.ExecuteScalar()
//This returns a single value .........(used only for select command)
//3.ExecuteReader()
//Return one or more than one record.
cmd.ExecuteNonQuery();
con.Close();
MessageBox.Show("Data Saved");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
con.Close();
}
}
Be sure that you've included your ConnectionString in your config file.
I'm trying to use SQLite as my storage. I've added reference dll using nuget and using statement as well.
I have
private void SetConnection()
{
sql_con = new SQLiteConnection
("Data Source=c:\\Dev\\MYApp.sqlite;Version=3;New=False;Compress=True;");
}
private void ExecuteQuery(string txtQuery)
{
SetConnection();
sql_con.Open();
sql_cmd = sql_con.CreateCommand();
sql_cmd.CommandText = txtQuery;
sql_cmd.ExecuteNonQuery();
sql_con.Close();
}
and I'm sending query txt like this
public void Create(Book book)
{
string txtSqlQuery = "INSERT INTO Book (Id, Title, Language, PublicationDate, Publisher, Edition, OfficialUrl, Description, EBookFormat) ";
txtSqlQuery += string.Format("VALUES (#{0},#{1},#{2},#{3},#{4},#{5},#{6},#{7},{8})",
book.Id, book.Title, book.Language, book.PublicationDate, book.Publisher, book.Edition, book.OfficialUrl, book.Description, book.EBookFormat);
try
{
ExecuteQuery(txtSqlQuery);
}
catch (Exception ex )
{
throw new Exception(ex.Message);
}
}
My db is correctly created and passed book instance with valid data is ok. But exception is thrown always on executing query on this line of code:
sql_cmd.ExecuteNonQuery();
I obviously doing something wrong here but I cannot see.
Update: thrown exception message is
SQL logic error or missing database
unrecognized token: "22cf"
where this 22cf is part of passed book.Id guid string.
Don't EVER insert your data in your statement!
Use prepared statements and bind parameters:
public void Create(Book book) {
SQLiteCommand insertSQL = new SQLiteCommand("INSERT INTO Book (Id, Title, Language, PublicationDate, Publisher, Edition, OfficialUrl, Description, EBookFormat) VALUES (?,?,?,?,?,?,?,?,?)", sql_con);
insertSQL.Parameters.Add(book.Id);
insertSQL.Parameters.Add(book.Title);
insertSQL.Parameters.Add(book.Language);
insertSQL.Parameters.Add(book.PublicationDate);
insertSQL.Parameters.Add(book.Publisher);
insertSQL.Parameters.Add(book.Edition);
insertSQL.Parameters.Add(book.OfficialUrl);
insertSQL.Parameters.Add(book.Description);
insertSQL.Parameters.Add(book.EBookFormat);
try {
insertSQL.ExecuteNonQuery();
}
catch (Exception ex) {
throw new Exception(ex.Message);
}
}
To insert the records into the SqliteDB:
using System.Data.SQLite;
private void btnInsert_Click(object sender, RoutedEventArgs e)
{
string connection = #"Data Source=C:\Folder\SampleDB.db;Version=3;New=False;Compress=True;";
SQLiteConnection sqlite_conn = new SQLiteConnection(connection);
string stringQuery ="INSERT INTO _StudyInfo"+"(Param,Val)"+"Values('Name','" + snbox.Text + "')";//insert the studyinfo into Db
sqlite_conn.Open();//Open the SqliteConnection
var SqliteCmd = new SQLiteCommand();//Initialize the SqliteCommand
SqliteCmd = sqlite_conn.CreateCommand();//Create the SqliteCommand
SqliteCmd.CommandText = stringQuery;//Assigning the query to CommandText
SqliteCmd.ExecuteNonQuery();//Execute the SqliteCommand
sqlite_conn.Close();//Close the SqliteConnection
}
Update most recent version using Dapper (Dapper.2.0.90) and SQLite (System.Data.SQLite.Core.1.0.114.2)
using System.Data.SQLite;
using Dapper;
connection string (DB name - 'Store.db')
connectionString="Data Source=.\Store.db;Version=3;"
Person save method in my application
public static void SavePerson(PersonModel person)
{
using (IDbConnection cnn = new SQLiteConnection(connectionString))
{
cnn.Execute("insert into Person (FirstName, LastName) values (#FirstName, #LastName)", new { person.FirstName, person.LastName} );
}
}
person model
public class PersonModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get
{
return $"{ FirstName } { LastName }";
}
}
}
This answer includes the code methods I used and use in my applications.
You should use (') when sending string into INSERT statement
VALUES (#{0},'#{1}','#{2}','#{3}','#{4}','#{5}','#{6}','#{7}',{8})
you should also catch SQLExeprion
I'd like to implement N-tier architecture in my WinForms applications to separate (just logically - in one project) business logic from data access, however I have some doubts about using transacion in BLL. All tutorials I've found in the Internet either are very simple implementations of that architecture (without transactions), or are too complex for my needs. Trying to find my own way, I've come to the point, where I don't know the best way to handle transactions of in BLL layer.
I'll try to use some simple example to illustrate the problem (all classes are in separate files):
//DTO - Data Transfer Objects
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SomeOtherItem
{
public int Id { get; set; }
public string Name { get; set; }
}
//DAL - Data Access layer
public class ItemDAL
{
public ItemDAL()
{
}
public void Add(Item item)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", item.Name);
cmd.ExecuteNonQuery();
}
}
}
}
public class SomeOtherItemDAL
{
public SomeOtherItemDAL()
{
}
public void Add(SomeOtherItem someOtherItem)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_some_other_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", someOtherItem.Name);
cmd.ExecuteNonQuery();
}
}
}
}
//BLL - Business Logic Layer
public class SomeBLL
{
public SomeBLL()
{
}
public void Add(Item item, SomeOtherItem someOtherItem)
{
ItemDAL itemDAL = new ItemDAL();
SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL();
// *** this must be done in one transaction ***
itemDAL.Add(item);
someOtherItemDAL.Add(someOtherItem);
}
}
Now, the problem is that if I want to use Transacion, I cannot use:
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
in DAL. To use NpgsqlTransacion object I must somehow keep connection opened and visible in both DAL classes.
I've tried use TransacionScope object for that, but from from some reasons it's not working with PostgreSQL and the driver I'm using (INSERTS are done just after executed and there is no transaction rollback when exception within TransacionScope occures).
What I've come into is to make additional Singleton class to keep connection alive and manage transactions:
public class DB
{
private static DB instance;
private const string connString = #"Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass";
private NpgsqlConnection conn;
private DB()
{
conn = new NpgsqlConnection(connString);
}
public static DB Instance
{
get
{
if (instance == null)
{
instance = new DB();
}
return instance;
}
}
#region --- connection ---
public NpgsqlConnection GetOpenConnection()
{
OpenConnection();
return conn;
}
private void OpenConnection()
{
if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken)
conn.Open();
}
public void CloseConnection()
{
if (conn != null && !inTransaction)
{
conn.Close();
}
}
#endregion
#region --- transaction ---
private NpgsqlTransaction trans;
private bool inTransaction;
public bool InTransaction { get { return inTransaction; } }
public void TransactionStart()
{
OpenConnection();
trans = conn.BeginTransaction();
inTransaction = true;
}
public void TransactionCommit()
{
if (inTransaction)
{
try
{
trans.Commit();
trans.Dispose();
}
finally
{
inTransaction = false;
CloseConnection();
}
}
}
public void TransactionRollback()
{
if (inTransaction)
{
try
{
trans.Rollback();
trans.Dispose();
}
finally
{
inTransaction = false;
CloseConnection();
}
}
}
#endregion
}
and rebuild both DAL Add methods to access connection like that:
//DAL - Data Access layer
public class ItemDAL
{
public ItemDAL()
{
}
public void Add(Item item)
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = DB.Instance.GetOpenConnection();
cmd.CommandText = #"INSERT INTO tbl_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", item.Name);
cmd.ExecuteNonQuery();
}
if (!DB.Instance.InTransaction)
DB.Instance.CloseConnection();
}
}
public class SomeOtherItemDAL
{
public SomeOtherItemDAL()
{
}
public void Add(SomeOtherItem someOtherItem)
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = DB.Instance.GetOpenConnection();
cmd.CommandText = #"INSERT INTO tbl_some_other_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", someOtherItem.Name);
cmd.ExecuteNonQuery();
}
if (!DB.Instance.InTransaction)
DB.Instance.CloseConnection();
}
}
Note that I'd like to follow the rule to "close database connection as soon as possible", so when Add method is called without transaction scope, I'd like it to close the connection.
So, the final questions are:
1. What do you think about it, is there a better way to handle that issue, any suggestions?
2. Should I dispose a connection in DB.CloseConnecion()? I surely do when using using (NpgsqlConnection conn = ...) { ... } pattern, but as Singleton is alive as long as application, does it make sense? Connection is returned to ConnectionPool after Close(), isn't it? Or maybe I should also dispose a Singleton object (together with connection), after each using?
3. It's not directly connected question, but if I use DTO objects (just properties, no methods) and have also some BusinessObjects (BO) with the same properties, but also with additional methods (validations, calculations, operations etc.), can it be inherited from DTO? Or maybe I can use full BusinessObject to transfer it between layers, and get rid off DTO?
EDIT: TransacionScope
As requested, I add some code from my tries with TransactionScope. Simply WinForm application, no Exceptions handling. As a result, there is an Exception window when I throw it, but in database I see records with values test1 ans test2. Both when debbuging in VS and executing application from .exe
using Npgsql;
using System.Transactions;
//...
private void button1_Click(object sender, EventArgs e)
{
using (System.Transactions.TransactionScope scope = new TransactionScope())
{
AddValue("test1");
AddValue("test2");
throw new Exception("bam!");
AddValue("test3");
scope.Complete();
}
}
private void AddValue(string value)
{
string connString = "Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass";
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_test (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", value);
cmd.ExecuteNonQuery();
}
}
}
I've never used NpgSql, but reading the documentation of NpgSql it appears they have some support of TransactionScope() if you add "enlist=true" in your connection string.
I'm looking at the "System.Transactions Support" section of the below NpgSql documentation:
http://npgsql.projects.postgresql.org/docs/manual/UserManual.html
Assuming TransactionScope() did work, then you can do simething like this...
using (var scope = new System.Transactions.TransactionScope())
{
ItemDAL itemDAL = new ItemDAL();
SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL();
// *** this must be done in one transaction ***
itemDAL.Add(item);
someOtherItemDAL.Add(someOtherItem);
scope.Complete()
}
What you did is brave, but not scalable. I'm not familiar with PGSQL, but this problem is the exact reason why the TransactionScope API was designed.
Can you show your code using the TransactionScope api? Make sure that you are not calling scope.Complete(); if an error occurs in one of the methods. Be careful to not "eat" the exceptions inside the methods because in that case the flow will continue just like if nothing happened.
More reading about TransactionScope here:
http://msdn.microsoft.com/en-us/library/ms172152.aspx
Update 1
Thanks for sharing your code that uses the TransactionScope class. The code looks perfectly correct to me. According to this (http://npgsql.projects.postgresql.org/docs/manual/UserManual.html) document (the same quoted by ChrisNeil52), Enlist=true should be included in the connection string for transactions to work.
You might be dealing with a buggy API. Good luck with this.
I know this sounds peculiar, but something I would try would be to use a different NpgsqlCommand constructor. new NpgsqlCommand("sql query", connection), instead of creating the command and assigning it the connection. They should be equivalent. but who know...