Friday 20 September 2013

Directory Services Restore Mode NightMare

In short, I got a development virtual machine I run on my PC using virtual box, it's a stand alone SharePoint 2007 box along with SQL Server 2005 and K2 server 2003.

I was testing something which actually hanged my VPC machine and as a result I had to power off it which is veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy veryyyyyyyyyyyyyyyyyyyyyyyyyyy bad practice, but since I am using it for development and got out dated backup copies so wasn't a big issue for me. Anyway when i restarted it gave me this error,



I almost tried 100's of articles on Internet and couple of hours to fix the issue but couldn't but at-last I fixed it by following this MSDN article,

How To Use Ntdsutil to Manage Active Directory Files from the Command Line in Windows Server 2003

You also might find it useful to read this information about active directory files which helped me fix the issue and understand what happened but I won't going to explain it in here,

Active Directory files and their functions

and this link also helped even though it's for Exchange edb but applies to active directory edb recovery as well,

How to Solve Exchange Dirty Shutdown Error



To be continued if I get time I will explain a bit more or just add comment if you need help...


Friday 13 September 2013

Getting SPUser using Login Name


Today I came across a situation where I had to let user input user name(s) and add them to people picker field within a list, unfortunately it has to be done using web services as user is interacting with SharePoint throw iPad.

Problem ?

User can input anything (any string) so I need to validate if user name(s) exists or not,  if user with login name provided has access to site collection page and at end if all previous are true then get SPUser object and add it to people picker field of custom list.

Solution

Assuming you already got or know how to get reference to SPSite, SPWeb, SPList and SPListItem objects

SPUser user = null;

if (!string.IsNullOrEmpty(userLogin))
{
       try
      {
           // statement below will throw exception if user doesn't exists
           if (site.RootWeb.DoesUserHavePermissions(userLogin, SPBasePermissions.Open))
          {
                try
               {
                    user = site.RootWeb.SiteUsers[userLogin];
                }
               catch (Exception ex)
              {
                   string other = string.Format("EXCEPTION! UserName '{0}' provided doesn't has permissions to root web", userLogin);
                   throw new SoapException(other, SoapException.ServerFaultCode, ex);
              }
           }
       }
       catch (Exception ex)
       {
            string other = string.Format("EXCEPTION! UserName '{0}' provided doesn't exists", userLogin);
            throw new SoapException(other, SoapException.ServerFaultCode, ex);
       }

       SPFieldUserValueCollection uvCollection = new SPFieldUserValueCollection();
       SPFieldUserValue uValue = new SPFieldUserValue(web, user.ID, user.LoginName);
       uvCollection.Add(uValue);

//Now update item just by item["People PIcker Field Name"] = uvCollection;
}

That's it, above code worked for me so it should work for you.

Lesson learned

I tried to use RootWeb.Users[userLogin] but it didn't worked for me as it does not return users who have access through a group whereas I had some user's given access to root web through groups only.

