SqlDependency not updating cache - c#

I'm on a project using .NET 4.5, MVC, EF 6
I had naively implemented a caching system using the HttpRuntime cache and needed to invalidate the data I cache on updates to that data; except I forgot to take into account that our production server is published to a load balanced set of two servers... :|
So on production, after the data was updated, the app would sometimes serve the right data, and sometimes the old data depending on which server the request was hitting. Bad news bears.
So I decided to define a dependency on the SQL table AcademicTerms which is where my data is coming from. But I did something wrong, and I'm not sure what.
SQL that I ran to set up the permissions after enabling the Service Broker
EXEC sp_addrole 'sql_dependency_role'
GRANT CREATE PROCEDURE to sql_dependency_role
GRANT CREATE QUEUE to sql_dependency_role
GRANT CREATE SERVICE to sql_dependency_role
GRANT REFERENCES on
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
to sql_dependency_role
GRANT VIEW DEFINITION TO sql_dependency_role
GRANT SELECT to sql_dependency_role
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO sql_dependency_role
GRANT RECEIVE ON QueryNotificationErrorsQueue TO sql_dependency_role
EXEC sp_addrolemember 'sql_dependency_role', 'MY_ASPNET_APP_USERNAME'
My implementation of inserting new data after fetching and thus setting up the SqlDependency (hopefully less naive!):
private void insertIntoCache(
AcademicTermLockingInfo newItem,
string itemKey,
Guid termID) {
var dbContextConnection = db.Database.Connection;
var connectionString = dbContextConnection.ConnectionString;
// important step otherwise it won't work
SqlDependency.Start(connectionString);
CacheItemPolicy policy = new CacheItemPolicy {
AbsoluteExpiration = DateTime.UtcNow.AddMonths(6)
};
CacheItem item = new CacheItem(itemKey, newItem);
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
// command which will be used to notify updates - probably want to parametrize this
using (SqlCommand command =
new SqlCommand(
String.Format("SELECT Name, LockDate FROM dbo.AcademicTerms WHERE ID = '{0}'",
termID),
connection)) {
SqlDependency dependency = new SqlDependency(command);
SqlChangeMonitor monitor = new SqlChangeMonitor(dependency);
policy.ChangeMonitors.Add(monitor);
MemoryCache.Default.Set(item, policy);
// execute once otherwise dependency not registered
command.ExecuteNonQuery();
}
}
}
Any help would be very much appreciated!
Things I've done:
Created two new users in SQL Server, net and sanba
Added every NT* login and the sa login to the net user, added net to the sql_dependency_role
Ran grant alter on schema::sql_dependency_role to net and grant alter on schema::dbo to net
Check that my local SQL Server's Broker Enabled option is True under Service Broker
Tried the web cache and the Memory Cache interchangeably (probably wouldn't change anything)
Tried making the sql command string have a fully qualified name DevUMS.dbo.AcademicTerms and dbo.AcademicTerms
I queried the sys.dm_qn_subscriptions and saw I had one subscription, good!
I queried DevUMS.sys.transmission_queue and found an excpetion!
An exception occurred while enqueueing a message in the target
queue. Error: 15517, State: 1. Cannot execute as the database
principal because the principal "dbo" does not exist, this type of
principal cannot be impersonated, or you do not have permission.
I found this SO post with the same error

The secret sauce I was missing was alter authorization on database::DevUMS to [sa]; which I found on the linked SO post's answer.
There are a number of other steps, like adding a Role to use the appropriate login, but honestly, I'm really unsure as to whether or not those are actually necessary.
I'm going to publish a little later on today, and then I'll try to do the minimal amount of steps and document that here. I found the documentation in the wild to be very scattered and poor, so I hope to have this answer be a definitive place to refer to in the future

Related

Realm sync permissions for flexibly-named partitions based on user id

I'm new to Realm Sync (and Realm). I'm trying to convert a REST / SQL Server system to Realm Sync (to avoid having to write my own local-device caching code).
I got a simple configuration working, with a single API-key user and the null partition, read and write permissions just set to true.
But for my more complex application, I want smaller sub-partitions to reduce the amount of data that needs to be cached on local devices, and I want the sub-partitions to be able to be created dynamically by the client. Ideally, I would like to allow an API-key user to connect to any partition whose name starts with their user id (or some other known string, e.g. the profile name). But I can't find a way to get a "starts with" condition into the permissions.
My best attempt was to try setting Read and Write sync permissions to:
{
"%%partition": {
"$regex": "^%%user.id"
}
}
but my client just fails to connect, saying Permission denied (BIND, REFRESH). (Yes, I tried using "$regex": /^%%user.id/ but the Realm UI rejected that syntax.) The Realm Sync log says "user does not have permission to sync on partition (ProtocolErrorCode=206)".
As you can see in the log image, the partition name was equal to the user id for this test.
Is what I'm trying to do possible? If so, how do I set up the Sync Permissions to make it work?
This can be done using a function. If, like me, you're new to Realm Sync and not fluent in Javascript, don't worry - it turns out to be not too hard to do, after all. (Thanks Jay for encouraging me to try it!)
I followed the instructions on the Define a Function page to create my userCanAccessPartition function like this:
exports = function(partition){
return partition.startsWith(context.user.id);
};
Then I set my sync permissions to:
{
"%%true": {
"%function": {
"name": "userCanAccessPartition",
"arguments": ["%%partition"]
}
}
}

how to register SQL Table dependency for non dbo user

We are trying to enable SQLTableDependency from .Net code using Non-DBO SQL user, Currently, it's throwing a permission exception "The specified schema name "dbo" either does not exist or you do not have permission to use it ".
var connectionString = dataAccessManage.ConnectionStringToBroker;
_tableDependency = new SqlTableDependency<Plan>(connectionString, schemaName : "",executeUserPermissionCheck: false); ;
try
{
_tableDependency.OnChanged += Plan_OnChanged;
_tableDependency.Start();
Console.WriteLine("Waiting for receiving notifications...");
}
catch (Exception ex)
{
LogsWritter.SendErrorToText(ex, errorLogConfiguration);
_tableDependency.Stop();
throw;
}
We did some troubleshooting and found if we grand below permission for NON-DBO users it will work fine.
Permission -
ALTER
CONNECT
CONTROL
CREATE CONTRACT
CREATE MESSAGE TYPE
CREATE PROCEDURE
CREATE QUEUE
CREATE SERVICE
EXECUTE
SELECT
SUBSCRIBE QUERY NOTIFICATIONS
VIEW DATABASE STATE
VIEW DEFINITION
The problem is these permission statements will not pass our security review. We cannot authorize db_owner permissions, and cannot grant ALTER, CONTROL, EXECUTE, and SELECT to the entire database (which is basically db_owner permissions).
So need to know the solution for enabling SQLTableDependency for limited permission user, Which passes our security review.

Alachisoft Ncache SqlCacheDependency adding dependency through stored procedure

We are trying to implement SqlCacheDependency and while adding the dependency through Stored Procedure it remove the object immediately after adding it to cache.It gives CacheItemRemovedReason as 4.
We have used the Database Synchronization sample provided in mentioned below link.
http://www.alachisoft.com/resources/samples/
string storedProcName = "dbo.TestStoredProc1";
SqlCmdParams sqlParams = new SqlCmdParams();
sqlParams.Value = prod.ProductID;
//sqlParams.
Dictionary<string, SqlCmdParams> prms = new Dictionary<string, SqlCmdParams>();
prms.Add("ID", sqlParams);
_cache.Add(prod.ProductID.ToString(), item,DSWriteOption.None,null);
item.Dependency = new SqlCacheDependency(connString, "select * from dbo.state_lookup where state_id = " + prod.ProductID.ToString());
Database Synchronizationpublic void OnCacheDataModification(string key, CacheEventArg args)
{
switch (args.EventType)
{
case EventType.ItemAdded:
// Key has been added to the cache
Console.WriteLine(key + " Item Added");
break;
case EventType.ItemUpdated:
Console.WriteLine(key + " Item Updated");
break;
case EventType.ItemRemoved:
break;
}
I have added item change listener and it goes to ItemRemoved section immediatly after going into ItemAdded part.The item removed call back should get called only when data is changed in the database.
SQL Server Version : Microsoft SQL Server 2008 R2 (SP2)
Alachisoft Edition : Enterprise Trial Version
Alachisoft SDK version : 4.9.1.1
there are a few things that you need to look into while working with NCache SQLDependency and I have listed them below.
• NCache SQLDependency makes use of SQL Broker service to invalidate and remove items from cache based on notifications. Please make sure that you have enabled the broker service for your database so that NCache SQLDependency can work without any issues as mentioned in the steps here.
• Moreover, please note that SQLDependency has a few limitations on the statements that are supported when you create a query for SQLDependency. This limitation gets inherited by NCache SQLDependency feature as well since NCache depends upon SQL Broker. One of the main limitation is with the SELECT statement, which is that "The statement may not use the asterisk () or table_name. syntax to specify columns.". Your SQLDependency query should follow all the rules and requirements that have been mentioned by Microsoft.
• A complete list of requirements for creating a query for notification on SQL Server can be found at the following MSDN site: https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms181122(v=sql.105)
• Moreover, please note that whenever NCache SQLDependency fails to get registered, the corresponding item (for which the dependency was added) gets removed instantaneously, and an error message gets logged in the server logs. Please go through your server logs and you should be able to see an error entry stating that SQL Broker was unable to establish SQLDependency due to an invalid SQL query.
Once you have addressed all the above mentioned items, your SQLDependency should work flawlessly.

How do you connect from ODP.NET to Oracle (12G+) by proxy user with no password

There seems to be no answer online as to how you can use Oracle Data Provider for .NET (ODP.NET) to connect to Oracle (12G and later) in a very specific scenario:
User is identified externally on a database
User is granted access to another schema (application user) by proxy connect
User has been set up like this:
CREATE USER user_in_question
IDENTIFIED EXTERNALLY
-- etc.
And connect by proxy has been set up like this:
ALTER USER specified_app_user GRANT CONNECT THROUGH user_in_question
The logical approach when creating the ODP.NET OracleConnection string would be something like this (using the user friendly OracleConnectionStringBuilder):
var connBuilder = new OracleConnectionStringBuilder
{
UserID = "/", // External login using the user running the program
ProxyUserId = "specified_app_user",
DataSource = "database",
};
This does not work. Nor does providing blank "Password" or blank "Proxy Password". Nor does removing the UserId.
So how do you connect using ODP.NET in these circumstances?
The answer (which I spend an hour searching for without any luck) is actually really simple, yet not very user friendly:
var connBuilder = new OracleConnectionStringBuilder
{
UserID = "[specified_app_user]",
DataSource = "database",
};
//connBuilder.ToString() output:
//"USER ID=[specified_app_user];DATA SOURCE=database"
This works in .NET 4.5+ on Oracle 12G+, but probably also on earlier platforms of .NET/Oracle/ODP.NET. I did not test it in ASP.NET, but it should work there too.
This way the UserId actually functions just like the ProxyUserId, just enclosed within brackets, just as you would normally log in on the Oracle Database using, say, Toad or SQlPlus.
It might also be possible using this format (but in my case the connection string had to be compatible with the OraOLEDB format so that did not work):
//Without the use of the conn string builder class, just for the fun of it...
var connString = "User Id=specified_app_user;Data Source=database;Proxy User Id=/";
EDITED 2nd March 2017: The line above does not seem to work in certain cases. Added comment about it and here is the code that IS working:
USER ID=[specified_app_user];DATA SOURCE=database
This info does not seem to exist anywhere - else I overlooked it, and in that case PLEASE do correct me.

Azure Mobile Services PullAsync() Not Filling Sync Table

So I have a remote table called Profile that has multiple entries made already. Now I'm trying to integrate offline capabilities into my application. As of right now I'm having issues with PushAsync and PullAsync methods in my code.
I would like to be able to copy all the data from my remote table into my local table just by calling PullAsync, and although it doesn't throw any exceptions it also doesn't populate my table. Here's an example of what I mean.
var db = new SQLiteConnection("syncstoreTEMP.db");
var store = new MobileServiceSQLiteStore("syncstoreTEMP.db");
store.DefineTable<Profile>();
MobileService.SyncContext.InitializeAsync(store).Wait();
var remoteValues = MobileService.GetTable<Profile>()
.ToListAsync()
.Result;
MobileService.GetSyncTable<Profile>()
.PullAsync(null, MobileService.GetSyncTable<Profile>().CreateQuery())
.Wait();
var localValues = MobileService.GetSyncTable<Profile>()
.ToListAsync()
.Result;
When I run this code remoteValues has a total length of 5, but localValues still shows 0 entries even after issuing a Pull from remote. How can I get the sync table to match my remote table?
It's strange that you're not getting any exceptions, but you seem to be creating two connections to your local store. You should not have the line for new SQLiteConnection, only the line that creates the MobileServiceSQLiteStore.
Here's a tutorial that walks through how to set things up: https://azure.microsoft.com/en-us/documentation/articles/mobile-services-xamarin-ios-get-started-offline-data/
I also recommend that you add a DelegatingHandler to your MobileServiceClient in order to log all of the requests and responses. Then you can see exactly what's going wrong. Here's a sample that shows this, and also includes a logging extension to the SQLite store so you can see exactly what is happening locally: https://github.com/paulbatum/FieldEngineerLite/blob/master/FieldEngineerLite.Client/FieldEngineerLite/Helpers/LoggingHelpers.cs

Categories