I'm trying to standardise some data access code with my colleagues. One of the aforementioned colleagues asserts that the EntLib Data Access Block trys to cache parameters on stored proc calls.
I've had a look in reflector and there is some evidence that it could be caching them. But I don't think it does in the following situation.
public Dictionary<long, string> GetQueue(int maxItems)
{
var sq = new SqlDatabase(_connString.ConnectionString);
var result = new Dictionary<long, string>();
using (var cmd = (SqlCommand)sq.GetStoredProcCommand("dbo.GetQueue"))
{
sq.AddInParameter(cmd, "maxItems", DbType.Int32, maxItems);
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
long id = reader.GetInt64(reader.GetOrdinal("id"));
string fileName = reader.GetString(reader.GetOrdinal("meta_data_filename"));
result.Add(id, fileName);
}
}
return result;
}
Can anyone confirm or deny this?
I'm using EntLib 4.1
It definetly used to, I ripped the code out and threw in in my library.
it used sp_help and parsed the output to determine the data types.
These days, I ripped the code out, .Net is much much better about adding parameters.
cmd.Parameters.AddWithValue("#name",somevalue)
in your example of you keep reflectoring ... you will find it being done down this path GetStoredProcCommand()
You will get a Command object back, already populated with parameters
The ent lib code is copyrighted, but the code is almost identical to this
http://code.google.com/p/dbdotnet/source/browse/trunk/ParameterCache.cs
As far as I can tell it doesn't cache the parameters. Using the same instance of a Database object I called DiscoverParameters multiple times while running a trace. Each time I call DiscoverParameters I can see a [sys].[sp_procedure_params_100_managed] so it looks like it's making the round trip every time.
Here's an example of how to do it yourself that's seems like it might be alright:
http://davidhayden.com/blog/dave/archive/2006/11/03/CachingStoredProcedureParameters.aspx
Related
Is there a way to dump the generated sql to the Debug log or something? I'm using it in a winforms solution so the mini-profiler idea won't work for me.
I got the same issue and implemented some code after doing some search but having no ready-to-use stuff. There is a package on nuget MiniProfiler.Integrations I would like to share.
Update V2: it supports to work with other database servers, for MySQL it requires to have MiniProfiler.Integrations.MySql
Below are steps to work with SQL Server:
1.Instantiate the connection
var factory = new SqlServerDbConnectionFactory(_connectionString);
using (var connection = ProfiledDbConnectionFactory.New(factory, CustomDbProfiler.Current))
{
// your code
}
2.After all works done, write all commands to a file if you want
File.WriteAllText("SqlScripts.txt", CustomDbProfiler.Current.ProfilerContext.BuildCommands());
Dapper does not currently have an instrumentation point here. This is perhaps due, as you note, to the fact that we (as the authors) use mini-profiler to handle this. However, if it helps, the core parts of mini-profiler are actually designed to be architecture neutral, and I know of other people using it with winforms, wpf, wcf, etc - which would give you access to the profiling / tracing connection wrapper.
In theory, it would be perfectly possible to add some blanket capture-point, but I'm concerned about two things:
(primarily) security: since dapper doesn't have a concept of a context, it would be really really easy for malign code to attach quietly to sniff all sql traffic that goes via dapper; I really don't like the sound of that (this isn't an issue with the "decorator" approach, as the caller owns the connection, hence the logging context)
(secondary) performance: but... in truth, it is hard to say that a simple delegate-check (which would presumably be null in most cases) would have much impact
Of course, the other thing you could do is: steal the connection wrapper code from mini-profiler, and replace the profiler-context stuff with just: Debug.WriteLine etc.
You should consider using SQL profiler located in the menu of SQL Management Studio → Extras → SQL Server Profiler (no Dapper extensions needed - may work with other RDBMS when they got a SQL profiler tool too).
Then, start a new session.
You'll get something like this for example (you see all parameters and the complete SQL string):
exec sp_executesql N'SELECT * FROM Updates WHERE CAST(Product_ID as VARCHAR(50)) = #appId AND (Blocked IS NULL OR Blocked = 0)
AND (Beta IS NULL OR Beta = 0 OR #includeBeta = 1) AND (LangCode IS NULL OR LangCode IN (SELECT * FROM STRING_SPLIT(#langCode, '','')))',N'#appId nvarchar(4000),#includeBeta bit,#langCode nvarchar(4000)',#appId=N'fea5b0a7-1da6-4394-b8c8-05e7cb979161',#includeBeta=0,#langCode=N'de'
Try Dapper.Logging.
You can get it from NuGet. The way it works is you pass your code that creates your actual database connection into a factory that creates wrapped connections. Whenever a wrapped connection is opened or closed or you run a query against it, it will be logged. You can configure the logging message templates and other settings like whether SQL parameters are saved. Elapsed time is also saved.
In my opinion, the only downside is that the documentation is sparse, but I think that's just because it's a new project (as of this writing). I had to dig through the repo for a bit to understand it and to get it configured to my liking, but now it's working great.
From the documentation:
The tool consists of simple decorators for the DbConnection and
DbCommand which track the execution time and write messages to the
ILogger<T>. The ILogger<T> can be handled by any logging framework
(e.g. Serilog). The result is similar to the default EF Core logging
behavior.
The lib declares a helper method for registering the
IDbConnectionFactory in the IoC container. The connection factory is
SQL Provider agnostic. That's why you have to specify the real factory
method:
services.AddDbConnectionFactory(prv => new SqlConnection(conStr));
After registration, the IDbConnectionFactory can be injected into
classes that need a SQL connection.
private readonly IDbConnectionFactory _connectionFactory;
public GetProductsHandler(IDbConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
}
The IDbConnectionFactory.CreateConnection will return a decorated
version that logs the activity.
using (DbConnection db = _connectionFactory.CreateConnection())
{
//...
}
This is not exhaustive and is essentially a bit of hack, but if you have your SQL and you want to initialize your parameters, it's useful for basic debugging. Set up this extension method, then call it anywhere as desired.
public static class DapperExtensions
{
public static string ArgsAsSql(this DynamicParameters args)
{
if (args is null) throw new ArgumentNullException(nameof(args));
var sb = new StringBuilder();
foreach (var name in args.ParameterNames)
{
var pValue = args.Get<dynamic>(name);
var type = pValue.GetType();
if (type == typeof(DateTime))
sb.AppendFormat("DECLARE #{0} DATETIME ='{1}'\n", name, pValue.ToString("yyyy-MM-dd HH:mm:ss.fff"));
else if (type == typeof(bool))
sb.AppendFormat("DECLARE #{0} BIT = {1}\n", name, (bool)pValue ? 1 : 0);
else if (type == typeof(int))
sb.AppendFormat("DECLARE #{0} INT = {1}\n", name, pValue);
else if (type == typeof(List<int>))
sb.AppendFormat("-- REPLACE #{0} IN SQL: ({1})\n", name, string.Join(",", (List<int>)pValue));
else
sb.AppendFormat("DECLARE #{0} NVARCHAR(MAX) = '{1}'\n", name, pValue.ToString());
}
return sb.ToString();
}
}
You can then just use this in the immediate or watch windows to grab the SQL.
Just to add an update here since I see this question still get's quite a few hits - these days I use either Glimpse (seems it's dead now) or Stackify Prefix which both have sql command trace capabilities.
It's not exactly what I was looking for when I asked the original question but solve the same problem.
Almost every tutorial I have read seems to incorrectly setup SqlCacheDependency. I believe they normally mix up the outdated polling method with the query notification method.
Here are two of many examples:
Web Caching with SqlCacheDependency Simplified (non-microsoft)
SqlCacheDependency Class (Microsoft)
Based on my testing, if you are using the broker (MSSQL 2015+) you don't need to make any .config changes nor do you need to make any SqlCacheDependencyAdmin calls (Don't need to define tables, etc).
I simplify just do this...
SqlDependency.Start(connString)
...
queryString = "SELECT ...";
cacheName = "SqlCache" + queryString.GetHashCode();
...
using (var connection = new SqlConnection(connString))
{
connection.Open();
var cmd = new SqlCommand(queryString, connection)
{
Notification = null,
NotificationAutoEnlist = true
};
var dependency = new SqlCacheDependency(cmd);
SqlDataReader reader = cmd.ExecuteReader();
try
{
while (reader.Read())
{
// Set the result you want to cache
data = ...
}
}
finally
{
reader.Close();
}
HostingEnvironment.Cache.Insert(cacheName, data, dependency);
}
(The code that checks if the cache is null or not is not included, as that's all just setup. I just want to show the setting of the cache)
This seems to work without the need to define which tables are involved in the query and make complicated triggers on each table. It just works.
More surprising to me is that the rules for making a query have notification :
Creating a Query for Notification (Can't find documentation newer than 2008) don't seem to apply. I purpose to do a TOP in my SQL and it still works.
For a test, I have it run a query 1000 times involving a table named "Settings". Then I update a value in the table and repeat the query.
I watch the Profiler for any queries involving the word "Settings" and I see the query is executed just 1 time (to set the cache) and then the update statement occurs, and then the query is re-executed one more time (the cache was invalidated and the query ran again)
I am worried that in my 2-3 hours of struggling with the proper way to do this I am missing something and it really is this simple?
Can I really just put any query I want and it'll just work? I am looking for any pointers where I am doing something dangerous/non-standard or any small print that I am missing
var dependency = new SqlCacheDependency(cmd);
when you write query like this you autiomatically define table name in it.Your connection already has db name.
It is non explicit way to do same.
Explicit way to catch exception and to know what went wrong is this.
// Declare the SqlCacheDependency instance, SqlDep.
SqlCacheDependency SqlDep = null;
// Check the Cache for the SqlSource key.
// If it isn't there, create it with a dependency
// on a SQL Server table using the SqlCacheDependency class.
if (Cache["SqlSource"] == null) {
// Because of possible exceptions thrown when this
// code runs, use Try...Catch...Finally syntax.
try {
// Instantiate SqlDep using the SqlCacheDependency constructor.
SqlDep = new SqlCacheDependency("Northwind", "Categories");
}
// Handle the DatabaseNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableNotifications method.
catch (DatabaseNotEnabledForNotificationException exDBDis) {
try {
SqlCacheDependencyAdmin.EnableNotifications("Northwind");
}
// If the database does not have permissions set for creating tables,
// the UnauthorizedAccessException is thrown. Handle it by redirecting
// to an error page.
catch (UnauthorizedAccessException exPerm) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// Handle the TableNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableTableForNotifications method.
catch (TableNotEnabledForNotificationException exTabDis) {
try {
SqlCacheDependencyAdmin.EnableTableForNotifications("Northwind", "Categories");
}
// If a SqlException is thrown, redirect to an error page.
catch (SqlException exc) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// If all the other code is successful, add MySource to the Cache
// with a dependency on SqlDep. If the Categories table changes,
// MySource will be removed from the Cache. Then generate a message
// that the data is newly created and added to the cache.
finally {
Cache.Insert("SqlSource", Source1, SqlDep);
CacheMsg.Text = "The data object was created explicitly.";
}
}
else {
CacheMsg.Text = "The data was retrieved from the Cache.";
}
As documented in https://learn.microsoft.com/en-us/dotnet/api/system.web.caching.sqlcachedependency?view=netframework-4.8 "Using a SqlCacheDependency object with SQL Server 2005 query notification does not require any explicit configuration."
So, the CMD has explicit table names in it, and ADO.net is issuing the correct Service Broker configuration commands for you. When the table is updated, SQL Server posts a Service Broker message saying the table has been updated. When ADO.net validates the CMD it checks the explicit tables in the broker for updates.
This is why the SQlCacheDependency associated CMD must use explicit tables.
After days of samples, demos, tutorials, walkthroughs, etc., ...
I created the code below based on what I've learned.
It works perfect when hard coded with an entity (as shown in the top commented 2 lines for the Advertisers db table).
It creates the excel file, then it offers it up for download to the user via the browser.
However, I wanted to make it a general purpose utility for creating and downloading other entities in my db.
I've refactored this from my original method:
by replacing all the 'Advertisers' references with the passed in parameter 'listTitle'.
Then, I replaced the var adv with the passed in parameter 'context' in var ctx.
[Updated]
I plan to call this method with a collection list I create from other areas of my code (similar to the way I used the var adv, commented (i.e., context = db.Publisher.OrderBy(p => p.Name).ToList())),
and pass it in as the context parameter in the method call.
(ex., ExportExcelList("Publishers", context)).
Now, I get the red squigglies on the line with LoadFromCollection, and it won't build my code properly.
So my questions are:
How can I get this to work?
Is there a way of getting this to work without hugh blocks of code that include annonymous object types and GUID's (as described in other posts)?
Am I just missing a using statement?
Or should the type of the context parameter be different?
Currently, for the EPPlus using statement, I'm using: using OfficeOpenXml;
I've installed EPPlus via Nuget.
This is my code:
[HttpPost]
public ActionResult ExportExcelList(string listTitle, ApplicationDbContext context)
{
////create the list of advertisers [ HARD CODED HERE ]
//var adv = db.Advertisers.OrderBy(a => a.Name).ToList();
var ctx = context;
//create the path and filename to use for the excel file and check for file exists/delete here.
//here xfile is the name of the excel file without the directory path.
....
file = new FileInfo(filename); //this is the file object used for creating the new excel file.
//create the excel file
using (ExcelPackage ep = new ExcelPackage(file))
{
ep.Workbook.Worksheets.Add(listTitle);
var worksheet = ep.Workbook.Worksheets[1];
worksheet.Cells[1, 1].LoadFromCollection(ctx, true); //this line barks at the ctx usage, it wants explicit type declaration.
ep.Save();
}
//download the excel file to the user
using (ExcelPackage ep = new ExcelPackage(file))
{
//Write it back to the client
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=" + xfile);
Response.BinaryWrite(ep.GetAsByteArray());
}
//end this process
return null;
}
I haven't found a solution that would allow me to do what I wanted.
Many thanks to #CodingYoshi for his insights.
Below is my temporary solution. For now I'm using a switch case scenario.
Not too happy about it, but it serves the purpose for now, in spite of it's limitations.
Hopefully, someone here at SO can treat to a better solution. I hope.
[ Updated Code ]
//since I can't pass the context in by parameter when calling this method, I've decided to use a switch case scenario and just pass in the listTitle
switch(listTitle)
{
case "Advertisers":
worksheet.Cells[1, 1].LoadFromCollection(
db.Advertisers.OrderBy(a => a.Name).ToList(),
true);
break;
case "Publishers":
worksheet.Cells[1, 1].LoadFromCollection(
db.Publishers.OrderBy(a => a.Name).ToList(),
true);
break;
}
ep.Save();
That line breaks because it is expecting IEnumerable but you are passing an ApplicationDbContext. If you want to do what you are after, you need to somehow figure out a way to enumerate all the dbsets in your ApplicationDbContext and pass them one by one to the LoadFromCollection method.
I know DbContext has a property which will allow you to do this: GetDbSetProperties, not sure if ApplicationDbContext has something similar. That will get you all your tables into Excel.
The next thing you will need to worry about is the filters and sort code. For example, maybe you want to sort by a specific field (by Name for advertisers as you have) or maybe you want to filter them. Normally this will be passed in using a predicate but since your are hitting the action method from the browser, that is not an option. Therefore, you can create multiple meaningful actions such as ExportAdvertisersSortedByName, ExportTopAdvertisers, ExportSomeOtherEntity etc. For these actions you will hardcode the entity like you have above and then the common code for epplus export can be in another method.
This way you can export a whole database, or specific items for specific needs.
You need to use reflection to pass the collection name dynamically.
So like this
var p = ctx.GetType.GetProperty(listTitle).getValue(ctx, null)
and see further details here.
public static IList<T> SelectAll<T>() where T : class
{
try
{
using (NWindDataContext context = new NWindDataContext())
{
// get the table matching the type
// and return all of it as typed list
var table = context.GetTable<T>().ToList<T>();
return table;
}
}
catch (Exception)
{
throw;
}
}
I am new in SignalR .
my project is that bringing up sql change on signalR and sql dependency .
This is sample code that used C# corner
Every thing is ok but i get exception through this code
using (var connection = new SqlConnection("Server=.;Database=fidilio;Trusted_Connection=True;"))
{
const string query = "SELECT Count(*) FROM [dbo].[MemberComment]";
connection.Open();
using (var command = new SqlCommand(query, connection))
{
command.Notification = null;
var dt = new DataTable();
var dependency = new SqlDependency(command);
dependency.OnChange += dependency_OnChange;
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteScalar();
commentCount = Int16.Parse((reader.ToString()));
}
}
var context = GlobalHost.ConnectionManager.GetHubContext<NotficationHub>();
return context.Clients.All.RecevieNotification(commentCount);
Is there any idea ?
Unfortunately, the code you posted is incomplete and far from clear, never mind useful. Note, for example, that the code posted as actual code does not actually match the code you posted as a bitmap.
(Please also keep in mind that bitmaps, especially those for which the description still reads "enter image description here", cannot be searched in any meaningful way by tools like the web-site's own search feature or search engines like Bing and Google).
However, the specific exception you got is very typical of misuse of an async method. It seems to me that you could get the code to work by changing the return statement to look like this:
return (string)context.Clients.All.RecevieNotification(commentCount).Result;
(That's the code from the text version of your question...copy/paste is also easier than trying to retype something from a bitmap. I presume you can adapt the above change to the code in the bitmap if needed).
Note that if that actually does fix the problem, then what you really should do is change the method containing that return statement to be async Task<string>, and change the return statement to this:
return (string)(await context.Clients.All.RecevieNotification(commentCount));
But doing so will simply push the need to use await back up to the calling method, and the caller of that method, and so on, until you get to whatever top-level method in the thread or UI event started the whole process.
That is really the right thing to do, but there's not enough context here to explain how to do that in any specific way. If you do decide to fix your code that way, and need help figuring out how to correctly make your code async-aware, please post a new question, being sure to include a good, minimal, complete code example that clearly and reliably illustrates that question.
An unhandled exception of type 'System.AccessViolationException' occurred in StatCentric.Tracker.Worker.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I've read numerous posts on both Stack Overflow and various blogs and can't seem to find a solution for this.
I'm doing something very basic:
public void Execute(ITrackerRequestModel model)
{
PageviewRequest p = (PageviewRequest)model;
using (var db = new StatCentricEntities())
{
db.SetTimeout(60);
db.sp_Log_PageView2(p.SiteId, p.DateTimeUtc, p.vid, p.QueryString, p.p, p.t);
}
}
But this error pops up every time I try to call db.sp_Log_PageView2. This only seems to happen inside my worker role (I'm using Windows Azure).
Also worthy of note is that I'm using the Windows Azure Emulator and I am on Windows 8.1.
I've tried the following:
Doing a winsocket reset
Disabling JIT debugging (native,script, managed)
Disabling JIT debugging on module load
Followed some old posts to hot fixes that seem to be specific to .NET
2.0 and discontinued.
Did a memory diagnostic with no issues to make sure it wasn't my hardware.
I am running Visual Studio as administrator and connecting to a remote SQL Server Database hosted in Azure.
Any ideas on how to resolve or further diagnose this are appreciated.
This is not real fix but while waiting for fix from Microsoft you can use this workaround.
I have same problem. I also tried everything to solve that issue. After few days I gave up and used manual "workaround". It only took few minutes to copy and convert existing sproc calls to new ones.
Just ignore auto generated functions and manually call stored procedures. You can use auto generated classes for returned data. Copy and modify existing function and you will get easily correct parameter names and types.
Just implement partial class to different file:
public partial class StatCentricEntities
{
public virtual List<sp_Log_PageView2_Result> my_sp_Log_PageView2(
Guid? siteId,
DateTime time,
string param3,
string param4 )
{
return Database.SqlQuery<sp_Log_PageView2_Result>(
"sp_Log_PageView2 #siteId #time #param3 #param4",
new SqlParameter("siteId", siteId),
new SqlParameter("time", time),
new SqlParameter("param3", param3),
new SqlParameter("param4", param4)).ToList();
}
}
I was getting this "Attempted to read or write protected memory exception" error while using a SQL Server stored procedure that had an output parameter of type 'Date'. I tried various things without success and, in the interest of time, settled on the following solution.
1) Remove the output parameter of type date
2) Return a string via a select statement
SELECT CONVERT(char(10), #AsOfDate, 20) AS AsOfDate
3) Convert the string returned from the stored procedure to a DateTime value in C#
DateTime asOfDate = DateTime.Now;
using (var context = new DA.MyEntities())
{
var procResult = context.myStoredProcedure(myParameter).FirstOrDefault();
DateTime.TryParse(procResult, out asOfDate);
}
I'm not super happy with this compromise, but it did allow me to move forward.