RootWeb.AllUsers[userLogin] is a dangerous function as  it returns list of all users who ever had access to web (even ones who don't have access to web anymore now).

web.EnsureUser() method creates a user if not exists already which I couldn't use in this scenario as client input is being used to validate SPUser object.

Monday 9 September 2013

Stored Procedure to query data using list parameter

Working on this task required me to create a stored procedure which will get a list of strings, do some processing on them and then return some data. Since SQL Server 2000 don't support arrays object so I had to find a different approach. After a bit of googling i figured out I can send list by building a string out of it and then in SQL Server use function "IN" to do processing as required.

Here'e the Stored Procedure,

USE [DataBaseName]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO


Create PROCEDURE [dbo].[StoredProcedureName]
(
@listParameter varchar(8000)   -- this is because SQL Server don't support "Max" --
)
AS

exec('SELECT  *
FROM TableName
WHERE ColumnaName IN (' +@PasswordPolicyGroups+ ')   -- checks for list of values --
and CoumnaNameB= 1')

Now I am providing list as

List<String> myListData = GetListData(); // or your logic
string tempListData = string.Empty;

foreach(string data in myListData)
     data += "'" + myListData + "',";

// now just pass data as your parameter and call stored procedure


Tuesday 3 September 2013

How to check if a SPWeb exists within a SPSite


SharePoint objects get bit tricky when it comes to check if an objects exists or not specially in SharePoint 2007. Today I came across a situation where I had to check if a web exists or not, so I tried using this code,


using (SPSite site = new SPSite("https://myPortal.myCompany.com/Sites/SubSites/DoesnotExistsSite"))
using (SPWeb web = site.OpenWeb())
{
         if(web.Exists)
                //Do this
}


Above code complied perfectly and didn't threw any error at all, however it didn't worked as I was expecting an error e.g. "Web doesn't exists". So I did realized you give any address to SPSite() method it will try to get site collection object regardless of full URL provided exists or not, okay it's something that might does makes sense, but when it tries to open web using OpenWeb() method, it should open the web URL has been provided to, for example this URL,

https://myPortal.myCompany.com/Sites/SubSites/DoesnotExistsSite

It will opens SubSites web, and if URL is 

https://myPortal.myCompany.com/Sites/SubSites/DoesnotExistsSite/alksjdalskdjaslkdjaskldjasldjasld

It still opens up SubSites as SPWeb object.

I was expecting it to throw an error however it didn't so I got "true" for web.exists.

I did some research and figured out another way to find out if SPWeb actually exists for supplied URL or not,


public static void DoesWebExists()
{
       string tempUrl = "https://myPortal.myCompany.com/Sites/SubSites/DoesnotExistsSite/alksjda";
       Uri u = new Uri(tempUrl);

       using (SPSite site = new SPSite(tempUrl))
       using (SPWeb web = site.OpenWeb(u.AbsolutePath.ToString(), true))
       {
              Console.WriteLine(web.Exists);
       }
}


Above example has been tested and works fine as far as SPSite object exists for provided Url.

How to check Assembly related errors using Fusion Log utility


One of most annoying exceptions we developer get is assembly found doesn't match with loaded assembly, even though we put right assembly in GAC. There can be tens of reason as why assembly loaded doesn't match and that's where fusion log utility helps.

In order to use this utility you need to have installed Microsoft SDKs or it gets installed when you installed visual studio. I have installed Visual Studio 2008 on Windows Server 2003 so my guide will be based on it.

Open Visual Studio 2008 command prompt and navigate to directory

C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin>

Now type fuslogvw.exe and you will see a windows appears as shown below,




Click on settings and check box Log all binds to disk as shown, you can also select "Custom log path" but make sure directory exists.

If your log was disabled before then you might need to do a IIS reset to make it start working. (it took mine around 30 minutes to pick up errors because of unknown reasons).

You will see logs as shown in picture below,


And clicking on any log will open it in browser as it's a html file, as shown below.



Make sure to disable log afterwords as otherwise they will take a lot of space within short time.

How to return Primary Key of just Inserted record into SQL Datatable using Strongly Typed DataSet

Inserting data directly into SQL data table isn't recommended because of security reasons, which is why most of us use Strongly Typed data-sets, it provides our solution with a secured layer to interact with database, enhancing security.

Saying that strongly data-sets are better way of interacting with database but they can sometime become hectic and consume time while debugging them specially when you use TableAdapter Query Configuration Wizard as it doesn't always configures your data-sets the way they should be.

Back to problem SCOPE_IDENTITY() not returning right value, as we use it to return the primary key generated by table when we try to insert some records into the specified table. Most of us simply use return SCOPE_IDENTITY() as it works sometimes and on some servers but it doesn't work other times (and that's when debugging takes ages).

Best way I recommend of getting back primary key of inserted record is by using OUTPUT Parameter in your stored procedure as it will make sure your strongly typed data-set get's the right value.

Here's an example of how your Stored Procedure for inserting a record should look like,

USE [DataBaseName]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE procedure [dbo].[StoredProcedureName]
(
          @ColumnA int,
          @ColumnB int,
          @NewID int
 )
AS

INSERT Into tableName
Values
(
          @ColumnA,
          @ColumnB
 )
SELECT @NewID = SCOPE_IDENTITY()

In this article I will assume you already know how to create a data-set or have it ready, but if you don't know then you can find instructions at How to Create a DataSet ?.

Once your data-set is ready just drag and drop table or stored procedure into designer class from Server Explorer. Now if you drag and drop table which I would recommended you  can add a query and select your stored procedure using TableAdapter Query Configuration Wizard. Thing that you need to take care of is to select

"A Single Value - A typed function will be...."

Then name your stored procedure as you desire and finish the wizard. Your method will look something like this,

public int InsertDataMethod(solution.myRow details)
{
      int? output = 0;
      TableName_TableAdapter tableAdapter = new TableName_TableAdapter();
      using (tableAdapter.Connection = new SqlConnection(ConnectionString))
      {
         tableAdapter.StoredProcedureDataSet(details.ColumnA, details.ColumnB, ref output);
       }
       return Convert.ToInt32(output);
}


Make sure dataset's ExecuteMode property is Scalar otherwise it might doesn't return right ID.