I am fetching my Database object in PetaPoco using this method:
internal static Database GetCurrentDatabase()
{
if (HttpContext.Current == null)
{
if (Thread.GetData(Thread.GetNamedDataSlot(_databaseKey)) != null)
return Thread.GetData(Thread.GetNamedDataSlot(_databaseKey)) as Database;
else
{
var database = new Database("testDB");
database.KeepConnectionAlive = true;
database.EnableAutoSelect = true;
database.CommandTimeout = 180;
Thread.SetData(Thread.GetNamedDataSlot(_databaseKey), database);
return database;
}
}
else
{
if (HttpContext.Current.Items.Contains(_databaseKey))
return HttpContext.Current.Items[_databaseKey] as Database;
else
{
Database database = new Database("testDB");
database.EnableAutoSelect = true;
database.CommandTimeout = 180;
database.KeepConnectionAlive = true;
HttpContext.Current.Items[_databaseKey] = database;
return database;
}
}
}
My connection string in web.config:
<add name="testDB" connectionString="Data Source=192.168.10.10;Initial Catalog=testDB;User Id=test;Password=test;pooling='true'; Max Pool Size=200"
providerName="System.Data.SqlClient" />
Question is already connection pooling is enabled. Do I have to set KeepConnectionAlive as true or false? What is the use of this property?
Yes, pooling is already enable and you don't need to set KeepConnectionAlive to true (in fact, it will bring trouble if you set it to true)
Related
We had security threat issue when scanning applications in Veracode. Got "External Control of System or Configuration Setting (CWE ID 15)".
Scan reported for using (var connection = new SqlConnection(connectionString))
we are checking whether "SQLConnectionExists" by passing connection string,
string sqlConnString = SqlHelper.GetSQLConnectionString(input.ServerName, dbName, isWinAuth, input.UserName, input.Password);
if (!DBUtil.CheckSQLConnectionExists(sqlConnString))
{
_ValidationMessage += "Database Unreachable \n";
isValid = false;
}
public static bool CheckSQLConnectionExists(string connectionString)
{
bool isExist = false;
try
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
connection.Close();
isExist = true;
}
}
catch (Exception ex)
{
Logger.Instance.Log(LogLevel.EXCEPTION, "CheckSQLConnectionExists Exception : " + ex.Message);
}
return isExist;
}
public static string GetSQLConnectionString(string servername, string db, bool isWinAuth, string username, string password)
{
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
builder["Data Source"] = servername;
builder["Initial Catalog"] = db;
if (isWinAuth)
{
builder["Integrated Security"] = "SSPI";
builder["Trusted_Connection"] = "Yes";
}
else
{
builder["Persist Security Info"] = false;
builder["User ID"] = username;
builder["Password"] = password;
}
return builder.ConnectionString;
}
In this line using (var connection = new SqlConnection(connectionString)) we got error in security scan. Could you please some one provide suggestions to resolve this Veracode error.
Veracode detects input.ServerName, input.UserName and input.Password to be user-controlled which is a risk.
Ensure validation is implemented - if possible, compare against a whitelist or known predefined server names. Also, check if the entered (injected) Min Pool Size is larger than expected. Use framework classes such as the one that you used SqlConnectionStringBuilder
Propose this check as a mitigation afterwards.
I'm having trouble updating values in Entity Framework 6, I've looked thoroughly through the internet for answers but it seems I'm doing everything just fine, yet I can't seem to make it work.
It's worth mentioning that adding entities into the DB works just fine, meaning I can add products/users in my project to the DB, but not update them.
This is homework.
public bool ChangeAccountStatus(long userID, bool isUserActive)
{
User userToChange = GetUserById(userID); // Gets the user whose values I want to change.
using (var context = new ShopContext())
{
if (userToChange != null)
{
if (isUserActive)
{
userToChange.IsActive = false;
context.SaveChanges();
}
else
{
userToChange.IsActive = true;
context.SaveChanges();
}
return true;
}
return false;
}
}
I can make the update work if I use linq to find the user whose value I want to change, but I don't want too much code-duplication in my project, and I'm using the same linq (function GetUserById) in many other functions.
Do I need to use linq to access the user from the database, instead of using a function I created to avoid code-duplication?
This is the GetUserById function:
public User GetUserById(long userId)
{
using (var context = new ShopContext())
{
var userToFind = context.UsersTable
.Where((u) => u.Id == userId).FirstOrDefault();
if (userToFind != null)
return userToFind;
else
return null;
}
}
You are retrieving an entity from one context, then calling SaveChanges() on a different context. If you inline the method, it becomes more clear:
var userToChange;
using (var context = new ShopContext())
{
userToChange = context.UsersTable.Where((u) => u.Id == userId).FirstOrDefault();
}
using (var context = new ShopContext())
{
if (userToChange != null)
{
if (isUserActive)
{
userToChange.IsActive = false;
context.SaveChanges();
}
else
{
userToChange.IsActive = true;
context.SaveChanges();
}
return true;
}
return false;
}
The second context doesn't know anything about userToChange, because it isn't tracking it. You can tell it to though:
context.UsersTable.Attach(userToChange);
As an aside, you have some redundant code there - an if statement, which assigns a boolean to true or false can be simplified:
if (isUserActive)
userToChange.IsActive = false;
else
userToChange.IsActive = true;
// Equivalent to:
userToChange.IsActive = !isUserActive;
And the null check is not necessary:
if (userToFind != null)
return userToFind;
else
return null;
// Equivalent to:
return userToFind;
have look at below codes
Here is my _cSynchronization Class where Sync Function are precent,
(500) in connection string means timeout = 500
public static class _cSynchronization
{
public static int transactionCount;
public static uint BatchSize = 10000;
public static uint MemorySize = 20000;
public static List<string> _MGetAllTableList()
{
List<string> list = new List<string>();
DataRowCollection _dr = _CObjectsofClasses._obj_CDatabase._MGetDataRows("Select TABLE_NAME From INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME <> N'AUTOBACKUPSET' AND TABLE_NAME <> N'BINDATA' AND TABLE_NAME <> N'_ATTENDANCESTATUS' AND TABLE_NAME NOT like '%_tracking%' AND TABLE_TYPE ='BASE TABLE' AND TABLE_NAME <> N'schema_info' AND TABLE_NAME <> N'scope_info' AND TABLE_NAME <> N'scope_config' AND TABLE_NAME <> '_CLIENTNAME' AND TABLE_NAME <> '_TABSETTING' AND TABLE_NAME <> '_EMPLOYEEPAYMENT1' AND TABLE_NAME <> '_LOCALCOMPANYINFO' ORDER BY TABLE_NAME");
int a = 0;
string x = "";
if (_dr.Count > 0)
{
_CPubVar._value_I = 0;
_CPubVar._MaxValue_I = _dr.Count + 2;
_CPubVar._IsTableProcess_bool = true;
foreach (DataRow _row in _dr)
{
_CPubVar._value_I++;
_CPubVar._ProcessText_S = "Preparing Tables " + _CPubVar._value_I + " of " + _CPubVar._MaxValue_I;
x = _CObjectsofClasses._obj_CConvert._MConvertToString(_row[0]);
// serverConn.Open();
list.Add(x);
}
}
return list;
}
public static void SetUp(string _pTableName)
{
// Connection to SQL Server database
SqlConnection serverConn = new SqlConnection(_CObjectsofClasses._obj_CConnectionString._MGetServerConnectionString(500));
// Connection to SQL client database
SqlConnection clientConn = new SqlConnection(_CObjectsofClasses._obj_CConnectionString._MGetConnectionString(500));
// Create a scope named "product" and add tables to it.
DbSyncScopeDescription productScope = new DbSyncScopeDescription(_pTableName + "_SCOP");
// Select the colums to be included in the Collection Object
// Define the Products table.
DbSyncTableDescription productDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable(_pTableName,serverConn);
// Add the Table to the scope object.
productScope.Tables.Add(productDescription);
// Create a provisioning object for "product" and apply it to the on-premise database if one does not exist.
SqlSyncScopeProvisioning serverProvision = new SqlSyncScopeProvisioning(serverConn, productScope);
serverProvision.ObjectSchema = ".dbo";
//
serverProvision.SetCreateProceduresForAdditionalScopeDefault(DbSyncCreationOption.Create);
serverProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);
serverProvision.SetCreateProceduresDefault(DbSyncCreationOption.CreateOrUseExisting);
serverProvision.SetCreateTrackingTableDefault(DbSyncCreationOption.CreateOrUseExisting);
serverProvision.SetCreateTriggersDefault(DbSyncCreationOption.CreateOrUseExisting);
if (!serverProvision.ScopeExists(_pTableName + "_SCOP"))
serverProvision.Apply();
// Provision the SQL client database from the on-premise SQL Server database if one does not exist.
SqlSyncScopeProvisioning clientProvision = new SqlSyncScopeProvisioning(clientConn, productScope);
if (!clientProvision.ScopeExists(_pTableName + "_SCOP"))
clientProvision.Apply();
// Shut down database connections.
serverConn.Close();
serverConn.Dispose();
clientConn.Close();
clientConn.Dispose();
}
public static List<_CSyncDetails> Synchronize(string _pScopeName, SyncDirectionOrder _pDirection)
{
// Connection to SQL Server database
SqlConnection serverConn = new SqlConnection(_CObjectsofClasses._obj_CConnectionString._MGetServerConnectionString(500));
// Connection to SQL client database
SqlConnection clientConn = new SqlConnection(_CObjectsofClasses._obj_CConnectionString._MGetConnectionString(500));
List<_CSyncDetails> _Statics = new List<_CSyncDetails>();
// Perform Synchronization between SQL Server and the SQL client.
SyncOrchestrator syncOrchestrator = new SyncOrchestrator();
// Create provider for SQL Server
SqlSyncProvider serverProvider = new SqlSyncProvider(_pScopeName, serverConn);
// Set the command timeout and maximum transaction size for the SQL Azure provider.
SqlSyncProvider clientProvider = new SqlSyncProvider(_pScopeName, clientConn);
clientProvider.CommandTimeout = serverProvider.CommandTimeout = 500;
//Set memory allocation to the database providers
clientProvider.MemoryDataCacheSize = serverProvider.MemoryDataCacheSize = MemorySize;
//Set application transaction size on destination provider.
serverProvider.ApplicationTransactionSize = BatchSize;
//Count transactions
serverProvider.ChangesApplied += new EventHandler<DbChangesAppliedEventArgs>(RemoteProvider_ChangesApplied);
// Set Local provider of SyncOrchestrator to the server provider
syncOrchestrator.LocalProvider = serverProvider;
// Set Remote provider of SyncOrchestrator to the client provider
syncOrchestrator.RemoteProvider = clientProvider;
// Set the direction of SyncOrchestrator session to Upload and Download
syncOrchestrator.Direction = _pDirection;
// Create SyncOperations Statistics Object
SyncOperationStatistics syncStats = syncOrchestrator.Synchronize();
_Statics.Add(new _CSyncDetails { UploadChangesTotal = syncStats.UploadChangesTotal, SyncStartTime = syncStats.SyncStartTime, DownloadChangesTotal = syncStats.DownloadChangesTotal, SyncEndTime = syncStats.SyncEndTime });
// Display the Statistics
// Shut down database connections.
serverConn.Close();
serverConn.Dispose();
clientConn.Close();
clientConn.Dispose();
return _Statics;
}
}
Here the function where I am sync
private void _MSync()
{
_CPubVar._IsContinue = true;
_CPubVar._PausebtnCondition = 0;
// _cSynchronization._MClearSyncprovision();
_CPubVar._Stop_bool = false;
this.Text += " - Started at : " + DateTime.Now;
string a = "";
// Define the Products table.
List<string> _Tablelist = new List<string>();
Collection<string> _ColNames = new Collection<string>();
_list1.Add(new _CSyncDetails { SyncStartTime = DateTime.Now });
_Tablelist.AddRange(_cSynchronization._MGetAllTableList());
SyncDirectionOrder _order = SyncDirectionOrder.Download;
_CPubVar._MaxValue_I = (_Tablelist.Count * 2);
_CPubVar._value_I = 0;
foreach (string tbl in _Tablelist)
{
try
{
a = Regex.Replace(Environment.MachineName + Environment.UserName, #"[^0-9a-zA-Z]+", "").ToUpper() + "_" + tbl + "_SCOPE";
_CPubVar._value_I++;
_CPubVar._ProcessText_S = "Sync Tables " + _CPubVar._value_I + " of " + _CPubVar._MaxValue_I;
_cSynchronization.SetUp(tbl);
if (_CPubVar._IsServerRunning_bool)
{
_order = SyncDirectionOrder.DownloadAndUpload;
}
else
{
if (tbl == "_BANK" || tbl == "_BANKACCOUNT" || tbl == "_CLIENTNAME" || tbl == "_PACKAGE" || tbl == "_PACKAGEDET" || tbl == "_PAYMENTEXPENCES" || tbl == "_PROJECT" || tbl == "_PROJECTDET" || tbl == "_REQUIREMENT" || tbl == "_REQUIREMENTDET" || tbl == "_SERVER" || tbl == "_UNIT" || tbl == "_ITEM" || tbl == "ManageUser" || tbl == "USERPERMISSION" || tbl == "USERROLE" || tbl == "USERROLEDET")
{
_order = SyncDirectionOrder.DownloadAndUpload;
}
else
{
_order = SyncDirectionOrder.Download;
}
}
_CPubVar._value_I++;
_CPubVar._ProcessText_S = "Sync Tables " + _CPubVar._value_I + " of " + _CPubVar._MaxValue_I;
if (tbl != "_COMPANYINFO")
{
_list1.AddRange(_cSynchronization.Synchronize(tbl + "_SCOP", _order));
}
else
{
if (_CPubVar._IsServerRunning_bool)
{
_list1.AddRange(_cSynchronization.Synchronize(tbl + "_SCOP", SyncDirectionOrder.DownloadAndUpload));
}
}
}
catch (Exception exx)
{
_syncErr.Add(new _CSyncErrors { SyncErrorDateTime = DateTime.Now, SyncErrorMessage = exx.Message, SyncTableAlies = _CTableName._MgetTableAlies(tbl) });
pictureBox1.Visible = label3.Visible = true;
label3.Text = _syncErr.Count.ToString();
Application.DoEvents();
continue;
}
}
thread.Abort();
}
Problem :
Above codes are working fine for only one PC Sync at a time (let Take A) there is no error and done.
one PC Sync at a time (let Take B) there is no error and done.
But when I am trying to run Application simultaneously (A and B Same time) then for some table I am getting
Cannot enumerate changes at the RelationalSyncProvider for table 'TableName'
Running Status
PC A PC B Result
YES NO No Error
NO YES No Error
YES YES Error
Please Note That On Client side MY database is 2008 and Server side MY database is 2012
Where I am wrong
UPDATE :
I have 72 tables in database and below Error is not specific 'TableName' it may be any table from 72,
For Example Table1 gives me Error, and after Sync done if I rerun application may be this error not come.
Cannot enumerate changes at the RelationalSyncProvider for table 'TableName'
Check the Timeout
Eight minutes might not be enough time. Try increasing the synchronization command timeout to an absurd number to find out if that's the problem.
clientProvider.CommandTimeout = 3000;
serverProvider.CommandTimeout = 3000;
Turn on Tracing
Edit the app.config file for your application by adding the following system.diagnostics segment. It will log verbosely to C:\MySyncTrace.txt.
<configuration>
<system.diagnostics>
<switches>
<!--4-verbose.-->
<add name="SyncTracer" value="4" />
</switches>
<trace autoflush="true">
<listeners>
<add name="TestListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="c:\MySyncTrace.txt"/>
</listeners>
</trace>
</system.diagnostics>
</configuration>
My Recreation
I tried to recreate the error that you are experiencing. I created a simplified version of what you are trying to accomplish. It builds and successfully synchronizes two SqlExpress databases.
Unfortunately, I wasn't able to recreate the error. Here is the setup that I used and the test cases afterwards.
Mock Databases Create Script
USE Master;
GO
IF EXISTS (
SELECT *
FROM sys.databases
WHERE NAME = 'SyncTestServer'
)
DROP DATABASE SyncTestServer
GO
CREATE DATABASE SyncTestServer;
GO
CREATE TABLE SyncTestServer.dbo.Table1 (Column1 VARCHAR(50) PRIMARY KEY)
CREATE TABLE SyncTestServer.dbo.Table2 (Column1 VARCHAR(50) PRIMARY KEY)
INSERT INTO SyncTestServer.dbo.Table1 (Column1)
VALUES ('Server Data in Table1')
INSERT INTO SyncTestServer.dbo.Table2 (Column1)
VALUES ('Server Data in Table2')
IF EXISTS (
SELECT *
FROM sys.databases
WHERE NAME = 'SyncTestClient'
)
DROP DATABASE SyncTestClient
GO
CREATE DATABASE SyncTestClient;
GO
CREATE TABLE SyncTestClient.dbo.Table1 (Column1 VARCHAR(50) PRIMARY KEY)
CREATE TABLE SyncTestClient.dbo.Table2 (Column1 VARCHAR(50) PRIMARY KEY)
INSERT INTO SyncTestClient.dbo.Table1 (Column1)
VALUES ('Client Data in Table1')
INSERT INTO SyncTestClient.dbo.Table2 (Column1)
VALUES ('Client Data in Table2')
Mock Console Application
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
using Microsoft.Synchronization;
namespace StackOverflow_SyncFramework
{
public class _CSyncDetails
{
public int UploadChangesTotal;
public DateTime SyncStartTime;
public int DownloadChangesTotal;
public DateTime SyncEndTime;
}
public class Program
{
static void Main(string[] args)
{
_cSynchronization sync = new _cSynchronization();
sync._MSync();
Console.ReadLine();
}
}
public class _cSynchronization
{
public static int transactionCount;
public static uint BatchSize = 10000;
public static uint MemorySize = 20000;
public const string ServerConnString =
#"Data Source=.\SQLExpress;initial catalog=SyncTestServer;integrated security=True;MultipleActiveResultSets=True;";
public const string ClientConnString =
#"Data Source=.\SQLExpress;initial catalog=SyncTestClient;integrated security=True;MultipleActiveResultSets=True;";
public static List<string> _MGetAllTableList()
{
// I just created two databases that each have the following table
// Synchronization is working
List<string> list = new List<string>()
{
"Table1",
"Table2"
};
return list;
}
public static void SetUp(string _pTableName)
{
// Connection to SQL Server database
SqlConnection serverConn =
new SqlConnection(ServerConnString);
// Connection to SQL client database
SqlConnection clientConn =
new SqlConnection(ClientConnString);
// Create a scope named "product" and add tables to it.
Console.WriteLine(_pTableName);
DbSyncScopeDescription productScope = new DbSyncScopeDescription(_pTableName + "_SCOP");
// Define the Products table.
DbSyncTableDescription productDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable(_pTableName, serverConn);
// Add the Table to the scope object.
productScope.Tables.Add(productDescription);
// Create a provisioning object for "product" and apply it to the on-premise database if one does not exist.
SqlSyncScopeProvisioning serverProvision = new SqlSyncScopeProvisioning(serverConn, productScope);
serverProvision.ObjectSchema = ".dbo";
serverProvision.SetCreateProceduresForAdditionalScopeDefault(DbSyncCreationOption.Create);
serverProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);
serverProvision.SetCreateProceduresDefault(DbSyncCreationOption.CreateOrUseExisting);
serverProvision.SetCreateTrackingTableDefault(DbSyncCreationOption.CreateOrUseExisting);
serverProvision.SetCreateTriggersDefault(DbSyncCreationOption.CreateOrUseExisting);
if (!serverProvision.ScopeExists(_pTableName + "_SCOP"))
serverProvision.Apply();
// Provision the SQL client database from the on-premise SQL Server database if one does not exist.
SqlSyncScopeProvisioning clientProvision = new SqlSyncScopeProvisioning(clientConn, productScope);
if (!clientProvision.ScopeExists(_pTableName + "_SCOP"))
clientProvision.Apply();
// Shut down database connections.
serverConn.Close();
serverConn.Dispose();
clientConn.Close();
clientConn.Dispose();
}
public static List<_CSyncDetails> Synchronize(string _pScopeName, SyncDirectionOrder _pDirection)
{
// Connection to SQL Server database
SqlConnection serverConn =
new SqlConnection(ServerConnString);
// Connection to SQL client database
SqlConnection clientConn =
new SqlConnection(ClientConnString);
List<_CSyncDetails> _Statics = new List<_CSyncDetails>();
// Perform Synchronization between SQL Server and the SQL client.
SyncOrchestrator syncOrchestrator = new SyncOrchestrator();
// Create provider for SQL Server
SqlSyncProvider serverProvider = new SqlSyncProvider(_pScopeName, serverConn);
// Set the command timeout and maximum transaction size for the SQL Azure provider.
SqlSyncProvider clientProvider = new SqlSyncProvider(_pScopeName, clientConn);
clientProvider.CommandTimeout = serverProvider.CommandTimeout = 500;
//Set memory allocation to the database providers
clientProvider.MemoryDataCacheSize = serverProvider.MemoryDataCacheSize = MemorySize;
//Set application transaction size on destination provider.
serverProvider.ApplicationTransactionSize = BatchSize;
//Count transactions
serverProvider.ChangesApplied +=
new EventHandler<DbChangesAppliedEventArgs>(RemoteProvider_ChangesApplied);
// Set Local provider of SyncOrchestrator to the server provider
syncOrchestrator.LocalProvider = serverProvider;
// Set Remote provider of SyncOrchestrator to the client provider
syncOrchestrator.RemoteProvider = clientProvider;
// Set the direction of SyncOrchestrator session to Upload and Download
syncOrchestrator.Direction = _pDirection;
// Create SyncOperations Statistics Object
SyncOperationStatistics syncStats = syncOrchestrator.Synchronize();
_Statics.Add(new _CSyncDetails
{
UploadChangesTotal = syncStats.UploadChangesTotal,
SyncStartTime = syncStats.SyncStartTime,
DownloadChangesTotal = syncStats.DownloadChangesTotal,
SyncEndTime = syncStats.SyncEndTime
});
// Shut down database connections.
serverConn.Close();
serverConn.Dispose();
clientConn.Close();
clientConn.Dispose();
return _Statics;
}
private static void RemoteProvider_ChangesApplied(object sender, DbChangesAppliedEventArgs e)
{
Console.WriteLine("Changes Applied");
}
public void _MSync()
{
// Define the Products table.
List<string> _Tablelist = new List<string>();
_Tablelist.AddRange(_cSynchronization._MGetAllTableList());
foreach (string tbl in _Tablelist)
{
SetUp(tbl);
_cSynchronization.Synchronize(tbl + "_SCOP", SyncDirectionOrder.DownloadAndUpload);
}
}
}
}
Select Statements for Before and After Tests
SELECT *
FROM SyncTestServer.dbo.Table1
SELECT *
FROM SyncTestServer.dbo.Table2
SELECT *
FROM SyncTestClient.dbo.Table1
SELECT *
FROM SyncTestClient.dbo.Table2
Before the Sync
This was the DB state before the sync.
After Sync
This was the state afterward. So, the sync appears to have worked.
Reattempted with a Remove Server and Three Concurrent Syncs
This is the connection string for the remote DB.
public const string ServerConnString =
#"data source=x0x0x0x0x0x.database.windows.net,1433;initial catalog=SyncTestServer01;user id=xoxoxox#xoxoxox;password=xoxoxoxoxox;MultipleActiveResultSets=True;";
This is the modification to mimic three concurrent synchronizations.
public class Program
{
public static string[] ClientConnString = new string[]
{
#"Data Source=.\SQLExpress;initial catalog=SyncTestClient01;integrated security=True;MultipleActiveResultSets=True;"
,#"Data Source=.\SQLExpress;initial catalog=SyncTestClient02;integrated security=True;MultipleActiveResultSets=True;"
,#"Data Source=.\SQLExpress;initial catalog=SyncTestClient03;integrated security=True;MultipleActiveResultSets=True;"
};
static void Main(string[] args)
{
foreach (var connString in ClientConnString)
{
Action action = () =>
{
_cSynchronization sync = new _cSynchronization();
sync._MSync(connString);
};
Task.Factory.StartNew(action);
}
Console.ReadLine();
}
}
I'm afraid I wasn't able to recreate the error that you experienced. Please turn on tracing and then post the tracing results to your questions. That way we can analyze the trace and see what's problematic.
I am using C# 4.0. (Winform Application)
I have a code like this:
private bool ChangeEFConnectionString(string connStringName, string newValue)
{
try
{
//CreateXDocument and load configuration file
XDocument doc = XDocument.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
//Find all connection strings
var query1 = from p in doc.Descendants("connectionStrings").Descendants()
select p;
//Go throught each connection string elements find atribute specified by argument and replace its value with newVAlue
foreach (var child in query1)
{
foreach (var atr in child.Attributes())
{
if (atr.Name.LocalName == "name" && atr.Value == connStringName)
if (atr.NextAttribute != null && atr.NextAttribute.Name == "connectionString")
{
// Create the EF connection string from existing
EntityConnectionStringBuilder entityBuilder =
new EntityConnectionStringBuilder(atr.NextAttribute.Value);
//change hte provide conn string
entityBuilder.ProviderConnectionString = newValue;
//back the full connection string to the configuration fiel
atr.NextAttribute.Value = entityBuilder.ToString();
}
}
}
doc.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
}
It works well, but somehow it not work any more.
As I debug it : entityBuilder.ProviderConnectionString = newValue; had the right value
but it still not change the connectionstring.
Use ConfigurationManager for reading and updating connection strings:
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.ConnectionStrings.ConnectionStrings[connStringName].ConnectionString = newValue;
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection("connectionStrings");
I think all you need is, after the Save, a:
ConfigurationManager.RefreshSection("connectionStrings");
Have you considered giving EF the whole connection string instead of the name? That would keep you from having to modify what the name means at run time.
I have a database that may be on the network drive.
There are two things that I want to achieve:
When the first user connects to it in read-only mode (he doesn't
have a read-write access to the location, or the database is
read-only), other users must use the read-only connection also (even
if they have RW access).
When the first user connects to it in RW mode, others can not
connect to the database at all.
I'm using SQLite, and the concurrency should not be the problem, as the database should never be used by more than 10 people at the same time.
UPDATE: This is a sample that I'm trying to make work, so I could implement it in the program itself. Almost everything can be changed.
UPDATE: Now when I finally understood what #CL. was telling me, I made it work and this is the updated code.
using System.Diagnostics;
using System.Linq;
using System.IO;
using DbSample.Domain;
using DbSample.Infrastructure;
using NHibernate.Linq;
using NHibernate.Util;
namespace DbSample.Console
{
class Program
{
static void Main(string[] args)
{
IDatabaseContext databaseContext = null;
databaseContext = new SqliteDatabaseContext(args[1]);
var connection = LockDB(args[1]);
if (connection == null) return;
var sessionFactory = databaseContext.CreateSessionFactory();
if (sessionFactory != null)
{
int insertCount = 0;
while (true)
{
try
{
using (var session = sessionFactory.OpenSession(connection))
{
string result;
session.FlushMode = NHibernate.FlushMode.Never;
var command = session.Connection.CreateCommand();
command.CommandText = "PRAGMA locking_mode=EXCLUSIVE";
command.ExecuteNonQuery();
using (var transaction = session.BeginTransaction(ReadCommited))
{
bool update = false;
bool delete = false;
bool read = false;
bool readall = false;
int op = 0;
System.Console.Write("\nMenu of the day:\n1: update\n2: delete\n3: read\n4: read all\n0: EXIT\n\nYour choice: ");
op = System.Convert.ToInt32(System.Console.ReadLine());
if (op == 1)
update = true;
else if (op == 2)
delete = true;
else if (op == 3)
read = true;
else if (op == 4)
readall = true;
else if (op == 0)
break;
else System.Console.WriteLine("Are you retarded? Can't you read?");
if (delete)
{
System.Console.Write("Enter the ID of the object to delete: ");
var objectToRemove = session.Get<MyObject>(System.Convert.ToInt32(System.Console.ReadLine()));
if (!(objectToRemove == null))
{
session.Delete(objectToRemove);
System.Console.WriteLine("Deleted {0}, ID: {1}", objectToRemove.MyName, objectToRemove.Id);
deleteCount++;
}
else
System.Console.WriteLine("\nObject not present in the database!\n");
}
else if (update)
{
System.Console.Write("How many objects to add/update? ");
int number = System.Convert.ToInt32(System.Console.ReadLine());
number += insertCount;
for (; insertCount < number; insertCount++)
{
var myObject = session.Get<MyObject>(insertCount + 1);
if (myObject == null)
{
myObject = new MyObject
{
MtName = "Object" + insertCount,
IdLegacy = 0,
};
session.Save(myObject);
System.Console.WriteLine("Added {0}, ID: {1}", myObject.MyName, myObject.Id);
}
else
{
session.Update(myObject);
System.Console.WriteLine("Updated {0}, ID: {1}", myObject.MyName, myObject.Id);
}
}
}
else if (read)
{
System.Console.Write("Enter the ID of the object to read: ");
var objectToRead = session.Get<MyObject>(System.Convert.ToInt32(System.Console.ReadLine()));
if (!(objectToRead == null))
System.Console.WriteLine("Got {0}, ID: {1}", objectToRead.MyName, objectToRead.Id);
else
System.Console.WriteLine("\nObject not present in the database!\n");
}
else if (readall)
{
System.Console.Write("How many objects to read? ");
int number = System.Convert.ToInt32(System.Console.ReadLine());
for (int i = 0; i < number; i++)
{
var objectToRead = session.Get<MyObject>(i + 1);
if (!(objectToRead == null))
System.Console.WriteLine("Got {0}, ID: {1}", objectToRead.MyName, objectToRead.Id);
else
System.Console.WriteLine("\nObject not present in the database! ID: {0}\n", i + 1);
}
}
update = false;
delete = false;
read = false;
readall = false;
transaction.Commit();
}
}
}
catch (System.Exception e)
{
throw e;
}
}
sessionFactory.Close();
}
}
private static SQLiteConnection LockDbNew(string database)
{
var fi = new FileInfo(database);
if (!fi.Exists)
return null;
var builder = new SQLiteConnectionStringBuilder { DefaultTimeout = 1, DataSource = fi.FullName, Version = 3 };
var connectionStr = builder.ToString();
var connection = new SQLiteConnection(connectionStr) { DefaultTimeout = 1 };
var cmd = new SQLiteCommand(connection);
connection.Open();
// try to get an exclusive lock on the database
try
{
cmd.CommandText = "PRAGMA locking_mode = EXCLUSIVE; BEGIN EXCLUSIVE; COMMIT;";
cmd.ExecuteNonQuery();
}
// if we can't get the exclusive lock, it could mean 3 things
// 1: someone else has locked the database
// 2: we don't have a write acces to the database location
// 3: database itself is a read-only file
// So, we try to connect as read-only
catch (Exception)
{
// we try to set the SHARED lock
try
{
// first we clear the locks
cmd.CommandText = "PRAGMA locking_mode = NORMAL";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT COUNT(*) FROM MyObject";
cmd.ExecuteNonQuery();
// then set the SHARED lock on the database
cmd.CommandText = "PRAGMA locking_mode = EXCLUSIVE";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT COUNT(*) FROM MyObject";
cmd.ExecuteNonQuery();
readOnly = true;
}
catch (Exception)
{
// if we can't set EXCLUSIVE nor SHARED lock, someone else has opened the DB in read-write mode and we can't connect at all
connection.Close();
return null;
}
}
return connection;
}
}
}
Set PRAGMA locking_mode=EXCLUSIVE to prevent SQLite from releasing its locks after a transaction ends.
I don't know if it can be done within db but in application;
You can set a global variable (not sure if it's a web or desktop app) to check if anyone connected and he has a write access or not.
After that you can check the other client's state.