I have ASP.NET Core 2.2 application using EF Core. In startup.cs i am setting CommandTimeout to 60 seconds
services.AddDbContext<CrowdReason.WMP.Data.Entities.WMPContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
sqlServerOptions => sqlServerOptions.CommandTimeout(60)));
Then i am executing the stored proc using the following code. Please note the values of t1, t2 and t3 in comments
public static async Task<int?> prcDoWork(this WMPContext dbContext, int id, int userID)
{
var t1 = dbContext.Database.GetCommandTimeout();
// t1 is 60. Same as what i set in startup.cs
using (var connection = dbContext.Database.GetDbConnection())
{
var t2 = connection.ConnectionTimeout;
//t2 is 15
using (var cmd = connection.CreateCommand())
{
var p1 = new SqlParameter("#ID", SqlDbType.Int)
{
Value = id
};
var p2 = new SqlParameter("#UserID", SqlDbType.Int)
{
Value = userID
};
cmd.CommandText = "dbo.prcDoWork";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
var t3 = cmd.CommandTimeout;
//t3 is 30
await dbContext.Database.OpenConnectionAsync().ConfigureAwait(false);
var result = await cmd.ExecuteScalarAsync().ConfigureAwait(false);
if (result != null)
{
return Convert.ToInt32(result);
}
return null;
}
}
}
I understand that ConnectionTimeout is different from CommandTimeout.
However issue is when i create command using connection.CreateCommand() its not automatically getting the timeout from DBContext
EF Core works on top of the underlying System.Data implementation (in your case System.Data.SqlClient) and uses it to perform its DB operations. All settings you make will only reflect the way EF uses this underlying implementation.
When you use the GetDbConnection method you get a reference to an SqlConnection class from the System.Data.SqlClient assembly that knows nothing about EF and its settings and cannot be expected to honor the RelationalOptionsExtension.CommandTimeout from the Microsoft.EntityFrameworkCore.Relational assembly.
To have EF settings respected you should use the RelationalDatabaseFacadeExtensions.ExecuteSqlCommandAsync method.
Related
I created a generic method in reading sql statement, but I am having a memory leak whenever I do a select query and using while read.
Sample Query:
public CMItemPackagingType GetItemPackagingType(int itemID)
{
try
{
List<CommandParameter> param = new List<CommandParameter>();
StringBuilder sb = new StringBuilder();
using (BaseConnection db = new BaseConnection())
{
sb.Append("SELECT RATIO, PACKAGING_TYPE_CODE FROM ITEM_PACKAGING_TYPE WHERE ROUND_UP = 0.01 AND ITEM_ID = #itemID");
param.Add(new CommandParameter("#itemID", itemID));
using (var rs = db.ExecSQL(sb.ToString(), param.ToArray()))
{
CMItemPackagingType cmItemInfo = new CMItemPackagingType();
while (rs.Read())
{
CMItemPackagingType list = new CMItemPackagingType();
if (!rs.IsDBNull(0))
list.Ratio = Convert.ToInt32(rs.GetValue(0));
if (!rs.IsDBNull(1))
list.PackagingTypeCode = rs.GetValue(1).ToString();
cmItemInfo.ItemPackagingTypeList.Add(list);
}
return cmItemInfo;
}
}
}
catch (Exception ex)
{
GlobalFramework.HandleException(ex);
}
return null;
}
Generic Reader:
public DbDataReader ExecSQL(string sqlStmt, CommandParameter[] param)
{
List<MySqlParameter> p = ParameterMySql(param);
_mySqlConn = new MySqlConnection(szConnect);
if (_mySqlConn.State == ConnectionState.Open)
{
_mySqlConn.Close();
}
_mySqlConn.Open();
_mySqlComm = new MySqlCommand(sqlStmt, _mySqlConn);
_mySqlComm.Parameters.AddRange(p.ToArray());
MySqlDataReader reader = _mySqlComm.ExecuteReader();
return reader;
}
I'm assuming the BaseConnection is a wrapper around a SqlConnection and _mySqlConn is an instance of BaseConnection. I suspect the issue is that you are opening and closing the connection in ExecSQL and at the same time have a using statement around BaseConnection creating this leak. I would refactor your code with proper placement of using statements to ensure correct disposal of the objects and freeing of resources.
Example
var query = "YOUR QUERY";
using (var connection = new SqlConnection("YOUR CONNECTION STRING"))
{
using (var command = new SqlCommand(query, connection))
{
await connection.OpenAsync();
using (var reader = await command.ExecuteReaderAsync())
{
if (reader != null)
{
while (await reader.ReadAsync())
{
// your logic
}
}
} // reader closed and disposed up here
} // command disposed here
} //connection closed and disposed here
}
Also notice how I'm using the async versions of the ADO.NET methods. Async commands are critical in achieving scale, throughput, and latency.
I recommend you use Dapper over trying to develop a generic data reader and writing all the boilerplate ADO.NET code yourself.
I have Added Unit Testing in Existing MVC project and also added references
when i create object of controller
it throws an Exception due to DBContext context object i created in MVC but I need to do Dependency Injection and mocking so that it will not check for it.
please how can i make interface of db to do dependency injection.
code in mvc
public class TestingController : Controller
{
//
// GET: /Testing/
ApplicationDbContext db = new ApplicationDbContext();
Random rnd = new Random();
[Authorize]
public ActionResult Index()
{
string uName = User.Identity.GetUserName();
QuestionsViewModel vm = new QuestionsViewModel();
List<AddQuestion> adlist = new List<AddQuestion>();
List<QuestionsViewModel> qlist = new List<QuestionsViewModel>();
List<int> rn = new List<int>();
List<int> rn2 = new List<int>();
List<int> rn3 = new List<int>();
AddQuestion adq = new AddQuestion();
var Sessionid = System.Guid.NewGuid();
vm.sessionid = Sessionid.ToString();
Session["ApplicantSession"] = Sessionid.ToString();
ViewBag.StartTime = Session.Timeout;
List<List<int>> threecompQids = new List<List<int>>();
List<int> c1question = db.AddQuestions.Where(x => x.ComplexityLevel == 1)
.Select(y => y.AddQuestionID).ToList();
List<int> c2question = db.AddQuestions.Where(x => x.ComplexityLevel == 2)
.Select(y => y.AddQuestionID).ToList();
List<int> c3question = db.AddQuestions.Where(x => x.ComplexityLevel == 3)
.Select(y => y.AddQuestionID).ToList();
for (int i = 0; i < 5; i++)
{
int r = rnd.Next(c1question.Min(), c1question.Max() + 1);
while (!(c1question.Any(w => w.Equals(r)) && !rn.Any(w => w == r)))
{
r = rnd.Next(c1question.Min(), c1question.Max() + 1);
}
rn.Add(r);
r = rnd.Next(c2question.Min(), c2question.Max() + 1);
while (!(c2question.Any(w => w.Equals(r)) && !rn2.Any(w => w == r)))
{
r = rnd.Next(c2question.Min(), c2question.Max() + 1);
}
rn2.Add(r);
r = rnd.Next(c3question.Min(), c3question.Max() + 1);
while (!(c3question.Any(w => w.Equals(r)) && !rn3.Any(w => w == r)))
{
r = rnd.Next(c3question.Min(), c3question.Max() + 1);
}
rn3.Add(r);
}
var fstquestion = rn[0];
threecompQids.Add(rn);
threecompQids.Add(rn2);
threecompQids.Add(rn3);
vm.ComplexLevQidsLists = threecompQids;
adq = db.AddQuestions.Find(fstquestion);
List<Option> opt = db.Options.Where(op => op.AddQuestionID == adq.AddQuestionID).ToList();
vm.Questions = adq;
vm.Options = opt;
vm.UserName = uName;
return View(vm);
}
}
where as in test project i only created object of testcontroller
It seems like you didn't mock data access component your controller depends on, right? If this is the case and you are using actual implementation in your unit test, most likely your connection string defined in Test project is missing or differs from the connection string defined in MVC project.
Also keep in mind that if you do not mock your controller's dependencies, your unit test is technically cannot be considered as "unit" — it's more like an integration or scenario test.
You should either abstract the ApplicationDbContext using an interface like IAppDbContext or provide the connection string to the testing project too. In the latter case your test will stop being unit test though.
public class MyController: Controller
{
IAppDbContext _context;
pulbic MyController(IAppDbContext context)
{
_context = context; // Now you can use the interface to perform your data access operations
}
....
}
And now you will be able to inject mock implementations of IAppDbContext in your unit tests.
You should do some research on Dependency Inversion and Mocking.
I'm creating a simple sqlite driven app for ios using xamarin studio on a mac.
The sqlite file is created in the "personal" folder and is persisted between builds but when i run the app the tables i created in the previous debug session is gone?
In my code, after checking that the file exists, i connect using a sqliteconnection and create a table and insert a row with the executenonquery method from the command object. While in the same context i can query the table using a second command object but if i stop the debugger and restart the table i gone?
Should i have the file in a different folder, is it a setting in xamarin or ios to keep the tables? Am i unintentionally using temp tables in sqlite or what could be the problem?
Note: so far i'm only using starter version of xamarin and debugging on iphone simulator.
public class BaseHandler
{
private static bool DbIsUpToDate { get; set; }
const int DB_VERSION = 1; //Created DB
const string DB_NAME = "mydb.db3";
protected const string CNN_STRING = "Data Source=" + DB_NAME + ";Version=3";
public BaseHandler ()
{
//No need to validate database more than once on each restart.
if (DbIsUpToDate)
return;
CheckAndCreateDatabase(DB_NAME);
int userVersion = GetUserVersion();
UpdateDBToVersion(userVersion);
DbIsUpToDate = true;
}
int GetUserVersion()
{
int version = 0;
using (var cnn = new SqliteConnection(CNN_STRING))
{
cnn.Open();
using (var cmd = cnn.CreateCommand())
{
cmd.CommandText = "CREATE TABLE UVERSION (VERSION INTEGER);" +
"INSERT INTO UVERSION (VERSION) VALUES(1);";
cmd.ExecuteNonQuery();
}
using (var cmd = cnn.CreateCommand())
{
cmd.CommandText = "SELECT VERSION FROM UVERSION;";
var pragma = cmd.ExecuteScalar();
version = Convert.ToInt32((long)pragma);
}
}
return version;
}
void UpdateDBToVersion(int userVersion)
{
//Prepare the sql statements depending on the users current verion
var sqls = new List<string> ();
if (userVersion < 1)
{
sqls.Add("CREATE TABLE IF NOT EXISTS MYTABLE ("
+ " ID INTEGER PRIMARY KEY, "
+ " NAME TEXT, "
+ " DESC TEXT "
+ ");");
}
//Execute the update statements
using (var cnn = new SqliteConnection(CNN_STRING))
{
cnn.Open();
using (var trans = cnn.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
foreach(string sql in sqls)
{
using (var cmd = cnn.CreateCommand())
{
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
}
trans.Commit();
//SetUserVersion(DB_VERSION);
}
}
}
protected string GetDBPath (string dbName)
{
// get a reference to the documents folder
var documents = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
// create the db path
string db = Path.Combine (documents, dbName);
return db;
}
protected void CheckAndCreateDatabase (string dbName)
{
var dbPath = GetDBPath(dbName);
// determine whether or not the database exists
bool dbExists = File.Exists(dbPath);
if (!dbExists)
SqliteConnection.CreateFile(dbPath);
}
}
Again, my problem is that every time I run the debugger it runs GetUserVersion but the table UVERSION is not persisted between sessions. The "File.Exists(dbPath)" returns true so CreateFile is not run. Why is the db empty?
This is a code snippet I've used to save my databases in the iOS simulator and the data seems to persist between app compiles just fine:
string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
string libraryPath = Path.Combine (documentsPath, "../Library/");
var path = Path.Combine (libraryPath, "MyDatabase.db3");
You may also want to check out the SQLite class for Xamarin off of Github:
https://github.com/praeclarum/sqlite-net/tree/master/src
Here's a tutorial on how to use said class:
http://docs.xamarin.com/recipes/ios/data/sqlite/create_a_database_with_sqlitenet
Turns out that I was creating the connection object using the CNN_STRING which just had the db-name instead of the full path to the db. Apperantly the connection object creates the database if the file doesn't exist so the File.Exists(...) might not be needed. I'm not really sure if it should be a temporary db if the complete path is not supplied but it seems to be the case. Changing the creation of the connection object to "datasource=" solved the problem.
Below is code I use in a WCF Service Reference I'm building using the Entity Framework. As you can see, right now I'm using the same code over and over again. Is there a syntax that will allow me to replace the entities with a generic type or another such method that would cut down on the amount of code being used?
var aI = (from AgentIdentification s in _db.AgentIdentification
where s.SymetraNumber == sNum
select s);
foreach (var record in aI)
{
_db.AgentIdentification.DeleteObject(record);
}
_db.SaveChanges();
var aG = (from Agent s in _db.Agent
where s.SymetraNumber == sNum
select s);
foreach (var record in aG)
{
_db.Agent.DeleteObject(record);
}
_db.SaveChanges();
I believe this is what you want.
PerformChanges<YourDbObject, AgentIdentification>(x => x.AgentIdentification, sNum, _db);
PerformChanges<YourDbObject, Agent>(x => x.Agent, sNum, _db);
private void PerformChanges<DbType,TCollection>(Func<DbType,DbSet<TCollection>> FetchDbSetLambda, int sNum, DbType your_db)
{
var aI = (from s in FetchDbSetLambda(your_db)
where s.SymetraNumber == sNum
select s);
foreach (var record in aI)
FetchDbSetLambda(your_db).DeleteObject(record);
}
I have console application in which I am doing sqldependency. My problem is when I set commandType as Text, it is working fine. But if I use commandType as StoredProcedure, onchange method is calling infinitely.
Please see the code below:
static DataSet myDataSet;
static SqlConnection connection;
static SqlCommand command;
static void Main(string[] args)
{
// Remove any existing dependency connection, then create a new one.
string connstr = "Data Source=XYZ;Initial Catalog=Dev;Integrated Security=True";
string ssql = #"[dbo].[SchedulerPendingControlRequestIDFetch]";
CanRequestNotifications();
SqlDependency.Stop(connstr);
SqlDependency.Start(connstr);
if (connection == null)
connection = new SqlConnection(connstr);
if (command == null)
command = new SqlCommand(ssql, connection);
command.CommandType = CommandType.StoredProcedure;
if (myDataSet == null)
myDataSet = new DataSet();
GetAdvtData();
System.Console.ReadKey();
connection.Close();
}
private static bool CanRequestNotifications()
{
SqlClientPermission permission =
new SqlClientPermission(
PermissionState.Unrestricted);
try
{
permission.Demand();
return true;
}
catch (System.Exception)
{
return false;
}
}
private static void GetAdvtData()
{
myDataSet.Clear();
// Ensure the command object does not have a notification object.
command.Notification = null;
// Create and bind the SqlDependency object to the command object.
SqlDependency dependency = new SqlDependency(command,null,100);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(myDataSet, "ControlRequest");
}
}
private static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency =
(SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
Console.WriteLine(e.Info.ToString() + e.Source.ToString());
GetAdvtData();
}
My stored Procedure is:
IF OBJECT_ID('SchedulerSirasColcoDetailFetch') IS NOT NULL
DROP PROCEDURE SchedulerSirasColcoDetailFetch
Go
PRINT 'Creating stored procedure SchedulerSirasColcoDetailFetch'
Go
CREATE PROCEDURE [dbo].[SchedulerSirasColcoDetailFetch]
AS
BEGIN
SELECT Colco_Code AS 'CountryCode',Connection_String AS 'Url',Resend_Interval AS 'ResendInterval',
Default_Encoding AS 'Encoding' FROM dbo.SirasColcoDetail
END
If I copy the select statement inside stored procedure as my command text and set the commandType as Text, everything is working fine.
could you please let me know what the issue is????
Thanks a lot in advance.
Mahesh
You're supposed to check the values of the SqlNotificationEventArgs argument. Only if Type is Change and Source is Data where you notified for a data change.
You'll discover that you're not notified for data changes, but for incorrect settings or incorrect query. Your query and connection settings must comply with the requirements specified in Creating a Query for Notifications.