Tuesday, December 21, 2010

Logging using Enterprise Library - Change the log file Name and Path to the My Documents folder

When using Enterprise Library Logging Application Block as the logging tool, here is how to change the file path and the name of the trace file during run time. What I'm doing here is update the listener's "filename" (loggingConfiguration->listeners) in the App.config.


using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;

/// <summary>
/// Change the log file path and name.
/// </summary>
public void SetTraceLogPath()
{
// Log file path.
string logFilePath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) +
@"\MyLogs\" + DateTime.Now.ToString("yyyy-MM-dd") + ".log";

ConfigurationFileMap objConfigPath = new ConfigurationFileMap();

// App config file path.
string appPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
objConfigPath.MachineConfigFilename = appPath;

Configuration entLibConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

LoggingSettings loggingSettings = (LoggingSettings)entLibConfig.GetSection(LoggingSettings.SectionName);

TraceListenerData traceListenerData = loggingSettings.TraceListeners.Get("FlatFile TraceListener");
FlatFileTraceListenerData objFlatFileTraceListenerData = traceListenerData as FlatFileTraceListenerData;

objFlatFileTraceListenerData.FileName = logFilePath;

entLibConfig.Save();
}

Then I can use following method to log exceptions to the specified path. In this case log file will be created in the My Documents folder of the current user.


///<summary>
/// Log exceptions.
///</summary>
public void LogException(Exception ex)
{
string strMessage = string.Empty;
strMessage += ex.Message + "\r\n";
strMessage += ex.StackTrace;

LogEntry le = new LogEntry();
le.Categories.Add(Constants.LoggingCategory.Exception.ToString());
le.Severity = TraceEventType.Error;
le.Message = strMessage;
le.Title = ex.Message;
le.Priority = 1;

Logger.Write(le);
}

This is how my app.Config file looks. There should be a
FlatFileTraceListener configured as follows.

  <configSections>
<section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<loggingConfiguration name="Logging Application Block" tracingEnabled="true"
defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
<listeners>
<add fileName="trace.log" header="----------------------------------------"
footer="----------------------------------------" formatter=""
listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="FlatFile TraceListener" />
</listeners>
<formatters>
<add template="Timestamp: {timestamp}&#xD;&#xA;Message: {message}&#xD;&#xA;Category: {category}&#xD;&#xA;Priority: {priority}&#xD;&#xA;EventId: {eventid}&#xD;&#xA;Severity: {severity}&#xD;&#xA;Title:{title}&#xD;&#xA;Machine: {machine}&#xD;&#xA;Application Domain: {appDomain}&#xD;&#xA;Process Id: {processId}&#xD;&#xA;Process Name: {processName}&#xD;&#xA;Win32 Thread Id: {win32ThreadId}&#xD;&#xA;Thread Name: {threadName}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="Text Formatter" />
</formatters>
<categorySources>
<add switchValue="All" name="General">
<listeners>
<add name="FlatFile TraceListener" />
</listeners>
</add>
</categorySources>
<specialSources>
<allEvents switchValue="All" name="All Events" />
<notProcessed switchValue="All" name="Unprocessed Category" />
<errors switchValue="All" name="Logging Errors &amp; Warnings">
<listeners>
<add name="FlatFile TraceListener" />
</listeners>
</errors>
</specialSources>
</loggingConfiguration>

Wednesday, December 1, 2010

SharePoint - CRM Integration: Create a SharePoint Document Library on CRM Opportunity Creation

In CRM 4.0 there is no way to maintain a set of related documents for Opportunities created within the CRM. Therefore it is a good idea to integrate CRM and SharePoint here, so that for each Opportunity creation in CRM a document library will get created in a specified SharePoint site automatically. We can achieve this task by registering a plugin to execute in Opportunity Create step.

Plugin implementation

Add a Web Reference to the Lists service of the SP site (or sub-site) located in "/_vti_bin/Lists.asmx"

Document library creation method could be like this.


///<summary>
/// Create a SP document library for a specified Opportunity.
///</summary>
private string CreateDocLibrary(string opportunityName, string spSiteUrl)
{
ListsService.Lists listService = new ListsService.Lists();
listService.Url = String.Concat(spSiteUrl, "/_vti_bin/Lists.asmx");
listService.Credentials = CredentialCache.DefaultCredentials;

XmlNode xnodeList = listService.AddList("Opportunity-" + opportunityName,
opportunityName, 101);

// Get document library URL name.
string rootFolder = xnodeList.Attributes.GetNamedItem("RootFolder").Value;
return rootFolder.Remove(0, rootFolder.LastIndexOf("/"));
}

