Using Kentico 7 API via Console App - c#

I have the following, using Kentico API 7 via a console application:
String connectionString = CMS.DataEngine.ConnectionHelper.GetConnectionString("MyConnString");
Console.WriteLine("connectionString ? " + connectionString);
//CMS.DataEngine.GeneralConnection
CMS.DataEngine.GeneralConnection conn = CMS.DataEngine.ConnectionHelper.GetConnection(connectionString);
conn.Open();
Console.WriteLine("connection is open? " + conn.IsOpen());
CMS.CMSHelper.CMSContext.Init();
Console.WriteLine("CurrentSiteID " + CMS.CMSHelper.CMSContext.CurrentSiteID);
The connection is open. I get error
Console.WriteLine("CurrentSiteID " + CMS.CMSHelper.CMSContext.CurrentSiteID);
that says connection is not initialized. Got help?

It certainly is possible to use Kentico API outside of Kentico itself. Recently, I published an article on this topic. However, the article demonstrates the possibility on a newer version of Kentico. But back to your problem...
The CMS.CMSHelper.CMSContext.Init(); method expects a connection string named "CMSConnectionString" to exist in your app.config or web.config.
The documentation also says
You can call this method at any point in your application's life cycle, but it must occur before you use any other Kentico CMS API.
so you should not be touching CMS.DataEngine or any other CMS.* namespace before you call CMSContext.Init().
Once you call that method you can start using the parameter-less overload ConnectionHelper.GetConnection() but I would advise you to take advantage of the Info-Provider pattern that Kentico offers instead of using the direct DB access through CMS.DataEngine.
For instance, this is how you delete users:
// Get the user
UserInfo deleteUser = UserInfoProvider.GetUserInfo("MyNewUser");
// Delete the user
UserInfoProvider.DeleteUser(deleteUser);

Unless there's a specific need to create a console application to perform your task, then I would recommend avoiding a console app and instead creating a custom scheduled task.
You can write the code that your task needs to perform within the App_Code folder of your project - or within the CMSAppCode project if you're using a web application project type. This way you don't have to worry about having access to the database or referencing all the DLLs you'll need to utilize the Kentico API.

Related

Access to C:\Users unauthorized when running .NET Core 3.0

I am migrating a library from .NET Framework to .NET Core. In this library, I offer a convenient way to save XML data in the user's AppData folder.
When running under .Net Core, I get an exception:
Access to the path 'C:\Users' is denied
The value of _userDataPath at that time is:
C:\Users\cedbo\AppData\Roaming\MyApp
This works fine under .NET Framework, so it is not an authorization problem.
// compute filepath
_userDataPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
appName);
_userDataFile = Path.Combine(_userDataPath, "UserData.xml");
// load data or create file if required.
T userData = null;
if (File.Exists(_userDataFile)) // !!! Returns false even when file exists
{
userData = XmlTools.GetFromXml<T>(_userDataFile);
}
else
{
userData = new T();
if (!Directory.Exists(_userDataPath)) // !!! Returns false even when directory exists
{
Directory.CreateDirectory(_userDataPath); // Exception: Access to the path 'C:\Users' is denied.
}
XmlTools.SaveToXml<T>(userData, _userDataFile);
}
I am ready to use another standard approach if .NET Core proposes one, but I didn't find anything neither on this write access limitation, nor on another way to store user data recommended in .NET Core.
Can anyone please advise?
Thank you in advance.
Here are my findings:
I was trying to run this code from a UWP application. I tried but couldn't write anything either on other locations such as C:\ or D:\ drives (D: is a second hard drive).
I eventually found that UWP files access if different from what I used to do with WPF for instance: https://learn.microsoft.com/en-us/windows/uwp/files/file-access-permissions
For instance, ApplicationData.Current.LocalFolder will return a StorageFolder object that contains methods such as CreateFile, GetItemsAsync, etc. This is typically the place where I would store user data if I wanted to keep it in a file. See https://learn.microsoft.com/fr-fr/uwp/api/windows.storage.storagefolder?view=winrt-19041 for more information.
Given this substantial change in the way I'd store user data, I will consider using EntityFrameworkCore on a sqlite database instead:
https://learn.microsoft.com/en-us/windows/uwp/data-access/sqlite-databases
I hope that sharing this adventure will help others facing the same situation.
Happy coding to all!

C# Executing a query and getting data out of an Access Database (accdb) without using ACE.OLEDB

I'm writing a WPF application.
Trying to use the normal method of getting a connection returns an error similar to: "The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine."
ACE.OLEDB has never been installed on this machine so this error makes sense.
I'm trying to create this application in a way so that our users won't need to contact IT to have the application installed. Getting IT involved is a no go situation and the project will be abandoned.
Another team has an Access database (accdb) that I want my application to extract information (only read, no insert or update). I talked to the team and they won't convert this database back to an earlier version (mdb).
After my research I assume that installing ACE.OLEDB without using Admin privileges is impossible. Because of this and my application requirement of not requiring admin privileges I need to start looking for "Mutant"/Dirty solutions that don't involve ACE.OLEDB.
I tried using power-shell but I'm getting the same problems as I had with C# (requires IT to install ACE.OLEDB).
I have two potential solutions. One write a VBA script that opens up the database and dumps a query result into a file. My C# application would call this VB script and then parse the created file.
The second option is to create a new Access process using Process.Start(fullFilePath) and somehow pass the command to execute a query and somehow pass the results back to the executing application (either via a method return or first to a file).
How would you get the data out?
Is there a way for C# to duplicate the DB file and convert it from (accdb -> mdb)?
This is the second question I ask that is very similar.
C# Connecting to Access DB with no install
The difference between the two (to prevent this is a duplicate question) is that in the previous question I was looking for ways to install ACE.OLEDB without admin privileges while here I'm just looking for any other work around.
Found a workaround. It uses Microsoft.Office.Interop.Access found in NuGet.
var accApp = new Microsoft.Office.Interop.Access.Application();
accApp.OpenCurrentDatabase(#tests.DatabasePath);
Microsoft.Office.Interop.Access.Dao.Database cdb = accApp.CurrentDb();
Microsoft.Office.Interop.Access.Dao.Recordset rst =
cdb.OpenRecordset(
"SELECT * FROM Users",
Microsoft.Office.Interop.Access.Dao.RecordsetTypeEnum.dbOpenSnapshot);
while (!rst.EOF)
{
Console.WriteLine(rst.Fields["username"].Value);
rst.MoveNext();
}
rst.Close();
accApp.CloseCurrentDatabase();
accApp.Quit();

C# Open Same Application From Different Url and pass parameters

The steps of my application are:
Go to the setting page first, and the setting page will register the Registry Log (as 'regedit' in command line) in the background (people may seldom go to the setting page).
When users clicks the URL in a web page, it will trigger the registry and open my application.
The appplication reads the parameter that it gets and does things depending on the parameter value.
User may click on different links to send different parameters to my application
That is, if the application is not opened, it should be launched it and reads the parameter. If the application is already opened, it should just read the parameter.
The problem is: how could find out the different situations of my application - whether it is opened or not - and then use the parameter correctly?
The part of registry( in setting page):
Registry.ClassesRoot.CreateSubKey("MyApp").SetValue("", "URL:MyApp Protocol");
Registry.ClassesRoot.CreateSubKey("MyApp").SetValue("URL Protocol", "");
Registry.ClassesRoot.CreateSubKey("MyApp\\DefaultIcon").SetValue("", "\"" + Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + "\\" + "MyApp.exe" + ",1\"");
Registry.ClassesRoot.CreateSubKey("MyApp\\shell\\open\\command").SetValue("", "\"" + Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + "\\" + "MyApp.exe" + "\" \"%1\"");
%1 is the parameter I will get( from url to my application).
And the web link may be:
Call for Function 1
Call for Function 2
So there are many links in the web page to call same application.
But I cannot let my application be opened every time (that is, there should be only one application opened, and other clicks on links will only send parameters to the app).
I know how to find out whether the application is opened or not by the code:
Mutex mul = null;
bool is_createdNew;
try
{
string mutexName = Process.GetCurrentProcess().MainModule.FileName.Replace(Path.DirectorySeparatorChar, '_');
mul = new Mutex(true, "Global\\" + mutexName, out is_createdNew);
if (!is_createdNew)
{
// application be opened already, I close the application originally
Environment.Exit(Environment.ExitCode);
}
else
{
// the application is first run, open my MainWindow
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
}
Is it possible to send the parameter as a method of registry when the application is opened?
I even think about reading registry by Registry.GetValue, when my application starts up,
to use timer to read registry value per second......
This is my first time face this situation of user's request,
hope someone can give me any direction!
Thanks in advance.
When you find out that another instance of your application is already running (which you do in your code above using Mutex), you can programatically pass the parameter (of the second app instance) to the first, already running app instance and then just close the second app instance. This code for passing parameter to the first app instance would then be just before Environment.Exit(Environment.ExitCode);
(Presuming that your app is relatively small and does not loads lots of libraries on startup - in that case it would be better to create a separate small launcher app)
The problem is, how to pass the parameter between two independent processes - two instances of your app.exe. There are of course several options, look here:
Send/Receive message To/From two running application
I would use FileWatcher or Memory mapped file as it is specified in that answer.
The solution with timer and changing registry values is not good (registry operations require admin access, registry operations are not so fast etc.).
Here is a nice library that shows, how to pass parameters between 2 processes using Memory mapped file.
http://code.msdn.microsoft.com/windowsdesktop/Inter-process-communication-e96e94e7
I found another way to solve my problem,
just simply add 'static' before Mutex.
See the detail: C# static

Change SSRS data source of report programmatically in server side

Today, for each customer, we deploy same SSRS reports folder and data source folder.
The difference between these folders are the name of each folder and the connection string of the data source.
We are using Report Server 2008 R2.
Is it possible to maintain only one reports and data source folder and change programmatically its connection string on server-side before the report been rendered?
If not, Is it something that can be achieved by changing some logic in reports?
Today we use "shared data source" option.
This is something we've done in our environment - we maintain one set of reports that can be deployed at any client with their own configuration.
You've got a couple of options here. Since you're using a Shared Data Source this makes things easier as you won't need to define a Data Source for each report.
1. Use the rs.exe utility and a script file
rs.exe at Books Online
This program allows you to create script files (in VB.NET) that can interact with a Report Server Web Service. You create a script file (e.g. Deploy.rss) and call the rs.exe program with various parameters, including any custom ones you define:
rs.exe -i DeployReports.rss -s http://server/ReportServer -v DatabaseInstance="SQL" -v DatabaseName="ReportDB" -v ReportFolder="ClientReports"
So this would call a script DeployReports.rss, connect to http://server/ReportServer, with three user defined parameters which could be used to create a data source and the report folder.
In the scipt file you could have something like this:
Public Sub Main()
rs.Credentials = System.Net.CredentialCache.DefaultCredentials
CreateFolder(reportFolder, "Report folder")
CreateFolder(datasourceFolder, "Data source folder")
CreateDataSource()
End Sub
Which can then make Web Service calls like:
rs.CreateFolder(folderName, "/", Nothing)
'Define the data source definition.
Dim definition As New DataSourceDefinition()
definition.CredentialRetrieval = CredentialRetrievalEnum.Integrated
definition.ConnectString = "data source=" + DatabaseInstance + ";initial catalog=" + DatabaseName
definition.Enabled = True
definition.EnabledSpecified = True
definition.Extension = "SQL"
definition.ImpersonateUser = False
definition.ImpersonateUserSpecified = True
'Use the default prompt string.
definition.Prompt = Nothing
definition.WindowsCredentials = False
Try
rs.CreateDataSource(datasource, datasourcePath, False, definition, Nothing)
Console.WriteLine("Data source {0} created successfully", datasource)
Catch e As Exception
Console.WriteLine(e.Message)
End Try
You haven't specified what version of Reporting Services you're using, so I'm assuming 2008. Please note that there are multiple endpoints that can be used, depending on SQL Server version. The 2005/2008 end point is deprecated in 2008R2 and above but is still usable. Just something to bear in mind when writing your script.
2. Call the SSRS Web Service through an application
Report Server Web Service overview
The same calls that are made from the script above can be made in any other application, too. So you'd just need to add a reference to a Report Server Web Service through WSDL and you can connect to a remote service and call its methods to deploy reports, data sources, etc.
So ultimately you're connecting to the Report Server Web Service, it's just the medium used that you need to think about.
Using a script is easier to get running as it's just running a program from the command line, but writing your own deployment application will certainly give greater flexibility. I would recommend getting the script going, so you understand the process, then migrate this to a bespoke application if required. Good luck!
You can use an Expression Based Connection String to select the correct database. You can base this on a parameter your application passes in, or the UserId global variable. I do believe you need to configure the unattended execution account for this to work.
Note: be careful about the security implications. Realize that if you would pass sensitive data (e.g. passwords) into a parameter, that (a) it will go over the wire, and (b) will be stored in the execution log tables for reporting services.

Trying to use Launcher.LaunchUriAsync in a Share Target app win Win 8

I’m building an app that acts as a share target for URI’s; it takes the URI then URL for invokes the browser to our web site for sharing. I have the code working using the sharing target sample, however Launcher.LaunchUriAsync is always failing when I’m outside the debugger. It doesn’t throw though so I’m having a hard time figuring out what the error might be; maybe some security context.
Any ideas? The code itself is pretty short; I get success = false running directly from the shell and invoking via Win-C/Share. I’ve made sure the manifest has all three networking declarations set. Other thoughts?
if (this.shareOperation.Data.Contains(StandardDataFormats.Uri))
{
Uri uri = await this.shareOperation.Data.GetUriAsync();
if (uri != null)
{
Uri tempUri;
Uri.TryCreate("http://www.mysite.com/#/search?v=results&bk=1.0&q="+uri.ToString(), UriKind.Absolute, out tempUri);
bool success = await Windows.System.Launcher.LaunchUriAsync(tempUri);
if (success) {
contentValue.Text += "Success invoking browser" + ":URL="+tempUri.ToString()+ Environment.NewLine;
} else {
contentValue.Text += "Fail invoking browser" + ":URL=" + tempUri.ToString() + Environment.NewLine;
}
}
}
A sharing target shouldn't be launching another application. Share targets are meant to be hosted inside the sharing pane, and should provide a quick, in-context way for the user to share content with your app or service.
In your case, perhaps you should host a WebView and navigate that to your service's URL?
There are several conditions that needs to be met in order to successfully call LaunchUriAsync.
As seen here on MSDN you need to ensure the following:
The calling app must be visible to the user when the API is invoked.
Unless you are calling this API from a Classic Windows application, this API must be called from within an ASTA thread (also known as a UI thread).
You must specify the privateNetworkClientServer capability in the manifest in order to launch intranet URIs, for example a file:/// URI pointing to a network location.
You cannot use this method to launch a URI in the local zone. For example, apps cannot use the file:/// protocol to access files on the local computer. Instead, you must use the Storage APIs to access files.
I guess that one of the first two conditions is why your code fails. (At least that was my problem)

Categories