I'm using a SQL CLR trigger to push updates from the relational DB to a MongoDB instance. Both databases are running on the same Windows 2012 machine.
My SQL CLR project is built on .NET 3.5 and is using the mongocsharpdriver 1.10.0.
The C# code within my trigger is as follows:
SqlPipe pipe = SqlContext.Pipe;
pipe.Send("Begin ReportUpdateTrigger.VTProperty");
try
{
var settings = new MongoClientSettings();
settings.Server = new MongoServerAddress("127.0.0.1", 27017);
var client = new MongoClient(settings);
var server = client.GetServer();
var db = server.GetDatabase("VTProperty");
var coll = db.GetCollection<object>("Property");
var item = new { name = "test", datecreated = DateTime.Now };
coll.Insert(item);
}
catch (Exception ex)
{
pipe.Send("Error sending update to Reporting database: " + ex.Message);
}
pipe.Send("Done ReportUpdateTrigger.VTProperty");
(this is test code just to verify that the MongoDB operation will work).
I run the exact same code from a separate console app, and the data is posted to Mongo with no problems.
When running from the trigger, I see the following error:
Begin ReportUpdateTrigger.VTProperty Error sending update to Reporting
database: Unable to connect to server 127.0.0.1:27017: The type
initializer for 'MongoDB.Bson.Serialization.BsonSerializer' threw an
exception.. Done ReportUpdateTrigger.VTProperty
I have my DLL (and all supporting DLLs, including the MongoDB drivers) referenced as assemblies within the DB server. CLR is enabled. I know that the trigger is executing because I am getting the custom status and error messages in the SQL output window.
My hunch is that whatever user/process is executing the trigger code does not have access to the Mongo instance. Hence the "Unable to connect to server 127.0.0.1:27017" error. Not sure what my next step should be.
By default, SQLCLR external access has a security context of the service account (i.e. "Logon As" account) of the MSSQLSERVER service (or MSSQL$InstanceName, or something like that). And that service account, by default, is the Local System account. You can do one of two things here:
If your MSSQLSERVER service is using "Local System" as the account, then create a real local or domain / AD account and make that the service account. Then just make sure that the new real account has access to MongoDB.
Regardless of anything else, it is often best to have services such as SQL Server uses their own service accounts. That makes it easier to control and confine the permssions.
If you are using Windows logins, then you have the option of enabling Impersonation in the .NET code. When using Impersonation, the security context of external calls is set to the Windows Login that is executing the SQLCLR object.
For this, you would need to add something like the following to your code:
using System.Security.Principal;
// above the "try" block
WindowsImpersonationContext _ImpersonationIdentity = null;
// inside the "try", before anything else
_ImpersonationIdentity = SqlContext.WindowsIdentity.Impersonate();
// in a "finally" block
if (_ImpersonationIdentity != null)
{
_ImpersonationIdentity.Undo();
}
Related
I need to create a contained user for my Azure SQL database using C#. I have the following code already for creating the database:
// Login to Azure
var credentials = UserTokenProvider.LoginSilentAsync(clientId, domainName, username, password).Result;
// Create client
SqlManagementClient client = new SqlManagementClient(credentials)
{
SubscriptionId = subscriptionId
};
// Database parameters
var databaseParameters = new Microsoft.Azure.Management.Sql.Models.Database()
{
Location = "ukwest",
ElasticPoolId = elasticPoolId,
};
// Create database
var dbResponse = client.Databases.CreateOrUpdate(resourceGroupName, serverName, databaseName, databaseParameters);
I have found the following SQL script that can be used to create a contained user using SQL management studio but this requires that you manually connect to the database that you want to create the user for:
CREATE USER [databaseUser] WITH PASSWORD = 'xxxxxxxxxxx';
ALTER ROLE [db_datareader] ADD MEMBER [databaseUser]
ALTER ROLE [db_datawriter] ADD MEMBER [databaseUser]
So how can I use the SQL script within my C# code to connect the database after it has been created to then create the contained SQL user for that database, or is there some equivalent code available in Microsoft.Azure.Management.Sql.SqlManagementClient for doing this?
Conceptually, you should think about Azure SQL Database as having (at least) 2 different layers at which you can operate:
There is a REST API for control operations such as creating a database, changing its reservation size, restoring a copy, etc.
There is a T-SQL interface for operations within a database container, from creating tables to inserting rows, etc.
There can be some overlap across these two surfaces - in traditional SQL Server, all of it is exposed in the latter T-SQL interface. Some of those commands are enabled in Azure SQL Database and they are internally calling the REST API for you.
Note that operations to the REST API use the credentials associated with Azure's Portal infrastructure. Within the database, you can have mappings into SQL logins/users, but you can also have SQL logins/users that are not associated at all with the Azure identities.
Net-net: you should connect to the database using the C# SQLClient and run those commands using an authenticated user with enough permissions (in your case, likely the administrator account or sa) to get the new user and role information set up.
Here's a an example on how to set up C# SQLClient
I want to add an option to save data locally in my application using the mongo databse tools, I want to configure all the server information from within my application.
I have 2 questions.
the following code is working only after manual setup of mongodb localhost database in this way:
but on A computer that didn't configure the database setting, the code will not work.
my code is :
public void createDB()
{
MongoClient client = new MongoClient();
var db = client.GetDatabase("TVDB");
var coll = db.GetCollection<Media>("Movies");
Media video = new Media("", "");
video.Name = "split";
coll.InsertOne(video);
}
this code works only after manual set the database like the picture above.
without it I get in the last line A timeout exception.
how can I configure it from my application to make it work (define Server) ?
Is the user will be must install MongoDB software on his PC, or the API Package is enough in order to use the database?
Many Thanks!
By using that command you're not "configuring the database", you're running it.
If you don't want to manually run it, but want it to be always running, you should install it as a Windows Service as explained in How to run MongoDB as Windows service?.
You need to install and/or run a MongoDB server in order to use it. Using the API alone is not enough, it's not like SQLite.
The Code you are using will search for local mongodb.
Is it possible to call the AIF system services using a callcontext of another user?
I specifically need to fetch which tables a user has access to. By using the method GetAccessRights in the UserSessionService, I can extract which tables I have access to.
I need to call this for other users, so I tried setting the CallContext.LogonAsUser to another user, but I get the error:
An unhandled exception of type 'System.ServiceModel.FaultException'
occurred in mscorlib.dll
Additional information: Failed to logon to Microsoft Dynamics AX.
To be able to use LogonAsUser on custom services, I need to set the "Trusted intermediary users" on the inbound port. However, it doesn't seem to be possible to set this on the system services.
I'm testing this on a contoso image. I'm logged in as contoso\Administrator.
The complete code:
USSReference.UserSessionServiceClient client = new USSReference.UserSessionServiceClient();
USSReference.CallContext context = new USSReference.CallContext
{
LogonAsUser = "VishwaR"
};
USSReference.AccessControlledItemKey key = new USSReference.AccessControlledItemKey();
key.ItemType = USSReference.AccessControlledType.Table;
key.ItemName = "CUSTTABLE";
UserService.USSReference.AccessRight[] rights = client.GetAccessRights(context, new USSReference.AccessControlledItemKey[] { key });
You can create custom AIF Inbound Port and add AifUserSessionService.GetAccessRights operation.
Here you can set Allow trusted intermediary to impersonate.
I want to create an Sql Server Authentication Account, not a user in the database or in the account.
What i mean is that i want to create an account so i can login from it when i start the sql server, using SQL Transaction or using C#.
There are only two modes that I know you can use: Windows or SQL Server Authentication. This means you are limited to only two choices: You either use a Windows account, or you create an SQL Server login that you can use to authenticate.
EDIT:
To programmatically create an sql server login, you could use Sql Server Management objects. I notice you didn't say whether you want to do this from a Windows (desktop) or Web application. To use Sql Server Management to create a login (or do any sort of "management"), the code would need to run with higher privileges - say local or domain account with sufficient privileges to administer the SQL server instance.
You will need to add references to (you could use version 9.0 of the assemblies):
Microsoft.SqlServer.ConnectionInfo
Microsoft.SqlServer.Smo
Microsoft.SqlServer.SqlEnum
With that, the following code is enough to create an SQL login and also add the user to your target database
var serverName = "."; // Your SQL Server Instance name
var databaseName = "Test"; // Your database name
var loginName = "testuserY"; // Your login name (should not exist - or you should add code to check if the login exists)
Server svr = new Server(serverName);
var db = svr.Databases[databaseName];
if (db != null)
{
// You probably want to create a login and add as a user to your database
Login login = new Login(svr, loginName);
login.DefaultDatabase = "master"; // Logins typically have master as default database
login.LoginType = LoginType.SqlLogin;
login.Create("foobar", LoginCreateOptions.None); // Enter a suitable password
login.Enable();
User user = new User(db, loginName);
user.UserType = UserType.SqlLogin;
user.Login = login.Name;
user.Create();
// add a role
user.AddToRole("db_owner");
}
You will need to add:
using Microsoft.SqlServer.Management.Smo;
You can add try{} catch{} blocks and plumbing code to make it more robust you would recover gracefully in case of failure
To use transaction construct(as follows) in Subsonic, MSDTC needs to be running on Windows machine. Right?
using (TransactionScope ts = new TransactionScope())
{
using (SharedDbConnectionScope sharedConnectionScope = new SharedDbConnectionScope())
{
// update table 1
// update table 2
// ts.commit here
}
}
Is MS-DTC a default service on Windows systems(XP, Vista, Windows 7, Servers etc)?
If it is not enabled, how can I make sure it gets enabled during the installation process of my application?
MSDTC should come installed with windows. If it's not it can be installed with the following command:
msdtc -install
You can configure the MSDTC service using sc.exe. Set the service to start automatically and start the service:
sc config msdtc start= auto
sc start msdtc
Note you will need administrator privilege to perform the above.
I use:
private bool InitMsdtc()
{
System.ServiceProcess.ServiceController control = new System.ServiceProcess.ServiceController("MSDTC");
if (control.Status == System.ServiceProcess.ServiceControllerStatus.Stopped)
control.Start();
else if (control.Status == System.ServiceProcess.ServiceControllerStatus.Paused)
control.Continue();
return true;
}
This might be helpful:
http://www.thereforesystems.com/turn-on-msdtc-windows-7/
If your DBMS is SQL Server 2000 and you use a TransactionScope a distributed transaction is created even for local Transaction. However SQL Server 2005 (and probably SQL Server 2008) are smart enough to figure out that a distributed Transaction is not needed. I don't know if that only applies to local DB's or even is true if you Transaction only involves a single DB even if it's on a remove server. http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx
One hint, you can use a batch query to avoid the TransactionScope.
http://subsonicproject.com/docs/BatchQuery
BatchQuery, QueueForTransaction and ExecuteTransaction will not use a TransactionScope (of course that depends on the provider implementation) but choose the transaction mechanismn of the underlying data provider (SqlTransaction in this case) which won't require MSTDC.