Call document library creation method with proper parameters and maintain information about new document libraries in another custom SharePoint list.


public void Execute(IPluginExecutionContext context)
{
string opportunityName;
string stateCode;

if (context.MessageName.Equals("Create"))
{
opportunityName = ((DynamicEntity)context.PostEntityImages["Target"]).
Properties["name"].ToString();
stateCode = ((DynamicEntity)context.PostEntityImages["Target"]).
Properties["statecode"].ToString();
string docLibraryUrl = CreateDocLibrary(opportunityName,
"http://prasadmoss2010/sites/CRMTest");
AddConfigurationData(opportunityName, "http://prasadmoss2010/sites/CRMTest");
}
}

///<summary>
/// Update configuartion data custom list.
///</summary>
private void AddConfigurationData(string listName, string spSiteUrl)
{
ListsService.Lists listService = new ListsService.Lists();
listService.Url = String.Concat(spSiteUrl, "/_vti_bin/Lists.asmx");
listService.Credentials = CredentialCache.DefaultCredentials;

// Get Name attribute values (GUIDs) for list and view.
XmlNode node = listService.GetListAndView("CustomListName", "");
string listID = node.ChildNodes[0].Attributes["Name"].Value;
string viewID = node.ChildNodes[1].Attributes["Name"].Value;

// Add configuration entry - Construct a Batch element and its attributes.
XmlDocument doc = new XmlDocument();
XmlElement batch = doc.CreateElement("Batch");

batch.SetAttribute("OnError", "Continue");
batch.SetAttribute("ListVersion", "1");
batch.SetAttribute("ViewName", viewID);

// Specify update method for the batch post using CAML,
batch.InnerXml = "<Method ID='1' Cmd='New'>" +
"<Field Name='Title'>" + listName + "</Field>" +
"<Field Name='ProposalStatus'>Open</Field></Method>";

// Update SP list item using the list GUID.
try
{
XmlNode xnodeList = listService.UpdateListItems(listID, batch);
}
catch (Exception) {}
}

In addition, Opportunity status maintained in the custom SharePoint list can be updated when the opportunity is marked as a closed opportunity. For that purpose same plugin can be extended to execute on Win or Lose messages

(Ex: "context.MessageName.Equals("Lose")")

by registering plugin for the "Win" and "Lose" steps. In order to capture revert updates (Reopen opportunity), plugin should be registered for the "SetStateDynamicEntity" step as well.

Wednesday, November 24, 2010

CRM 4 Plugins – Read Entity Attributes within the Plugin


CRM 4.0 Plugins are a way to integrate custom business logic into CRM 4.0's platform. Here are the high-level steps to follow in plugin creation and registration. For the plugin registration, Plugin Registration Tool for CRM 4.0 delivered with CRM 4.0 SDK is used.

Implement Plugin

  1. Visual Studio class library project can be used to implement plugins
  2. Inherit the class from IPlugin class
  3. Implement "Execute" method taking IPluginExecutionContext type context as a parameter as follows

    public void Execute(IPluginExecutionContext context)

  4. Here is a sample "Execute" method implemented for Opportunity entity

public void Execute(IPluginExecutionContext context)
{
string m_OpportunityName = string.Empty;
string m_OpportunityId = string.Empty;
if (context.MessageName.Equals("Create"))
{
// Entity creation.
DynamicEntity postEntity;
if (context.PostEntityImages.Contains("Target"))
{
postEntity = ((DynamicEntity)context.PostEntityImages["Target"]);
if (postEntity.Properties.Contains("opportunityid"))
{
Key opportunityId = (Key)postEntity.Properties["opportunityid"];
if (opportunityId != null)
{
m_OpportunityId = opportunityId.Value.ToString();
}
}
if (postEntity.Properties.Contains("name"))
{
m_OpportunityName = postEntity.Properties["name"].ToString();
}
}
}
else if (context.MessageName.Equals("Update"))
{
// Entity update.
DynamicEntity pretEntity;
DynamicEntity postEntity;
if (context.PreEntityImages.Contains("Target"))
{
pretEntity = ((DynamicEntity)context.PreEntityImages["Target"]);
}
if (context.PostEntityImages.Contains("Target"))
{
postEntity = ((DynamicEntity)context.PostEntityImages["Target"]);
}
if (postEntity.Properties.Contains("opportunityid"))
{
Key opportunityId = (Key)postEntity.Properties["opportunityid"];
if (opportunityId != null)
{
m_OpportunityId = opportunityId.Value.ToString();
}
}
if (postEntity.Properties.Contains("name"))
{
m_OpportunityName = postEntity.Properties["name"].ToString();
}
}
}

