in

Dot Net Mafia

Group site for Tulsa area .NETdevelopers, with blogs dealing with (usually) .NET, SharePoint, and other Microsoft products, as well as some discussion of general programming related concepts.

Corey's .NET Tip of the Day

Bringing you the latest time saving tips for SharePoint, MOSS 2007, ASP.NET, LINQ, and Visual Studio 2008
  • More on Manfiest.xml

    There are a ton of tools out there to create SharePoint solution packages (.wsp files) for you, but it is still a good idea to know what those tools are creating for you exactly.  Sometimes, things don't always work right, and you may need to customize or tweak your manifest file.  If you are new to SharePoint development, the Manifest.xml file is used during the deployment of a .wsp solution package.  It tells SharePoint what files to copy, what features to install, where to put your binaries, adds SafeControl entries, as well as set code access security.  The file seems pretty straight forward but there are a lot of ways to mess it up.  As I pointed out in the past, do not put any comments in this file.  It will cause all kinds of FAIL. 

    I'll start by go over the key elements of this file and point out some things you need to know about each one.  If you create any of these main elements, note, that they will always require some kind of child element, otherwise the file won't validate.  The first element we will talk about is FeatureManifests.  This name as it sounds is used to deploy features.  Your solution will install features but not activate them for you.  You can list multiple features to install here using the FeatureManifest element.  It's Location attribute specifies the path to the feature (inside the DDF file).  What that means is the path specified to your feature.xml file will match whatever you have listed as the destination in your DDF file.  We can talk about DDF files again another day if necessary.

    <FeatureManifests>

        <FeatureManifest Location="MyFeature\feature.xml" />

    </FeatureManifests>

    The TemplateFiles element is used to copy out any additional files to the SharePoint server.  These could be .webpart files, user controls, other XML files, application pages, master pages, etc.  SharePoint will not copy any file that is not listed here (except for binaries and feature.xml and related files).  Again, you can specify multiple TemplateFile child elements to copy multiple files.  Part of what makes this file tricky is the paths.  Some are relative, some require you to specify something inside the 12 hive.  It takes some getting used to.  In this case, if you are copying additional files into a feature folder for a web part, you would specify something like FEATURES\MyFeature\mywebpart.webpart.

    <TemplateFiles>

      <TemplateFile Location="FEATURES\MyFeature\MyWebpart.webpart" />

    </TemplateFiles>

    The Assemblies element specifies which binaries will be deployed to SharePoint and where.  You can copy multiple assemblies out at a time with a feature by specifying more than one Assembly element.  The Assembly element has a Location attribute which typically just contains the DLL name (again dependent on where the DLL is copied in your DDF file).  It also contains a DepoymentTarget attribute which specifies wether to copy it to the GAC (GlobalAssemblyCache) or the bin folder (WebApplication).  It also contains a SafeControls element with the same syntax of what is used in the web.config. 

    <Assemblies>

      <Assembly DeploymentTarget="WebApplication" Location="MySolution.dll">

        <SafeControls>

          <SafeControl Assembly="MySolution, Version=1.0.0.0, Culture=neutral" Namespace="MySolution.WebParts" TypeName="*" Safe="True" />

        </SafeControls>

      </Assembly>

    </Assemblies>

    One thing to note here is that if you do not specify at least one assembly with a SafeControl element, SharePoint will decide that your solution does not contain a Web Application Resource and therefore will not deploy it when you specify a URL with stsadm.  So, I typically include this line even if I don't have any controls or web parts in my solution.  You will get an error like the one below.

    This solution contains no resources scoped for a Web application and cannot be deployed to a particular Web application.

    The last element, I am will talk about is CodeAccessSecurity.  This of course is the most complex one.  I have posted on this topic in the past and don't forget, I will be speaking about it next month.  The post I have on it covers it in more detail that I will cover today.  The one thing, I will remind you of is that it is ok to specify multiple assemblies. 

    I haven't talked about the root element yet. It's pretty simple.  Just remember the SolutionId needs to be unique.  The ResetWebServer attribute can be set to false if you don't want it to reset IIS on deployment.

    <Solution SolutionId="{B3270C0DF-EACA-4320-9B2A-C293435C5DF1}"  xmlns="http://schemas.microsoft.com/sharepoint/" ResetWebServer="true">

    Anyhow, hopefully this will help someone out the next time they are looking at a manfiest.xml file.

  • Speaking at Tulsa Tech Fest again

    Tulsa Tech Fest was a lot of fun last year and I am excited to be speaking at it again this year.  This year, I will be giving a talk on Implementing Partial Trust in SharePoint.  It should be a good talk in explaining how to create SharePoint solutions that can deploy Code Access Security settings.

    It also looks like the DotNetMafia might be involved in putting together an unofficial "social event".  More details on that to come.

  • Using a namespace with LINQ to XML

    If you are using LINQ to XML, sooner or later, you will need to work with XML documents that don't use the namespace.  For example, you might be working with the XML from an InfoPath form and you want to get the value of a particular element.  InfoPath uses the my namespace, and you can't just specify it directly in the string (i.e.: Root.Elements("my:Group2") will not work).  To resolve this, we create a XNamespace object and pass it the URI to the namespace.

    XNamespace myNamespace = "http://schemas.microsoft.com/office/infopath/2003/myXSD/2008-02-15T09:18:32";

    Once you declare the XNamespace, it's just a matter of including it before any elements or attributes you might use.

    var transmittalCollection = from transmittalNode in formDocument.Root.Descendants(myNamespace + "group2")

                                select new Transmittal

                                {

                                    MyNode = transmittalNode.Elements(myNamespace + "MyNode").Any() ? transmittalNode.Element(myNamespace + "MyNode").Value : null,

                                };

    That's all there is to it.  I have found it very useful when I want to query data out of an InfoPath form.  I just submit the data to a service and then I use LINQ to XML to parse out the values I need.  It is much easier for me than using XPath.

  • How to: Create a Group in SharePoint

    Today, I am continuing in my How to series of posts.  According to the stats, they have been quite popular, so I will keep them coming.  We are going to talk about how to create a SharePoint security group.  A group (represented by SPGroup) is an internal collection of users (or Active Directory) groups that you can then assign a permission level to on a given site.  You can do this through the web interface but in the case where you are programmatically provisioning sites, you will need code to set who is allowed to use it.

    Creating a group is pretty simple, but before you start you need to make sure that the group doesn't already exist.  Of course there is no exists method in any of the SharePoint collections, so you may want to use the try/catch technique.  Once you have established that the group does not exists, you need to decide who is going to be the owner of the group.  The owner of the group must be a member of the site you are using before you try to create a group.  Luckily the EnsureUser method makes adding this user easy.  Assume below that currentSite is an SPWeb object.  This could be for a specific subsite or perhaps RootWeb on your site collection.

    currentSite.EnsureUser(@"DOMAIN\OWNER");

    The EnsureUser method adds the user to the site if it doesn't exist.  If it does exist, it just returns without error.  We use the SiteGroups collection on the SPWeb object to add the group to the site. 

    currentSite.SiteGroups.Add("My Custom Group", currentSite.SiteUsers[@"DOMAIN\OWNER"],

        currentSite.SiteUsers[@"DOMAIN\OWNER"], "My Custom Group Description");

    The Add method takes four parameters.  The first parameter is the group name.  The second parameter is the owner of the group.  Note that it requires an SPMember object (SPUser inherits from SPMember) not a string with an Active Directory account.  This is why we have to execute the EnsureUser method above.  The third parameter is a default user.  Honestly, I have no idea what this is for.  It's not something you have to specify when creating a group using the UI, so I usually just pass the owner.  The last parameter is a description.

    The next thing we want to do is add users to the group.  To do that, we just get a reference to our newly created group and call the AddUser method.  We would call this multiple times if we were adding more than one user.

    currentSite.SiteGroups["My Custom Group"].AddUser(@"DOMAIN\USER1", string.Empty, "User 1", string.Empty);

    The first parameter is the login name.  The second is an E-mail address.  The third is the user's actual name and the last parameter is for notes.  If I don't know all of the values for the parameters, I just pass an empty string (I believe null causes an exception).

    The last thing we need to do is set the permissions on the group (i.e.: does this group have Read or Contribute access).  The way the API does it may not seem straight forward at first.  We have to create a SPRoleAssignment object by passing it our new group.  We then add a role definition binding to that SPRoleAssignment object.  We then add that SPRoleAssignemnt object to the RoleAssignments collection on the SPWeb object.

    SPRoleAssignment roleAssignment = new SPRoleAssignment(currentSite.SiteGroups["My Custom Group"]);

    roleAssignment.RoleDefinitionBindings.Add(currentSite.RoleDefinitions["Contribute"]);

    currentSite.RoleAssignments.Add(roleAssignment);

    currentSite.Update();

    In this case, we are setting Contribute access for this group.  You could also specify a custom permission level here as well.  Note, the last statement we must execute is the Update method on the SPWeb object.  Don't forget this otherwise your changes would be saved. 

  • SharePoint to API Translation Guide

    As anyone that has worked with SharePoint knows, there is next to zero correlation to what something is called in the SharePoint UI with what it is called in the SharePoint API.  A lot of this is due to the fact that they changed the name for most everything between versions 2 and 3.  This will be an ongoing post and I will add to it as I think of things. 

    SharePoint UI Term SharePoint API Class
    Site Collection SPSite
    Site SPWeb
    Business Data Catalog ApplicationRegistry
    Site Column SPField
    Permission Level SPRoleDefinition
    Alternate Access Path SPAlternateUrl
    Top Level Site RootWeb

    Think of something I am missing?  Leave a comment so that I can get it added.

  • The difference between .dwp and .webpart

    I have been doing some quite a bit of mentoring to new SharePoint developers lately and was asked the question of what the difference was between a .dwp and .webpart file and which one should be used.  Yes, I know this is an intro topic but if you are new to SharePoint you aren't going to know this.  As you may know, they are both files used for describing where the code is for a web part.  The difference is .dwp was the file extension used in version 2 of SharePoint and .webpart is a new extension used in version 3.  Inside the files, the schemas are different and it is indicated as such by the version number on the xmlns attribute.

    Here is an example of a version 2 schema.

    <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">

      <Assembly>Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>

      <TypeName>Microsoft.SharePoint.Portal.WebControls.SearchBoxEx</TypeName>

      <Title>Search Box</Title>

      <Description>Used to search document and items.</Description>

      <FrameType>None</FrameType>

      <AllowMinimize>true</AllowMinimize>

      <AllowRemove>true</AllowRemove>

      <IsVisible>true</IsVisible>

      <Width>335px</Width>

      <GoImageUrl xmlns="urn:schemas-microsoft-com:SearchBoxEx">/_layouts/images/gosearch.gif</GoImageUrl>

      <GoImageUrlRTL  xmlns="urn:schemas-microsoft-com:SearchBoxEx">/_layouts/images/goRTL.gif</GoImageUrlRTL>

      <GoImageActiveUrl xmlns="urn:schemas-microsoft-com:SearchBoxEx">/_layouts/images/gosearch.gif</GoImageActiveUrl>

      <GoImageActiveUrlRTL  xmlns="urn:schemas-microsoft-com:SearchBoxEx">/_layouts/images/goRTL.gif</GoImageActiveUrlRTL>

    </WebPart>

    Version 3 is a little different.

    <webParts>

      <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">

        <metaData>

          <type name="Microsoft.SharePoint.Portal.WebControls.BusinessDataListWebPart, Microsoft.SharePoint.Portal,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" />

          <importErrorMessage>Cannot import this web part.</importErrorMessage>

        </metaData>

        <data>

          <properties>

            <property name="Title" type="string">Business Data List</property>

            <property name="Description" type="string">Display a list of items from a data source in the Business Data Catalog.</property>

            <property name="CatalogIconImageUrl" type="string">/_layouts/images/bizdatawebpart.gif</property>

            <property name="CacheXslStorage" type="bool">true</property>

            <property name="CacheXslTimeOut" type="int">600</property>

          </properties>

        </data>

      </webPart>

    </webParts>

    The main difference is that all properties passed to the web part are specified with a property element and a name attribute in version 3.  Version 2 uses different element names for everything. 

    Which file extension should you be using?  Probably .webpart since it is version 3.  However, there is really nothing wrong with using version 2 although maybe it will be removed from a future version of SharePoint.  If you take a look at the web part gallery, you will see plenty of web parts still using .dwp.  So for now, I recommend version .webpart, but if you are more familiar with the syntax of version .dwp, have at it.

  • Faceted Search 2.5 is much improved

    I was pleased to see that a new version of the Faceted Search web parts had come out recently.  On my last couple of projects, the Faceted Search worked, but it didn't quite meat all the clients needs.  Mainly it didn't work with fixed queries, advanced search or support the keyword OR syntax.   So I ended up writing my own faceted search controls.  They weren't as functional and were coded to meet the needs of specific needs of my clients.  They weren't as nice as Faceted Search, but they did got the job done.  So when a new version came out, I had to try it out as soon as possible.

    If you are going to try them out, the first thing I recommend is to read the setup guide.  I didn't and just assumed the install process was the same as previous versions, and it was complete FAIL.  I went back and read the guide and that got me going somewhat.  There are a couple of things you need to remember to do.  First, remove the old version.  Deactivate the feature, retract the feature, and delete the solution.  Another thing you need to do is manually delete the web parts from the web part gallery.  The reason for this is that when you try to activate the new feature, you will get a file already exists error.  I wish more people would put code in their feature receivers to do that.  It's a pretty simple process and I should probably write something on it. 

    The last thing you need to do is to be sure and download the version of Enterprise Library that is provided on the Faceted Search site.  The reason for this is that the faceted search controls require partial trust and the Enterprise Library has to be recompiled to allow partially trusted callers.  Hopefully, Microsoft will put this attribute in the library next time so that, it doesn't have to be added manually.  You will need to throw these DLLs in the GAC.  Don't assume that the existing version of Enterprise Library will work because the code looks for a version with a specific public key token.

    After getting everything installed, I was pleased to see that I could now pass multiple values for the same managed property (i.e.: Color:"Red" Color:"Blue").  It also now has support for advanced search.  It has all sorts of other great new features that I haven't messed with yet.  On my setup, I did not have the caching database working correctly at first, so it puts the text Not Cached at the header of the facets control.  I really don't like that since the user doesn't really have any need for that information.  Not a deal breaker since I can just resolve the issue by getting my caching database setup correctly.  I would just modify the source code for the web part and recompile, but unfortunately it's not available yet.  Anyhow, the Faceted Search web parts are really nice now and from my experience users really like them.  If you haven't used them before, I encourage you to go try it.

  • To Web Part or not to Web Part?

    I am putting today's post out there in a hope of getting more of a feel for what the community does in regards to web parts.  The question is when you need to write custom functionality on a SharePoint site (i.e.: a form, some fancy data binding code, whatever), do you build a web part or do you build a web user control and expose it through some sort of SmartPart.  It may be that there is a best practice here or it may just come down to personal preference.  Personally, I don't really care to do HTML markup using code.  That is not any fun at all and makes it difficult to do any kind of changes.  On the other hand if you want to make use of custom properties that the end user can customize, building a web part is the way to go.  So what do you prefer?  How do you choose what you build?  Leave a comment and let us know.

  • How to search your site columns with Enterprise Search

    I decided to go with an intro topic for Enterprise Search today.  If you are like me, you often find yourself in a situation where you are doing ECM related activities such as creating custom content types that use site columns.  This may present itself in the form of a custom list, form library, or a document library.  After you get some documents, forms, or list items in there, eventually someone is going to want to search on them.  Searching these things is not really hard at all.  After all the Local Office SharePoint Sites content source will index this stuff for you.  If you want to be able to build more advanced searches though, you are going to need to be able to search on those site columns.

    In today's example, let's assume my content type uses site columns called City, State, and Product Type.  The reason I might want to do this is because I might want to have a search such as show me all documents from the state of California.  We'll also assume that the content type I have inherits from document (so we are dealing with document libraries).  The first thing we need to do is to get Enterprise Search to learn about our site columns.  It does this by crawling, but we can't crawl without doing a little work first.  In our document library, we have to go create a document or two that actually has those site columns populated.  If there isn't any documents with that metadata populated, Enterprise Search won't know about it and it won't add those site columns as crawled properties.

    After you create your documents and crawl, you should have some new crawled properties representing your site columns.  It puts these new crawled properties under the SharePoint folder in your Metadata Property Mappings page.  Here you can see crawled properties for all of the site columns on your SharePoint farm.  The one thing to note is that it prefixes ows_ to your property and replaces any spaces with _x0020_.  This means City would become ows_City and Product Type would become ows_Produt_x0020_Type.  I really don't like the encoding on the spaces, so I just try to avoid them in my site columns.  Now we just need to create managed properties and map them to the new crawled properties.  Remember, you don't have to call the managed property the same name as the crawled property (i.e.: State would map to ows_State).

    Once you map your properties, be sure and do another Full Crawl.  This is necessary for the index to pick up your managed property mappings.  Once you do that, use your search center (or create a new one) and you can perform your queries.  For example, to search for all documents in California like mentioned above, you would use something like IsDocument:"1" State:"California".

  • How to build your own SmartPart control

    Building a SmartPart type control really isn't all that complicated.  On a project I did in the past, I came into a situation where I couldn't use the SmartPart.  I needed specific functionality and unforutnately at the time, the source code for the SmartPart was not available.  Therefore, I decided to see if I can build my own.  In its simplest form, all you are really doing is calling Page.LoadControl and passing it a path to your user control.  Of course it's a little more complicated than that but not really.  Let's start by looking at the beginning of the web part.

    [SupportsAttributeMarkup(true), XmlRoot(Namespace = "http://schemas.mydomain.com/WebPart/MySmartPart")]

    public class MySmartPart : Microsoft.SharePoint.WebPartPages.WebPart

    {

        // this provides a refernece to the control we are loading

        private Control myControl;

     

        [XmlElement("ControlPath")]

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Control Path"), WebDescription("Specify a path to a user control.")]

        [Category("Configuration")]

        [WebPartStorage(Storage.Shared), Browsable(false), DefaultValue("")]

        public string ControlPath

        {

            get;

            set;

        }

    }

    So far what we have is a new class inheriting from WebPart which has a member variable to reference the control itself as well as a property to contain the path to the control.  The ControlPath has attributes on it so that it can be set via CAML or through the UI in SharePoint.  When the user specifies a path it can be on any relative URL on the site (not just in a UserControls folder).  The next part is to actually load the control and add it to the web part.

    protected override void CreateChildControls()

    {

        base.CreateChildControls();

     

        // don't use LoadControl if you already have a reference to the control

        if (myControl == null)

        {

            myControl = Page.LoadControl(ControlPath);

            Controls.Add(myControl);

        }

    }

    We start by overriding CreateChildControls and calling the base method.  Then, we check to see if the control already exists or not.  This is so that you don't load a new instance of the control on postbacks.  Then, we just call Page.LoadControl and add it to the Controls collection.  This is really all that is required to get up and running with a web part that displays a user control.  One thing that is different is that typically in ASP.NET when you load a control with Page.LoadControl, you need to specify an Id on the control for postbacks to work properly.  However, you don't want to do this in your web part.  For whatever reason, it will lead to very inconsistent behavior with your postbacks.  There isn't any error checking or anything in the above example, so you might want to check to make sure that the path is valid, that the control was able to load, and look for exceptions.

    Also, this web part pretty much has to run with full trust.  That means you'll have to put it in the GAC or run SharePoint with full trust.  I hate running thing with full trust, but I discovered something pretty deep in the call stack of Page.LoadControl that requires full trust.  It's pretty simple, but maybe it will be of use to you if you ever need to do something more advanced when loading a user control (i.e.: passing parameters).

  • Where did my horizontal line go in my search results after I installed the Infrastructure Update?

    I have been really enjoying the new features of the Infrastructure Update, but I noticed that something didn't look quite right about my search results.  It finally occurred to me, that the horizontal line separating the description and the URL was no longer being displayed.  So, I got out the IE Developer Toolbar and determined that the style in question was srch-MetaData.  Comparing the style between an updated server and one that hadn't been updated, I discovered that there was no longer a top border defined in this element.  I knew this file was defined in core.css, but that file still had the same date and the contents had not changed.   I noticed that portal.css had a new date on it so I began looking through there and found several new styles to handle the new search features, as long with the culprit style below.

    .srch-Metadata{
        BORDER-TOP: 0px none !important;
        MARGIN:0px 0px 20px !important;
    }

    This was it.  The top border was being removed by this element.  As a quick fix, I just removed this style and my search results looked normal again.  Of course as Microsoft always reminds you, any SharePoint files you modify in the hive are subject to be changed by a future version.

  • Infrastructure Update Configuration Fails if HOSTS file is missing

    I have been in the process of updating multiple MOSS development servers with the Infrastructure Update, and ran into an issue where the SharePoint Products and Technologies Configuration Wizard failed.  The reason was that this particular server did not have a HOSTS file and it was trying to apply permissions to the file.  I am not sure why this is.  You would think it would just skip the file and move on, but apparently not.  So if you run into this issue, just go create an empty hosts file and run the Configuration Wizard again and you will be good to go.

  • How to: Query Search using the Web Service

    According to FeedBurner, my posts on querying Enterprise Search with the FullTextSqlQuery and KeywordQuery classes have been some of the most popular.  So I thought, I would continue on these posts and explain how to do it using the web service.  The SDK covers this, but not in enough detail for me.  When I am learning something new, I like to see complete examples, so hopefully this will help someone trying to learn how to do this.  The first place to start is by adding a web reference to your project.  You can query SharePoint Search or Enterprise Search in the exact same manner it is just a matter of which web service you make a reference to.  SharePoint search can be queried at a URL similar to the one below.

    http://moss-server/_vti_bin/spsearch.asmx

    To query Enterprise Search, the URL will look similar to this.  Both web services have the same methods.  They just query different indexes.

    http://moss-server/_vti_bin/search.asmx

    To execute a query, you have to build an XML document. I posted about the XML you post to the service in the past, now I will give a complete example and then also explain some of the options you can configure when querying.   The type of query is specified on the QueryText element using the Type attribute.  A value of MSSQLFT like in the XML below is used to do a full text query.

    <QueryPacket xmlns="urn:Microsoft.Search.Query" Revision="1000">

      <Query domain="QDomain">

        <SupportedFormats>

          <Format>urn:Microsoft.Search.Response.Document.Document</Format>

        </SupportedFormats>

        <Context>

          <QueryText language="en-US" type="MSSQLFT">SELECT Title, Path, Description, Write, Rank, Size FROM Scope() WHERE CONTAINS('Accounting') AND "Scope" = 'Corporate Documents'</QueryText>

        </Context>

      </Query>

    </QueryPacket>

    In the example above, I have a simple query that is looking for documents with the word Accounting in the Corporate Documents scope.  Issuing a keyword query is similar but it uses a Type of STRING.  In this example, we are searching on the managed property city with a value of Austin.

    <QueryPacket xmlns="urn:Microsoft.Search.Query" Revision="1000">

      <Query domain="QDomain">

        <SupportedFormats>

          <Format>urn:Microsoft.Search.Response.Document.Document</Format>

        </SupportedFormats>

        <Context>

          <QueryText language="en-US" type="STRING">City:"Austin"</QueryText>

        </Context>

      </Query>

    </QueryPacket>

    There are two methods used to query search.  The Query method returns the results as XML.  The QueryEx method returns the results as an ADO.NET dataset.  They both take the same input XML document like the ones above.  The code is pretty simple. Here I create a new reference to the web service (I named it QueryWebService) and I pass it credentials.  Like any SharePoint web service, you have to pass it credentials.  You can specify custom ones or just use the credentials of the application you are running using DefaultCredentials.

    QueryWebService.QueryService queryService = new QueryWebService.QueryService();

    queryService.Credentials = System.Net.CredentialCache.DefaultCredentials;

    For this example, I am just going to build the input XML using a StringBuilder. 

    queryXml.Append("<QueryPacket xmlns=\"urn:Microsoft.Search.Query\" Revision=\"1000\">");

    queryXml.Append("<Query domain=\"QDomain\">");

    queryXml.Append("<SupportedFormats>");

    queryXml.Append("<Format>");

    queryXml.Append("urn:Microsoft.Search.Response.Document.Document");

    queryXml.Append("</Format>");

    queryXml.Append("</SupportedFormats>");

    queryXml.Append("<Range>");

    queryXml.Append("<Count>50</Count>");

    queryXml.Append("</Range>");

    queryXml.Append("<Context>");

    queryXml.Append("<QueryText language=\"en-US\" type=\"STRING\">");

    queryXml.Append("City:\"Austin\"");

    queryXml.Append("</QueryText>");

    queryXml.Append("</Context>");

    queryXml.Append("</Query>");

    queryXml.Append("</QueryPacket>");

    This query is similar to the one from above, but I added a Range and Count element.  By default, you will only get 10 results back, by changing this element I will get back 50 in this case.  You can also specify a StartAt element to start on a specific row.  This would be useful if you are building paging into something.  Then I just execute the query like so.

    string resultXml = queryService.Query(queryXml.ToString());

    This returns an XML document that looks similar to the following.

    <ResponsePacket xmlns="urn:Microsoft.Search.Response">

      <Response domain="QDomain">

        <Range>

          <StartAt>1</StartAt>

          <Count>10</Count>

          <TotalAvailable>7312</TotalAvailable>

          <Results>

            <Document relevance="1000" xmlns="urn:Microsoft.Search.Response.Document">

              <Title>Document 1</Title>

              <Action>

                <LinkUrl size="0">http://moss-server/Documents/Document1.docx</LinkUrl>

              </Action>

              <Description />

              <Date>2008-07-15T19:59:43.2511787-05:00</Date>

            </Document>

            <Document relevance="1000" xmlns="urn:Microsoft.Search.Response.Document">

              <Title>Document 2</Title>

              <Action>

                <LinkUrl size="0">http://moss-server/Documents/Document2.docx</LinkUrl>

              </Action>

              <Description />

              <Date>2008-07-15T19:59:43.2511787-05:00</Date>

            </Document>

          </Results>

        </Range>

        <Status>SUCCESS</Status>

      </Response>

    </ResponsePacket>

    From here, you have something that you can easily work with using LINQ to XML.  If you are feeling lazy, you can always just use QueryEx to get a dataset as well.  You can configure quite a few options in how things are searched using the input XML document.  The schema is in the SDK if you are interested.  Hopefully, this will help the next time you need to query search.

  • What's new with Enterprise Search with the Infrastructure Update

    Yesterday, the Infrastructure Update for Office SharePoint Server (as well as WSS and Project Server) was released.  This update makes changes to content publishing as well as Enterprise Search.   The install went smoothly.  Just remember like the service pack, you need to install the WSS update first. There are two notable changes as far as search goes.  First is the addition of Federated Search which comes over from the Search Server line of products.  The second is a new set of administration pages for setting up search.   

    If you are not familiar with Federated Search, it basically gives you the ability to have your search results page query additional content indexes that are not on your farm (i.e.: Live Search or Google).  After you get the update deployed to your MOSS server(s), go create a new Search Center with Tabs site.  Immediately, you will notice that it looks slightly different.  The first thing that is changed is that there is a Content Editor web part underneath the search box which basically has a link to a disclaimer that says you searches also pull content from the Internet.  I found this quite lame so I deleted it immediately.  Perform a search and you will see that the search results page is different now too.  The search results are the same but there is two new instances of the Federated Results web part.  This new web part takes what you searched for and also passes it to Windows Live search.  It works by specifying a Federated Search Location.

    This is where the new administration pages come in.  The existing search pages are still there under the link Search Settings, but if you look in your SSP, you will see a new link for Search Administration.  Just about every page for configuring search has been updated.  The first page you see displays the same basic information on search but also has two new web parts on it (new in Microsoft.Office.Server.Search.dll) that display currently running crawls and crawls that have recently finished.  The Content Sources page has been updated to show how long the current crawl has been running as well as the duration of the last crawl.  A new Crawl Log page which lets you get to all crawl logs in one place but the only identifier on them is a GUID, so you have to click on each one to view what it is.  I was sad to see that the interface for mapping crawled properties to managed properties still has not changed.  Of anything that needed to  be updated it was that since it is very difficult to pick properties with long names.  A new Proxy and Timeouts page lets you specify to use a proxy server as well as specify the timeout to use when crawling.

    There is also a page to configure Federated Search.  The first thing to note is that the local search index is listed here as Local Search Results.  From what I can tell is that this really only allows for it to be used in a Federated Results web part.  From what I can tell there are no changes to the CoreResultsWebPart.  Which means you are not going to be displaying federated results with it.  This also means that if you were getting your hopes up that you could display search results from your local index and some place else all mixed together it isn't going to happen.  Out of the box there are two other federation search locations that are used to query Live Search.  It is here where you configure the URLs to these external searches and provide XSLT to configure the output in the Federated Results web part.  There is also a link to an online gallery of other federated search connectors for things such as Google, Yahoo, etc.

    All in all, I am pretty excited about these new changes.  The new search stuff is really nice and I am looking forward to using it more.  Also note that Microsoft recommends installing this update as soon as possible.  Here is more info about the search specific stuff from the Enterprise Search Team's blog.  I expect I will be posting more about using these new features soon.

  • Passing a query string parameter to an InfoPath data source

    Recently, I wanted to create a simple InfoPath form that took a query string parameter (an id) and passed it to a secondary data source that called a web service.  There may be a simpler solution to this, but I went with a programmatic solution.  So, I set my preferred language as C# and went to the Programming menu item under the Tools menu.  For the purpose of this example, my secondary data source is called WebServiceDataSource and it takes a single parameter called ProductId.  Before you add any code, you will want to turn off the option to automatically execute the data source on form load.

    This code goes in the FormEvents_Loading event handling method.  The first thing we need to do is actually get the parameter passed in from the query string.  This works similarly to TryParse.  It returns true if successful and writes the value into an out parameter.

    e.InputParameters.TryGetValue("ProductId", out productId)

    This next line of code selects the node of the ProductId parameter and sets the value.  XPath is used to find the current node.  Remember you can get the XPath query of the node by selecting it in the Data Source explorer and using the Copy XPath context menu item.

    DataSources["WebServiceDataSource"].CreateNavigator().SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:WebServiceDataSource/tns:ProductId", NamespaceManager).SetValue(productId);

    The last thing you need to do is execute the data source.

    DataSources["WebServiceDataSource"].QueryConnection.Execute();

    At this point you can pass a parameter to the form and your secondary data source will use that parameter.  Since there is code in the form now, when you publish it you will of course have to select the Administrator-approved form template option.  You will then have to upload it on the manage form templates page in Central Administration.  Here is what all of the code looks like together.

    string productId;

     

    // get the value if it exists

    if (e.InputParameters.TryGetValue("ProductId", out productId))

    {

        // select the node in the secondary datasource

        DataSources["WebServiceDataSource"].CreateNavigator().SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:WebServiceDataSource/tns:ProductId", NamespaceManager).SetValue(productId);

     

        DataSources["WebServiceDataSource"].QueryConnection.Execute();

    }

    else

    {

        // handle an error

    }

    I am no expert on InfoPath, but this solution works for me.  If you know of a better solution, please post it.

More Posts Next page »
2008 dotnetmafia.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems