I am currently developing a utility to help automate our report deployment process. Multiple files, in multiple folders, to multiple servers.
I am using the reportservice2010.asmx web service, and I am deploying my files to the server - so most of the way there.
My issue is that I have shared data sets and shared data sources, which are deployed to individual folders, separate to the report folders. When the deployment occurs the web service looks locally for the data source rather than in the data source folder, giving an error like:
The dataset ‘CostReduction’ refers to the shared data source ‘CostReduction’, which is not
published on the report server. The shared data source ‘CostReduction’ must be published
before this report can run.
The data source/set has been deployed and the report functions correctly but I need to suppress these error messages as they may be hiding other actual errors.
I can hard code a lookup that checks if the data source/set exists and manually filter them via that, but it seems very in-efficient. Is there any way I can tell the web service where to look for these files or another approach that other people have used?
I'm not looking at changing the reports so the data source is read from
/DataSources/DataSourceName
as there are lots of reports and that's not how our existing projects are configured.
Many thanks in advance.
I realize you are using VB, but perhaps this will give you a clue if you convert it from C# to VB, using one of the translators on the web.
Hopefully this will give you a lead in the right direction.
When All the reports in a particular folder, referred to here as the 'parent folder', all use the same Shared Data source, I use this to set all the reports to the same shared Data Source (in this case "/DataSources/Shared_New")
using GetPropertiesSample.ReportService2010;
using System.Diagnostics;
using System.Collections.Generic; //<== required for LISTS
using System.Reflection;
namespace GetPropertiesSample
{
class Program
{
static void Main(string[] args)
{
GetListOfObjectsInGivenFolder_and_ResetTheReportDataSource("0_Contacts"); //<=== This is the parent folder
}
private static void GetListOfObjectsInGivenFolder_and_ResetTheReportDataSource(string sParentFolder)
{
// Create a Web service proxy object and set credentials
ReportingService2010 rs = new ReportingService2010();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
CatalogItem[] reportList = rs.ListChildren(#"/" + sParentFolder, true);
int iCounter = 0;
foreach (CatalogItem item in reportList)
{
iCounter += 1;
Debug.Print(iCounter.ToString() + "]#########################################");
if (item.TypeName == "Report")
{
Debug.Print("Report: " + item.Name);
ResetTheDataSource_for_a_Report(item.Path, "/DataSources/Shared_New"); //<=== This is the DataSource that I want them to use
}
}
}
private static void ResetTheDataSource_for_a_Report(string sPathAndFileNameOfTheReport, string sPathAndFileNameForDataSource)
{
//from: http://stackoverflow.com/questions/13144604/ssrs-reportingservice2010-change-embedded-datasource-to-shared-datasource
ReportingService2010 rs = new ReportingService2010();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
string reportPathAndName = sPathAndFileNameOfTheReport;
//example of sPathAndFileNameOfTheReport "/0_Contacts/207_Practices_County_CareManager_Role_ContactInfo";
List<ReportService2010.ItemReference> itemRefs = new List<ReportService2010.ItemReference>();
ReportService2010.DataSource[] itemDataSources = rs.GetItemDataSources(reportPathAndName);
foreach (ReportService2010.DataSource itemDataSource in itemDataSources)
{
ReportService2010.ItemReference itemRef = new ReportService2010.ItemReference();
itemRef.Name = itemDataSource.Name;
//example of DataSource i.e. 'itemRef.Reference': "/DataSources/SharedDataSource_DB2_CRM";
itemRef.Reference = sPathAndFileNameForDataSource;
itemRefs.Add(itemRef);
}
rs.SetItemReferences(reportPathAndName, itemRefs.ToArray());
}
}
To Call it I use this in the 'Main' Method:
GetListOfObjectsInGivenFolder_and_ResetTheReportDataSource("0_Contacts");
In this case "0_Contacts" is the parent folder, itself located in the root directory, that contains all the reports for which I want to reset their DataSources to the new Shared DataSource. Then that Method calls the other method "ResetTheDataSource_for_a_Report" which actually sets the DataSource for the report.
Related
I work on a plugin-based application that is currently scanning the Windows registry for compatible COM servers that expose certain "Implemented Categories" entries. This works well for "regular" COM servers installed through MSI installers.
However, I'm now facing a problem with COM servers installed through MSIX installers that expose COM extension points through the "Packaged COM" catalog as described in https://blogs.windows.com/windowsdeveloper/2017/04/13/com-server-ole-document-support-desktop-bridge/ . These COM servers can still be instantiated through CoCreateInstance, but RegOpenKey/RegEnumKey searches aren't able to detect their presence.
I'm not sure how to approach this problem. The best outcome would be some sort of Windows API for querying the "Packaged COM" catalog for installed COM servers that I can run in addition to the registry search. However, I don't know if that even exist? I'm also open for other suggestions, as long as they still allows my application to dynamically detect the presence of new COM-based plugins.
PLEASE DISREGARD THIS ANSWER. There's a better answer based on ICatInformation::EnumClassesOfCategories below.
Answering myself with sample code to query the "Packaged COM" catalog for installed COM servers. Based on suggestion from #SimonMourier.
using System.Collections.Generic;
using System.IO;
/** Use Target Framework Moniker as described in https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/desktop-to-uwp-enhance */
class PackagedComScan {
static void Main(string[] args) {
var packageManager = new Windows.Management.Deployment.PackageManager();
// this call require the "packageQuery" capability if called from a UWP app (add <rescap:Capability Name="packageQuery" /> to the appxmanifest)
IEnumerable<Windows.ApplicationModel.Package> my_packages = packageManager.FindPackagesForUser("");
foreach (var package in my_packages) {
try {
ParseAppxManifest(package.InstalledLocation.Path + #"\AppxManifest.xml");
} catch (FileNotFoundException) {
// Installed package missing from disk. Can happen after deploying UWP builds from Visual Studio.
}
}
}
static void ParseAppxManifest(string manifest_path) {
var doc = new System.Xml.XmlDocument();
using (var fs = new FileStream(manifest_path, FileMode.Open, FileAccess.Read, FileShare.Read))
doc.Load(fs);
var nsmgr = new System.Xml.XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("a", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); // default namespace
nsmgr.AddNamespace("com", "http://schemas.microsoft.com/appx/manifest/com/windows10");
// detect exported COM servers
var nodes = doc.SelectNodes("/a:Package/a:Applications/a:Application/a:Extensions/com:Extension/com:ComServer/com:ExeServer/com:Class/#Id", nsmgr);
foreach (System.Xml.XmlNode node in nodes)
System.Console.WriteLine("Exported COM CLSID: {0}", node.Value);
}
}
This is admittedly a bit ad-hoc since it relies on parsing the AppxManifest.xml files. Still, it seems to get the job done. Please note that UWP applications that runs within sandboxed AppContainer processes only seem to have read access to some of the AppxManifest.xml files, and not all. The code therefore only works for "regular" Win32 or .Net processes.
Answering myself with sample code to query all installed COM servers, including the "Packaged COM" catalog, using ICatInformation::EnumClassesOfCategories. Based on suggestion by Aditi_Narvekar:
#include <atlstr.h>
#include <vector>
static void CHECK(HRESULT hr) {
if (FAILED(hr))
abort(); // TODO: More graceful error handling
}
/** Return COM classes that implement any of the provided "Implemented Categories". */
inline std::vector<CLSID> GetClassesWithAnyOfCategories(std::vector<CATID> impl_categories) {
CComPtr<ICatInformation> cat_search;
CHECK(cat_search.CoCreateInstance(CLSID_StdComponentCategoriesMgr));
CComPtr<IEnumGUID> class_list;
CHECK(cat_search->EnumClassesOfCategories((ULONG)impl_categories.size(), impl_categories.data(), -1, nullptr, &class_list));
std::vector<CLSID> app_clsids;
app_clsids.reserve(64);
for (;;) {
CLSID cur_cls = {};
ULONG num_read = 0;
CHECK(class_list->Next(1, &cur_cls, &num_read));
if (num_read == 0)
break;
// can also call ProgIDFromCLSID to get the ProgID
// can also call OleRegGetUserType to get the COM class name
app_clsids.push_back(cur_cls);
}
return app_clsids;
}
I'm generating tables via OrmLite and I was wondering about best practices for prepopulating tables. Example tables - countries, states, cities, etc.
I can think of a few ways to pre-populate tables:
List item
Seed DB
API (when possible)
Static file
In code
Separate project
However, in some cases the data could get large as in the example of cities around the world so in code is not viable.
I could also consider generating tables that need to be pre-populated directly via another project where I can fetch data from a source and get it into the DB.
However, I was wondering about the scenario when you do generate it via an ORM (especially in production). How would you approach the problem?
This must be a common problem across all ORM's.
If it's only code tables like countries, states, etc, they're small enough to still have them as part of the project, normally I'd create a separate static class called SeedData with all the data in POCO's
1. Maintaining Code Tables in Host Project
public static class SeedData
{
public static List<Country> Countries
{
get { return new[] { new Country(...), ... }; }
}
}
Then in your AppHost populate add a flag on whether to re-create them on startup, e.g:
public void Configure(Container container)
{
var appSettings = new AppSettings(); //Read from Web.config <appSettings/>
if (appSettings.Get("RecreateTables", false))
{
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
db.DropAndCreateTable<Country>();
db.InsertAll(SeedData.Countries);
...
}
}
}
Change AppSetting to recreate tables
This will then let you re-create the tables and re-populate the data when you change the RecreateTables appSetting to True, e.g:
<appSettings>
<add key="RecreateTables" value="True" />
</appSettings>
As the default behavior of ASP.NET will automatically restart the AppDomain, just saving a change to Web.config is enough to restart your ASP.NET application the next time any page gets refreshed.
2. Add to Test Project in adhoc Explicit Test
If the Data gets too big to fit in the working project I would first move it to a separate test project inside an [Explicit] text fixture (so it's never automatically run), that you can easily run manuallu, e.g:
[Explicit]
[TestFixture]
public class AdminTasks
{
[Test]
public void Recreate_and_populate_tables()
{
var dbFactory = new OrmLiteConnectionFactory(...);
using (var db = dbFactory.Open())
{
db.DropAndCreateTable<Country>();
db.InsertAll(SeedData.Countries);
...
}
}
}
3. Save data in external static text Files
Finally if the data is even too big to fit in C# classes, I would then save it out to a static file in the test that you can easily re-hydrate into POCO's that you can populate with OrmLite, e.g:
[Test]
public void Recreate_and_populate_tables()
{
var dbFactory = new OrmLiteConnectionFactory(...);
using (var db = dbFactory.Open())
{
db.DropAndCreateTable<Country>();
var countries = File.ReadAllText("~/countries.txt".MapAbsolutePath())
.FromJson<List<Country>>();
db.InsertAll(countries);
...
}
}
I saw a related question:
Sitecore Glass Mapper always null
But unfortunately it does not give a solution for my case.
Here goes a code snippet:
var db = Factory.GetDatabase("master");
var context = new SitecoreContext();
// the ID of Needed item
var g = new Guid("{F21C04FE-8826-41AB-9F3C-F7BDF5B35C76}");
// just to test if it's possible to fetch item using db.GetItem
var i = db.GetItem(new ID(g), Language.Current, Sitecore.Data.Version.Latest);
// Grab item
var t = context.GetItem<Article>(g);
In the code above:
i is not null
t is null
Article is the simple class like:
[SitecoreType(TemplateId = "{4C4EC1DA-EB77-4001-A7F9-E4C2F61A9BE9}")]
public class Article
{
[SitecoreField(FieldName = "Title")]
public string Title { get; set; }
}
There are only one language installed in Sitecore - en, it has been specified in the web.config in the items as well.
Also I have added GlassMapperSc.Start(); to Application_Start in the Global.asax.cs and added my assembly to the list of included assemblies via var attributes = new AttributeConfigurationLoader(new[] { "Assembly.Name" }); and I succeeded to find my class in the SitecoreContext mappings.
It does not looks like a language issue, as stated in the link provided in the very beginning. And I'm struggling with it already for a pretty long time, but no luck...
Thank You!
I just noticed that you are using master db for the Sitecore DB and SitecoreContext for Glass.
The SitecoreContext class will use the database that is defined by the Sitecore.Context.Database property at runtime. This probably means that it is using the web database.
Can you check that you have published the item to the web database or instead using:
var context = new SitecoreService("master");
I installed a TFS2012 as a test system and doing some tests before we go productive.
This includes to define many BuildDefinitions which was a lot of work.
After the tests are successful, an new server will be installed with TFS2012 on it.
For this new server - which operates then as the productive system - i would like to restore the BuildDefinitions from the test system. But only the BuildDefinitions, not the whole TeamCollections. Because i ran test checkins and i don`t want these on my productive server.
Now, is it possible to backup and restore BuildDefinitions only?
Maybe it is possible directly throught the Sql database?, but i`am a little affraid of references there, pointing on some other tables.
Best Regards, Peter Bucher
Build definitions are not source controlled. The only option is relying on the TFS database backup where can restore or view the tbl_BuildDefinition* tables in the Tfs_DefaultCollection database.
There is a user voice for this feature and also you can use TFS API to do it.
Add a vote on uservoice:
provide a way to version-control build definitions
Using TFS API
How can I copy a TFS 2010 Build Definition?
Finally i decided not to touch the database, because there are references to a lot of other tables.
I used the TFS API v11 (TFS2012) and a bit C# Code, which i fitted to my needs from this base: How can I copy a TFS 2010 Build Definition?
It copies all Build Definitions from one TFS2012 Server to another. For both servers there is the need to specifiy a TeamCollection and a TeamProject.
So, the copy-task has to be done per TeamProject.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace TFSBuildDefinitionCreator
{
internal class Program
{
private static void Main(string[] args)
{
// Copies build definitions from one server to another.
// Uses the TeamFoundation API V11 (TFS2012).
// Code was used to copy b uild definitions from a test server to a productive.
string sourceServer = "http://testTfs:8080/tfs/MyTeamCollection";
string sourceTeamProject = "MyTeamProject";
string targetServer = "https://productiveTfs:8080/tfs/MyTeamCollection";
string targetTeamProject = "MyTeamProject";
// DropLocation for defininitions: Share on which the build should be dropped.
string defaultDropLocation = "\\\\MyBuildserver\\Builds$";
// Change the DefaultProcessTemplate in the following method below: GetDefaultProcessTemplateByServerPathFromBuildServer.
CopyBuildDefinitions(sourceServer, sourceTeamProject, targetServer, targetTeamProject, defaultDropLocation);
Console.Read();
}
private static IBuildServer GetBuildServerFromServerUrl(string serverUrl)
{
var tfs = TeamFoundationServerFactory.GetServer(serverUrl);
return (IBuildServer)tfs.GetService(typeof(IBuildServer));
}
private static IBuildController GetDefaultBuildControllerFromBuildServer(IBuildServer buildServer)
{
return buildServer.QueryBuildControllers()[0];
}
private static IProcessTemplate GetDefaultProcessTemplateByServerPathFromBuildServer(IBuildServer buildServer, string teamProject)
{
var processTemplates = buildServer.QueryProcessTemplates(teamProject);
var result = processTemplates.First(t => t.ServerPath.Contains("/BuildProcessTemplates/MyDefaultTemplate.xaml"));
return result;
}
private static void CopyBuildDefinitions(string sourceServer, string sourceTeamProject, string targetServer,
string targetTeamProject, string defaultDropLocation)
{
var sourceBuildServer = GetBuildServerFromServerUrl(sourceServer);
var sourceBuildDetails = sourceBuildServer.QueryBuildDefinitions(sourceTeamProject);
foreach (var sourceBuildDetail in sourceBuildDetails)
{
CopyBuildDefinition(sourceBuildDetail, targetServer, targetTeamProject, defaultDropLocation);
}
}
private static void CopyBuildDefinition(IBuildDefinition buildDefinition, string targetServer, string targetTeamProject, string defaultDropLocation)
{
var targetBuildServer = GetBuildServerFromServerUrl(targetServer);
var buildDefinitionClone = targetBuildServer.CreateBuildDefinition(targetTeamProject);
buildDefinitionClone.BuildController = GetDefaultBuildControllerFromBuildServer(targetBuildServer);
buildDefinitionClone.ContinuousIntegrationType = buildDefinition.ContinuousIntegrationType;
buildDefinitionClone.ContinuousIntegrationQuietPeriod = buildDefinition.ContinuousIntegrationQuietPeriod;
// Noch ändern.
//buildDefinitionClone.DefaultDropLocation = buildDefinition.DefaultDropLocation;
buildDefinitionClone.DefaultDropLocation = defaultDropLocation;
buildDefinitionClone.Description = buildDefinition.Description;
buildDefinitionClone.Enabled = buildDefinition.Enabled;
//buildDefinitionClone.Name = String.Format("Copy of {0}", buildDefinition.Name);
buildDefinitionClone.Name = buildDefinition.Name;
//buildDefinitionClone.Process = buildDefinition.Process;
buildDefinitionClone.Process = GetDefaultProcessTemplateByServerPathFromBuildServer(targetBuildServer, targetTeamProject);
buildDefinitionClone.ProcessParameters = buildDefinition.ProcessParameters;
foreach (var schedule in buildDefinition.Schedules)
{
var newSchedule = buildDefinitionClone.AddSchedule();
newSchedule.DaysToBuild = schedule.DaysToBuild;
newSchedule.StartTime = schedule.StartTime;
newSchedule.TimeZone = schedule.TimeZone;
}
foreach (var mapping in buildDefinition.Workspace.Mappings)
{
buildDefinitionClone.Workspace.AddMapping(
mapping.ServerItem, mapping.LocalItem, mapping.MappingType, mapping.Depth);
}
buildDefinitionClone.RetentionPolicyList.Clear();
foreach (var policy in buildDefinition.RetentionPolicyList)
{
buildDefinitionClone.AddRetentionPolicy(
policy.BuildReason, policy.BuildStatus, policy.NumberToKeep, policy.DeleteOptions);
}
buildDefinitionClone.Save();
}
}
}
Hope that helps others.
I have an InfoPath 2010 form that is published as a content type.
I have a SharePoint library that has this content type enabled. The SharePoint library also have major versioning enabled.
Let's say that I have saved an instance of the form in the library and edited it multiple times so that multiple versions are created.
I need to compare 2 versions against each other to see the exact changes. Is this supported by SharePoint or should I used code for that (I am examining SPDiffUtility now)?
No, SharePoint does not let you compare versions and their differences OOTB.
As far as I can tell SPDiffUtility simply tells you the difference between two strings, but does not support versions just like that. Comparing version is still very easy:
using (SPWeb web = new SPSite("http://sharepoint").OpenWeb())
{
SPList list= web.Lists["Shared Documents"];
SPFile file = list.Files["mydoc.doc"];
//Get all the versions
SPFileVersionCollection fileVersionCollection = file.Versions;
//Get the first version
SPFileVersion fileVersion= fileVersionCollection[3];
//Get the data
byte [] fileBytes = version.OpenBinary();
}
Basically you have to look into the SPFile.Versions collection and compare the versions you have.
The problem is that InfoPath stores its document as XML, so you will have to parse the XML you receive to extract all fields and see their differences - a good start for parsing the XML is to create a class file for easier access in code via xsd.exe like for example explained here.
For reference, here is the complete code I used to compare 2 versions of an InfoPath form
private static void CompareVersions()
{
using (SPWeb web = new SPSite("http://<website_name>").OpenWeb())
{
SPList lib = web.Lists["<library_name>"];
// Assuming that the file has at least 2 versions
var v1 = lib.RootFolder.Files[0].Versions[0];
myFields i1 = GetInstanceFromVersion(v1);
var v2 = lib.RootFolder.Files[0].Versions[1];
myFields i2 = GetInstanceFromVersion(v2);
Console.WriteLine(string.Format("{0,-20} | {1,-20} | {2,-20}", "Version", v1.VersionLabel, v2.VersionLabel));
// List the properties of both versions
Console.WriteLine(string.Format("{0,-20} | {1,-20} | {2,-20}", "Name", i1.Name, i2.Name));
}
}
private static myFields GetInstanceFromVersion(SPFileVersion version)
{
XmlTextReader reader = new XmlTextReader(version.OpenBinaryStream());
myFields fields = (myFields)new XmlSerializer(typeof(myFields)).Deserialize(reader);
return fields;
}