Sacha Tomey

Sacha Tomey's Blog

PerformancePoint Planning: Deleting a Custom Member Property - A Solution

I had a bit of a rant yesterday about the fact I have had to compromise naming member properties when I've inadvertently created them with the wrong data type.  As I mentioned, I found a Dimension attribute collection method in the Planning client assemblies that hinted that it might allow me to delete a member property so I decided to give it a go.

Below is some really rough and ready C# code that actually does delete a dimension member property.  I will improve the code and probably add it in to my PPSCMD GUI interface as a 'feature pack' bonus at some stage, however, if you are in desperate need for the code to delete a member property, and you can't wait for PPSCMD GUI v0.2 or PerformancePoint Version 2 (I'm not sure which will come first !) the code is below (Use at your own risk !!)

Note:  Replace "MyApp", "MyDimension", "MyAttribute", oh, and the server address, accordingly..

    using Microsoft.PerformancePoint.Planning.Client.Common;
    using Microsoft.PerformancePoint.Planning.Bmo.Core;




..
// Setup the PPS Application Metadata Manager ServerHandler serverHandler = new ServerHandler("http://localhost:46787"); MetadataManager manager = new MetadataManager(); manager.ServerHandler = serverHandler; manager.ServerHandler.Connect(); // Get the system metadata BizSystem system = manager.GetSystem(true); // Get hold of the PPS Application BizApplication ppsApp = system.Applications["MyApp"]; // Obtain the root model site from the application BizModelSite site = ppsApp.RootModelSite; // Obtain the dimension that contains the member property BizDimension dimension = site.Dimensions["MyDimension"]; // Obtain the member property BizDimensionAttribute attribute = dimension.Attributes["MyAttribute"]; // Check out the dimension manager.CheckOut(dimension.Id, dimension.ParentModelSite.Id); // Perform the delete dimension.DeleteDimensionAttribute(attribute, null); // Submit the change manager.SubmitModelSite(ppsApp.Id, dimension.ParentModelSite, Microsoft.PerformancePoint.Planning.Bmo.Interfaces.SubmissionType.Update); // Check in the dimension manager.CheckIn(dimension.Id, dimension.ParentModelSite.Id);
Update:  I've since discovered that you can obtain an unsupported utility from Microsoft Support that reportedly does the same thing, doh !  
Oh well, always nice to have the code ..J

XML Namespaces and the ADOMD XML Reader Result Set

I'm building a generic helper class to flatten an ADOMD data set to a data table for easier consumption by default and third party .NET controls.  I can't rely on the Load method of the data table, nor the CellSet object as in some cases not all the information is persisted; datatypes and top level hierarchies to name two.

To persist all of the information I need to traverse the actual xml dataset returned by the ExecuteXMLReader method of the ADOMDCommand object.

I don't use xml enough these days to remember all the nuances with namespaces and it took me a little while to work out what to set the namespaces to in order for the 'SelectNodes' method to work.

Below is the code snippet I used to ensure the output from the ExecuteXmlReader method is fully traversable.

// Execute the command returning the XML data set
XmlReader reader = command.ExecuteXmlReader();

// Load the object into an XML document
XmlDocument xml = new XmlDocument();
xml.Load(reader);

// Create an XML namespace manager
System.Xml.XmlNamespaceManager nsManager=new XmlNamespaceManager(xml.NameTable);

// Add the namepsaces to the manager
nsManager.AddNamespace("defaultNS","urn:schemas-microsoft-com:xml-analysis:mddataset");
nsManager.AddNamespace("xsi",
http://www.w3.org/2001/XMLSchema-instance);
nsManager.AddNamespace("xsd",
http://www.w3.org/2001/XMLSchema);

// Return a list of nodes that represent the row information
XmlNodeList rows = xml.SelectNodes("defaultNS:root/defaultNS:Axes/defaultNS:Axis[@name ='Axis1']", nsManager);

This kb article helped me out a bit:
http://support.microsoft.com/default.aspx?scid=kb;en-gb;q318545

There are some more goodies included in my helper class that I will blog about in the future, once it's stable !

Invalidating ASP.NET OutputCache

You can invalidate ASP.NET output level cache using cache dependencies.
 
The ASP.NET webform should contain something like this:
 
<%@ OutputCache Duration="60" Location="Server" VaryByParam="None" %>
 
If you need to invalidate the cache prior to the 60 seconds elapsing you can do this by setting up a cache dependency between the page and an item in the cache:
 

Response.AddCacheItemDependency("MyCachedData")

 

Now, when the "MyCachedData" cachekey is changed or invalidated the cached page is invalidated too.

Enterprise Library Woes

When deploying the enterprise library to a production server the following errors (or similar) can be fixed by running the 'InstallUtil' utility against the enterprise library assemblies.
 

Failed to fire the WMI event 'DataConnectionOpenedEvent'. Exception: System.Exception: This schema for this assembly has not been registered with WMI.

at System.Management.Instrumentation.Instrumentation.Initialize(Assembly assembly)

at System.Management.Instrumentation.Instrumentation.GetInstrumentedAssembly(Assembly assembly)

at System.Management.Instrumentation.Instrumentation.GetFireFunction(Type type)

at System.Management.Instrumentation.BaseEvent.get_FireFunction()

at System.Management.Instrumentation.BaseEvent.Fire()

at Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation.InstrumentedEvent.FireWmiEventCore(BaseEvent baseEvent).

Recycle IIS6.0 application pools from code

Works with IIS6.0 Windows Server 2003 SP1...
 

public static void Recycle(string host, string poolname)

{

using

(DirectoryEntry apppool = new DirectoryEntry(string.Format("IIS://{0}/w3svc/apppools/{1}", host, poolname)))

{

apppool.Invoke("Recycle");

}

}

PartialCachingControl.CachedControl confusion

 
If you are using the @ OutputCache directive on a user control that you are adding programatically you will not receive a reference to the control type directly when you 'LoadControl'.  Instead you will be returned an object of type PartialCachingControl.
 
The CachedControl property is not hugely intuitive, it's actually set when the object is cached the first time - it's the property saying, "By the way, I've cached this object for you, just so you know".  If the object is already in the cache the CachedControl property returns null, this time it's saying, "I haven't had to cache anything as it was already in the cache".
 
If you do not add the PartialCachingControl to the control tree the CachedControl property will ALWAYS return null.
 
Here is a simple routine that demonstrates this:
 

void Page_Init()
{

Control Item;

Item = Page.LoadControl("test1.ascx");

Test1a.Controls.Add(Item);  // Comment this line to see the "always null" behavior.

if ( ((PartialCachingControl)Item).CachedControl != null)
{
      Response.Write("CachedControl is not null! - Control was not in the cache but is now ");

}
else
{
     Response.Write("CachedControl is null! - Control was already in the cache");

}

 

// To obtain a reference to your object access the control collection (Alternatively you can use 'FindControl')

MyUserControl myControl = (MyUserControl) Item.Controls[0];

}