Register Plugin

Copy plugin dll(s) to "……\Program Files\Microsoft Dynamics CRM\Server\bin\assembly"

Run "Plugin Registration Tool" come with the SDK to register the plugin for an entity.

Then create a step for "Create" and another step for "Update". "Create Step" executes the plugin in new entity instance creation, while "Update Step" executes the plugin in entity update.

Then create a "Post Image" for "Create Step" and "Pre Image" and a "Post Image" for "Update step".

That is it. If the Steps are created as "Synchronous", plugin can be debugged by attaching to "w3wp.exe" process. If the Steps are created as "Asynchronous", plugin can be debugged by attaching to "CrmAsyncService.exe" process.

One thing that should be noted is, Active Directory account used to deploy and register new assembly has to be added into "Deployment Administrators" group in CRM Deployment Manager. Having assigned only "System Administrator" role is not sufficient for this task.

Wednesday, November 3, 2010

How to Get Row Count of All Tables - SQL Server

For this purpose doing a rowcount for each table will take forever, if the DB is fairly large. In both of following ways we don't count rows of each table, instead just look in SQL Server where it is already stored.

Query 1:
SELECT
[TableName] = O.name,
[RowCount] = MAX(I.rows)
FROM
sysobjects O,
sysindexes I
WHERE
O.xtype = 'U' AND I.id = OBJECT_ID(O.name)
GROUP BY O.name
ORDER BY [TableName] ASC


Query 2:
SELECT
'['+SCHEMA_NAME(T.[schema_id])+'].['+T.name+']' AS [Table_Name]
,I.rows AS Row_Count
FROM sys.tables T
INNER JOIN sysindexes I ON (T.object_id = I.id AND I.indid < 2
ORDER BY [Table_Name]


It should be noted that both of above queries give an approximate count only, because of sysindexes table is usually a little bit inaccurate, due to it is not updated constantly.

Friday, September 24, 2010

How to get a DLL Already Deployed in GAC

This is a simple task as mapping the Windows assembly folder as a network drive and opening respective folder with equivalent dll name. But since I’m always forgetting it, finally thought of blogging ...

First we need to add our assembly folder as a Network Drive by Tools -> Map Network Drive or by My Computer -> Right Click -> Map Network Drive


Specify the path to assembly folder as follows.


You will get a Network Location mapped. Go inside…



Open GAC_MSIL folder.

You will find a folder for each .dll deployed into the Global Assembly Cache with the same name as dll.


Monday, September 13, 2010

Running Dynamics CRM 4.0 with a Service Account

When I install CRM 4.0, I got the warning message "Verify Domain User account SPN for the Microsoft Dynamics CRM ASP.NET Application Pool account" in system requirements window. But it did not prevent me from installing CRM, so I proceeded.



After successfully completing other steps I couldn't browse CRM page. I was getting an error message saying "Caller does not have enough privilege to set the CallerOriginToken to the specified value.” To resolve this problem I had to follow following steps.

1. Using Setspn command line tool, setup the SPNs for the machine and service account as follows
.
setspn –a HOST/servername.domainabc:5555 domainabc\serviceusername


The Setspn command line tool is included in Windows Server 2003 Support Tools and you can download it from the Microsoft Download Center in this location.
You can find "Setspn Overview" from Microsoft TechNet in this location.

2. Verify/ Add the Microsoft Dynamics CRM server to the CRM installation’s Active Directory PrivUserGroup group by following these steps.

2.1 Locate the correct security group if you have more than one deployment of Microsoft Dynamics CRM 4.0 in the domain. Run the following SELECT statement on MSCRM_Config database using SQL Server Management Studio.

SELECT ID, DatabaseName, FriendlyName FROM Organization

2.2 Add the Microsoft Dynamics CRM server to the Active Directory PrivUserGroup group.
...1. Start -> Run -> dsa.msc, then click OK.

...2. Locate the organizational unit in which the Microsoft Dynamics CRM installed.

...3. Double click PrivUserGroup, click Members, and then click Add.

...4. Click Object Types, click Computers, and then click OK.

...5. Use Check Names option to find out Microsoft Dynamics CRM server, and then click OK.

3. Add the service account to the local machines IIS_WPG group.

4. Restart Microsoft Dynamics CRM server.
5. Restart Microsoft Dynamics CRM client for Outlook.

Tuesday, June 15, 2010

Issue in Embedding a Flash Image Rollup – SharePoint 2010

My goal is to embed a Flash control in a SharePoint site that displays several images iteratively. So one approach is, upload the .SWF file and the images to a web location, preferably a library in the same SharePoint site. Then can develop a custom WebPart control or use content editor WebPart to include following code segment.

<div>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/
cabs/flash/swflash.cab#version=9,0,28,0"

width="310" height="160">
<param name="movie" value="/FlashImages/ControlFiles/abc.swf" />
<param name="quality" value="high" />
<param name="flashvars" value="configXML=/FlashImages/ControlFiles/data.xml"/>
<embed src="/FlashImages/ControlFiles/abc.swf"
flashvars="configXML=/FlashImages/ControlFiles/data.xml"
width="310" height="160" quality="high"
pluginspage="http://www.adobe.com/shockwave/
download/download.cgi?P1_Prod_Version=ShockwaveFlash"

type="application/x-shockwave-flash">
</embed>
</object>
</div>

In here data.xml contains the URLs of the images to be displayed, which is taken as a parameter by the swf. Basically that is it. Flash control should work properly. But we need one more additional step in order to flash control to work as expected. That is we have to set “Browser File Handling” to
“Permissive”. By default this option is set to “Strict”, which prevents flash control performing as expected.

So in SharePoint Central Administration;

  • Go to “Application Management” > “Manage web applications”
  • Select your Web Application and go to “General Settings” > “General Settings”


  • Select “Permissive” option in the Browser File Handling section as following screen shot.




Sunday, May 2, 2010

Architecture Modeling in VS 2010

Microsoft Visual Studio has now become a good Integrated Requirements, and Architecture and Modeling tool by embracing Unified Modeling Language (UML) into VS 2010, thereby making architecture, design, development, and testing seamlessly possible with its IDE. Visual Studio 2010 has support for following UML 2.1.2 diagramming types;
  • Class diagram
  • Sequence diagram
  • Use Case diagram
  • Activity diagram
  • Component diagram
The "Modeling Project" type is used for the architecture modeling. Here Activity and Use case diagrams can be used for requirements modeling. Then Layer diagrams can be used to represent the layered view of the system. If the system is component oriented, subsystems or components are represented using component diagram. Note: Code generation capability is added to the sequence and class diagrams only.

Thursday, April 15, 2010

Visual Studio 2010

The official Visual Studio 2010 version is now released and trial versions can be downloaded from the Microsoft site this location.
Here is a feature comparison of different VS 2010 products.

Feature Description when you install VS 2010 Ultimate:
Installs the Visual Studio 2010 Ultimate integrated environment together with modeling, development, testing, and deployment components that can simplify the entire development process and help ensure high-quality solutions. Provides tools for building solutions on Windows, the Web, Azure, the Office system, SharePoint, SQL Server, and other platforms by using Visual Basic, Visual C#, Visual C++, or Visual F#.

Includes these advanced features: Test Impact Analysis, Coded UI testing, Test Case Management, Database Development, Profiling Tools, Code Analysis Tools, IntelliTrace, Code Coverage, Modeling and Architecture Diagram Tools and Team Explorer. Also includes SQL Server Express, the Windows SDK, Sync Framework, a graphics library, and more.

Sunday, February 21, 2010

Office Live Workspace

Office Live Workspace (Workspace.OfficeLive.com) is an online extension to Microsoft Office and any of Microsoft Office documents (Word, Excel, and PowerPoint) can be added and accessed through the Workspace. It is a free service for storing and sharing documents online.

Office Live Workspace requires web access and a compatible browser. In order to access workspace directly from MS Office; users of Word, Excel and PowerPoint must install an Office Live Update. Then documents can be ‘checked out’, ‘checked in’ and edit in MS Office.

With Office Live Workspace users can save up to 5GB of files and can access them even if Office isn't installed. Also users can share a workspace or a single document, collaborate online as a group, all you need for that is their email address. Workspaces are password protected and owner can control who views and edits information.