应用程序名称:ExternalResourceDBServer

Revit 平台:所有

Revit 版本:2015.0

首次发布于:2015.0

编程语言:C#

技能水平:中等

类别:数据交换

类型:ExternalApplication

主题:示例外部资源服务器

概要:

该示例演示了从IExternalResourceServer接口派生的具体类。该类模拟从远程存储位置中提供关键数据和Revit链接模型的实现,并展示如何从数据库中创建关键数据。

相关类:

Autodesk.Revit.DB.IExternalDBApplication

Autodesk.Revit.DB.IExternalResourceServer

Autodesk.Revit.DB.IGetLocalPathForOpenCallback

Autodesk.Revit.DB.IOnLocalLinkSharedCoordinatesSavedCallback

项目文件:

 

Application.cs - 包含从IExternalDBApplication接口继承并实现OnStartupOnShutdown方法的DBApplication类。在OnStartup方法中注册了示例外部资源数据库服务器。

SampleExternalResourceDBServer.cs - 包含SampleExternalResourceDBServer- 一个IExternalResourceServer接口的实现。

ServerInterfaceExtensionsForRevitLinks.cs - 包含GetLinkPathForOpenLocalLinkSharedCoordinatesSaved,它们是其他外部资源服务器接口的实现 - IGetLocalPathForOpenCallbackIOnLocalLinkSharedCoordinatesSavedCallback。这些回调类需要以支持Revit链接。

KeynotesDatabase.cs - 包含KeynotesDatabase类,它是一个“虚拟”的数据库,用于演示如何从任意来源加载关键数据,而不是传统的文本文件。

说明:

此示例提供以下功能。

- 允许最终用户从“远程服务器位置”加载主题演讲数据,而不是从存储在本地可访问驱动器上的文本文件中加载。

- 允许最终用户从“远程服务器位置”链接 Revit 模型,而不是从本地可访问驱动器中链接。

说明:

1. 设置

这份代码样本是为了演示目的,假设“远程服务器位置”实际上是一个文件夹,在该文件夹之下,DLL 由此代码所生成。因此,在构建此代码样本后,确保复制\SampleResourceServerRoot 文件夹和其内容到您放置 DLL 文件的同一个目录下。

 

2. 从服务器加载主题演讲数据。

a. 启动 Revit 并打开一个模型,或创建一个新项目。

b. 打开主题演讲设置对话框(注释选项卡|主题演讲按钮)

c. 选择“浏览”。

d. 在左侧的位置栏中,选择“外部资源”。

e. 选择 SDK 示例服务器。

f. 浏览服务器上的文件夹,选择要加载的主题演讲资源。注意: 为了演示服务器框架的灵活性,这个示例会检测用户是否使用法语或德语文化环境,并将其浏览定向到一个模拟数据库。其他用户则浏览在第一部分中描述的\SampleResourceServerRoot文件夹下的实际文件夹和文件。

g. 点击“打开”,主题演讲数据应该会加载到 Revit 中。

h. 您可以通过在主题演讲设置对话框中选择“视图”来检查数据。

 

3. 从服务器链接 Revit 模型。

a. 启动 Revit 并打开一个模型,或创建一个新项目。

b. 从项目浏览器、管理链接对话框或插入选项卡中,添加一个新的 Revit 链接。

c. 从第二部分复制步骤de

d. 浏览“远程服务器”上的目录(实际上只是第一部分所述的\SampleResourceServerRoot文件夹下的文件夹),选择要链接的Revit模型。

e. 点击“打开”,链接模型应会加载到 Revit 中。

源代码:

Application.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc. All rights reserved.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.

//
// AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL ITS FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable. 

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;

using Autodesk;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExternalService;
using Autodesk.Revit.ApplicationServices;

namespace Revit.SDK.Samples.ExternalResourceDBServer.CS
{
   /// <summary>
   /// <para>Implements the Revit add-in interface IExternalDBApplication.</para>
   /// <para>An IExternalResourceServer can be registered at any time during a Revit session.
   /// However, the most straightforward approach is to register during start-up, in the
   /// OnStartUp method of a Revit external application.  Since IExternalResourceServers
   /// do not implement any UI directly, they can be registered in a DB-only context,
   /// using an IExternalDBApplication.</para>
   /// </summary>
   public class DBApplication : IExternalDBApplication
   {
      #region IExternalDBApplication Members
      /// <summary>
      /// Registers an instance of a SampleExternalResourceDBServer with the ExternalService
      /// of type ExternalResourceService. 
      /// </summary>
      /// <param name="application">An object that is passed to the external application
      /// which contains the controlled application.</param>
      /// <returns>Return the status of the external application.  A result of Succeeded
      /// means that the external application was able to register the IExternalResourceServer.
      /// </returns>
      public ExternalDBApplicationResult OnStartup(ControlledApplication application)
      {
         // Get Revit's ExternalResourceService.
         ExternalService externalResourceService = ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.ExternalResourceService);

         if (externalResourceService == null)
            return ExternalDBApplicationResult.Failed;

         // Create an instance of your IExternalResourceServer and register it with the ExternalResourceService.
         IExternalResourceServer sampleServer = new SampleExternalResourceDBServer();
         externalResourceService.AddServer(sampleServer);
         return ExternalDBApplicationResult.Succeeded;
      }


      /// <summary>
      /// <para>Implements the OnShutdown event.</para>
      /// <para>The server implementer may wish to perform clean-up tasks here.  However, in the
      /// simplest case, no server-related code is required, and the server will be
      /// destroyed as Revit shuts down.</para>
      /// </summary>
      /// <param name="application">An object that is passed to the external application
      /// which contains the controlled application.</param>
      /// <returns>Return the status of the external application.</returns>
      public ExternalDBApplicationResult OnShutdown(ControlledApplication application)
      {
         return ExternalDBApplicationResult.Succeeded;
      }

      #endregion IExternalDBApplication Members
   }
}

KeynotesDatabase.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Autodesk.Revit;
using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.ExternalResourceDBServer.CS
{
   /// <summary>
   /// A "fake" keynote database used to demonstrate how Revit keynote data can
   /// be generated without reading from a *.txt file.
   /// </summary>
   static class KeynotesDatabase
   {

      /// <summary>
      /// Indicates what version of keynote data is currently available from the database.
      /// </summary>
      public static String CurrentVersion
      {
         // Assume that the server's keynote data is updated at the beginning of every month.
         get { return System.DateTime.Now.ToString("MM-yyyy"); }
      }


      /// <summary>
      /// Validates the specified database 'key.'
      /// </summary>
      /// <param name="key">A string containing the 'key' to a particular set of keynote data
      /// that is available from this 'database.'</param>
      /// <returns>True, if the specified 'key' corresponds to a valid set of keynote data in the
      /// 'database,' else False.</returns>
      public static bool IsValidDBKey(String key)
      {
         return key == "1" || key == "2" || key == "3" || key == "4";
      }


      /// <summary>
      /// Loads keynote data corresponding to the specified 'key' string into a KeyBasedTreeEntriesLoadContent
      /// object.
      /// </summary>
      /// <param name="key">A string containing the 'key' to a particular set of keynote data
      /// that is available from this 'database.'</param>
      /// <param name="kdrlc">A KeyBasedTreeEntriesLoadContent object to which the keynote data will be
      /// added.</param>
      /// <returns></returns>
      public static void LoadKeynoteEntries(String key, ref KeyBasedTreeEntriesLoadContent kdrlc)
      {
         if (!IsValidDBKey(key))
            throw new ArgumentOutOfRangeException("key", key, "The specified key cannot be found in the database");

         if (kdrlc == null)
            throw new ArgumentNullException("kdrlc");

         switch(key)
         {
            case "1":
               {
                  // German 1
                  kdrlc.AddEntry(new KeynoteEntry("01",    "",   "Dienstleistungen, Produktionen"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01", "01", "Fuhrparkkosten"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01",    "01.01",    "Frachten"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01", "01.01.01", "Eigene Fracht"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02", "01.01.01", "Fremdfracht"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.99", "01.01.01", "Sonstige - Fracht"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.02"	, "01.01",    "Fuhrparkleistungen"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.02.01", "01.01.02", "Eigener Fuhrpark"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.02.02", "01.01.02", "Fremder Fuhrpark"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.02.99", "01.01.02", "Sonstige - Fuhrparkleistung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.03"	, "01.01",    "Kran und Stapler"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.03.01", "01.01.03", "Kranentladung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.03.02", "01.01.03", "Staplerkosten"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.03.99", "01.01.03", "Sonstiger - Kran und Stapler"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02",        "01",       "Handwerkliche Leistungen"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01",     "01.02",    "Allgemeine Bauarbeiten"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01",  "01.02.01", "Dachdeckungsarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.02",  "01.02.01", "Elektroinstallation"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.03",  "01.02.01", "Erdarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.04",  "01.02.01", "Klempnerarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.05",  "01.02.01", "Rohbau"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.06",  "01.02.01", "Sanitär- und Heizungsbauarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.07",  "01.02.01", "Trockenbauarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.08",  "01.02.01", "Verglasungsarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.99",  "01.02.01", "Sonstige - allgem. Bauarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02",     "01.02",    "Belagsarbeiten"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01",  "01.02.02", "Asphaltarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02",  "01.02.02", "Estricharbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.03",  "01.02.02", "Mineralgemischeinbringung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.04",  "01.02.02", "Natursteinarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.99",  "01.02.02", "Sonstige - Belagsarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.03",     "01.02",    "Betonarbeiten"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.03.01",  "01.02.03", "Betoninstandhaltung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.03.02",  "01.02.03", "Betonsanierung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.03.99",  "01.02.03", "Sonstige - Betonarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.04",     "01.02",    "Montage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.04.01",  "01.02.04", "Dachmontage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.04.02",  "01.02.04", "Fenstermontage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.04.03",  "01.02.04", "Montagewand-/Unterdeckenmontage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.04.04",  "01.02.04", "Türenmontage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.04.99",  "01.02.04", "Sonstige - Montage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05",     "01.02",    "Verlegung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.01",  "01.02.05", "Fliesenverlegung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.02",  "01.02.05", "Parkett- und Laminatverlegung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.03",  "01.02.05", "Pflasterarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.04",  "01.02.05", "Plattenverlegung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.05",  "01.02.05", "PVC-Verlegung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.06",  "01.02.05", "Tapezierarbeit"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.07",  "01.02.05", "Teppichverlegung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.08",  "01.02.05", "Wand- und Deckenvertäfelung"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05.99",  "01.02.05", "Sonstige - Verlegung"));
                  break;
               }
            case "2":
               {
                  // German 2
                  kdrlc.AddEntry(new KeynoteEntry("02",             "",         "Schüttgüter"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01",          "02",       "Schüttungen, Asche, Salze"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.01",       "02.01",    "Abraum"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.01.01",    "02.01.01", "Humus"));	
                  kdrlc.AddEntry(new KeynoteEntry("02.01.01.02",    "02.01.01", "Mutterboden"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.01.03",    "02.01.01", "Schotter"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.01.04",    "02.01.01", "Torf"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.01.99",    "02.01.01", "Sonstiger - Abraum"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.02",       "02.01",    "Asche"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.02.01",    "02.01.02", "Steinkohlenflugasche"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.02.99",    "02.01.02", "Sonstige - Asche"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.03",       "02.01",    "Bims"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.03.01",    "02.01.03", "Hüttenbims"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.03.02",    "02.01.03", "Naturbims"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.03.99",    "02.01.03", "Sonstiger - Bims"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.04",       "02.01",    "Kies, Kiessand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.04.01",    "02.01.04", "Kies"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.04.01.A1", "02.01",    "Kies"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.04.02",    "02.01.04", "Kiessand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.04.02.A2", "02.01",    "Kiessand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.04.99",    "02.01.04", "Sonstiger - Kies/Kiessand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.05",       "02.01",    "Mineral und Salz"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.05.01",    "02.01.05", "Mineralstoffgemisch"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.05.02",    "02.01.05", "Salz"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.05.99",    "02.01.05", "Sonstiges - Mineral/Salz"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06",       "02.01",    "Sandsorten"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06.01",    "02.01.06", "Quarzsand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06.01.A3", "02.01",    "Quarzsand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06.02",    "02.01.06", "Sand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06.02.A4", "02.01",    "Sand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06.03",    "02.01.06", "Tennissand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06.03.A5", "02.01",    "Tennissand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.06.99",    "02.01.06", "Sonstige - Sandsorte"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.07",       "02.01",    "Schlacke"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.07.01",    "02.01.07", "Hüttenofenschlacke"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.07.02",    "02.01.07", "Lavaschlacke"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.07.03",    "02.01.07", "Schmelzkammerschlacke"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.07.99",    "02.01.07", "Sonstige - Schlacke"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08",       "02.01",    "Splittsorten"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08.01",    "02.01.08", "Splitt"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08.01.A6", "02.01",    "Splitt"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08.02",    "02.01.08", "Splittsand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08.02.A7", "02.01",    "Splittsand"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08.03",    "02.01.08 ", "Ziegelsplitt"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08.03.A8", "02.01",     "Ziegelsplitt"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.08.99",    "02.01.08",  "Sonstige - Splittsorte"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.50",       "02.01",     "Gewachsener Boden"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.50.01",    "02.01.50",  "Gewachsener Boden"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.50.01.A10","02.01",     "Gewachsener Boden"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.50.01.A7", "02.01",     "Gewachsener Boden"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.51",       "02.01",     "Schotter"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.51.01",    "02.01.51",  "Schotter"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.51.01.A6", "02.01",     "Schotter"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01.51.01.A9", "02.01",     "Schotter"));
                  kdrlc.AddEntry(new KeynoteEntry("02.99",          "02",        "Sonstiges"));
                  break;
               }
            case "3":
               {
                  // French 1
                  kdrlc.AddEntry(new KeynoteEntry("01",              "",             "VRD Assainissement"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01",           "01",           "Amélioration et stabilisation des sols"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01",        "01.01",        "Assainissement des sols"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01",     "01.01.01",     "Drain"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01.A1",  "01.01.01.01",  "Tube de drainage PVC 200"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01.A2",  "01.01.01.01",  "Tube de drainage PVC 150"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01.A3",  "01.01.01.01",  "Tube de drainage PVC 100"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01.A4",  "01.01.01.01",  "Tube de drainage terre-cuite 80"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01.A5",  "01.01.01.01",  "Tube de drainage terre-cuite 100"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01.A6",  "01.01.01.01",  "Tube de drainage PVC annelé 80"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.01.A7",  "01.01.01.01",  "Tube de drainage PVC annelé 100"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02",     "01.01.01",     "Nappe drainante"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A1",  "01.01.01.02",  "Nappe drainante 6"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A2",  "01.01.01.02",  "Nappe drainante 8"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A3",  "01.01.01.02",  "Nappe drainante 10"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A4",  "01.01.01.02",  "Nappe drainante 12"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A5",  "01.01.01.02",  "Nappe drainante 14"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A6",  "01.01.01.02",  "Nappe drainante 18"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A7",  "01.01.01.02",  "Nappe drainante 20"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A8",  "01.01.01.02",  "Nappe drainante 24"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.02.A9",  "01.01.01.02",  "Nappe drainante 28"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03",     "01.01.01",     "Matériau de drainage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A1",  "01.01.01.03",  "Drainage Gravier"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A10", "01.01.01.03",  "Drainage Terre naturelle"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A2",  "01.01.01.03",  "Drainage Gravier de compactage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A3",  "01.01.01.03",  "Drainage Sable de quartz"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A4",  "01.01.01.03",  "Drainage Sable"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A5",  "01.01.01.03",  "Drainage Sable de court de tennis"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A6",  "01.01.01.03",  "Drainage Gravillon"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A7",  "01.01.01.03",  "Drainage Gravillon de compactage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A8",  "01.01.01.03",  "Drainage Grave concassé"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.01.03.A9",  "01.01.01.03",  "Drainage Pierraille"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.02",        "01.01",        "Apport d'autre sol"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.03",        "01.01",        "Traitement chimique des sols"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.04",        "01.01",        "Compactage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.01.05",        "01.01",        "Stabilisation de remblais"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02",           "01",           "Voirie"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01",        "01.02",        "Revêtement de voirie"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01",     "01.02.01",     "Revt de voirie Enrobé"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01.A1",  "01.02.01.01",  "Couche Asphalte"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01.A2",  "01.02.01.01",  "Couche Ciment"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01.A3",  "01.02.01.01",  "Couche Anhydrite"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01.A4",  "01.02.01.01",  "Couche Bitume"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01.A5",  "01.02.01.01",  "Couche Gypse"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01.A6",  "01.02.01.01",  "Couche Résine"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.01.A7",  "01.02.01.01",  "Couche Magnésien"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.02",     "01.02.01",     "Revt de voirie Béton"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.01.03",     "01.02.01",     "Revt de voirie Pavage"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02",        "01.02",        "Bordure et caniveau"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01",     "01.02.02",     "Bordure"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01",  "01.02.02.01",  "Bordure en béton"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A1",  "01.02.02.01", "Bordure normalisée T1"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A10", "01.02.02.01", "Bordure haute 12x30"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A11", "01.02.02.01", "Bordure haute 12x25"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A12", "01.02.02.01", "Bordure arrondie 13,5x22"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A13", "01.02.02.01", "Bordure arrondie 16,5x22"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A15", "01.02.02.01", "Bordure basse 6,5x20"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A16", "01.02.02.01", "Bordure basse 8,5x25"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A17", "01.02.02.01", "Bordure basse 8,5x30"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A18", "01.02.02.01", "Bloc pour gazon 5x20"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A19", "01.02.02.01", "Bloc pour gazon 5x25"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A2",  "01.02.02.01", "Bordure normalisée T2"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A3",  "01.02.02.01", "Bordure normalisée T3"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A4",  "01.02.02.01", "Bordure normalisée T4"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A5",  "01.02.02.01", "Bordure normalisée A1"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A6",  "01.02.02.01", "Bordure normalisée A2"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A7",  "01.02.02.01", "Bordure normalisée P1"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A8",  "01.02.02.01", "Bordure haute 15x30"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.01.A9",  "01.02.02.01", "Bordure haute 15x25"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.02",     "01.02.02.01", "Bordure bateau en béton"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.02.D1",  "01.02.02.01", "Bordure bateau en béton 300x250"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.02.D2",  "01.02.02.01", "Bordure centrale bateau en béton 300x250"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.03",     "01.02.02.01", "Bordure en pierre naturelle"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.03.A1",  "01.02.02.01", "Bordure pierre naturelle 80x250"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.03.A2",  "01.02.02.01", "Bordure pierre naturelle 100x300"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.03.A3",  "01.02.02.01", "Bordure pierre granitique 80x250"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.01.03.A4",  "01.02.02.01", "Bordure pierre granitique 100x300"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02",        "01.02.02",    "Caniveau"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.01",     "01.02.02.02", "Caniveau bloc central"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.01.C1",  "01.02.02.02", "Caniveau central 40x100x12 - CC1"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.01.C2",  "01.02.02.02", "Caniveau central 50x100x14 - CC2"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.01.C3",  "01.02.02.02", "Caniveau central 30x40x9"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.01.C4",  "01.02.02.02", "Caniveau central 40x40x11,5"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.01.C5",  "01.02.02.02", "Caniveau central 50x40x13,5"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.01.C6",  "01.02.02.02", "Caniveau central 30x30x12"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.02",     "01.02.02.02", "Caniveau bloc de bordure"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.02.02.02.D1",  "01.02.02.02", "Caniveau en bordure 300x175x125"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.03",           "01.02",       "Marquage et signalisation"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.04",           "01.02",       "Contrôle d'accès"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.05",           "01.02",       "Eqt de sécurité"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.06",           "01.02",       "Eqt de chantier"));
                  kdrlc.AddEntry(new KeynoteEntry("01.02.07",           "01.02",       "Eqt divers"));
                  break;
               }
            case "4":
               {
                  // French 2
                  kdrlc.AddEntry(new KeynoteEntry("02",          "",          "Aménagement ext"));
                  kdrlc.AddEntry(new KeynoteEntry("02.01",       "02",        "Eclairage d'ext et commande (voir 190402)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02",       "02",        "Mobilier et eqt urbain"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.01",    "02.02",     "Stockage des ordures (voir 24.09.04)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.02",    "02.02",     "Kiosque de jardin"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.03",    "02.02",     "Siège et banc public, table"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.04",    "02.02",     "Jardinière, bac, vasque, ..."));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.05",    "02.02",     "Borne à eau, fontaine"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.06",    "02.02",     "Abris bus"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.07",    "02.02",     "Abris bicyclette et moto, porte bicyclette"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.08",    "02.02",     "Cabine téléphonique exte"));
                  kdrlc.AddEntry(new KeynoteEntry("02.02.09",    "02.02",     "Panneau d'affichage ext"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03",       "02",        "Parc et jardin (eqt), espace de rencontre"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.01",    "02.03",     "Abris jardin"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.02",    "02.03",     "Tonnelle et pergola"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.03",    "02.03",     "Véranda, verrière, serre (voir 1811)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.04",    "02.03",     "Bordure de jardin"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.05",    "02.03",     "Bac à sable"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.06",    "02.03",     "Bassin décoratif"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.07",    "02.03",     "Arrosage de jardin (dispositif et access)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.08",    "02.03",     "Grille et protection d'arbre"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.09",    "02.03",     "Mât de pavillon, pavillon, drapeau"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.10",    "02.03",     "Treillage, support de plante"));
                  kdrlc.AddEntry(new KeynoteEntry("02.03.11",    "02.03",     "Mobilier de jardin"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04",       "02",        "Sport et loisir, espace de jeu (eqt)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.01",    "02.04",     "Jeux aquatiques"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.02",    "02.04",     "Jeux pour enfant"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.03",    "02.04",     "Golf miniature"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.04",    "02.04",     "Piste de skateboard"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.05",    "02.04",     "Court de squash"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.06",    "02.04",     "Piscine (eqt et access),"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.07",    "02.04",     "Sanitaire (appareil) (voir 2004)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.08",    "02.04",     "Court de tennis"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.09",    "02.04",     "Piste de patinoire"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.10",    "02.04",     "Gymnase (eqt sportif et)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.11",    "02.04",     "Borne pour camping"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.12",    "02.04",     "Mur d'escalade"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.13",    "02.04",     "Station de ski (eqt)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.14",    "02.04",     "Tribune et podium"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.15",    "02.04",     "Barbecue fixe"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.16",    "02.04",     "Gradin de stade"));
                  kdrlc.AddEntry(new KeynoteEntry("02.04.17",    "02.04",     "Port de plaisance (eqt)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.05",       "02",        "Structure mobile, légère, provisoire (voir 0702)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.06",       "02",        "Espace vert et plantation"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07",       "02",        "Ouvrage ext (eqt)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.01",    "02.07",     "Clôture et mur d'enceinte, poteau de clôture"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.02",    "02.07",     "Portail, portillon et access"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.03",    "02.07",     "Protection des ouvrages (choc)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.04",    "02.07",     "Ecran antibruit (routier, ferroviaire)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.05",    "02.07",     "Métallerie de sûreté (voir 2302)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.06",    "02.07",     "Garde-corps (voir 1805)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.07",    "02.07",     "Ferronnerie d'art et décorative (voir 1806)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.08",    "02.07",     "Pont, passerelle, appui"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.09",    "02.07",     "Revt de sol et mur pierre, ciment (voir 1608)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.10",    "02.07",     "Boîte aux lettres et access (voir 1809)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.11",    "02.07",     "Câble, corde, grillage, filet (voir 2510)"));
                  kdrlc.AddEntry(new KeynoteEntry("02.07.12",    "02.07",     "Etendoir, poteau d'étendage"));
                  kdrlc.AddEntry(new KeynoteEntry("02.99",       "02",        "Prestations en aménagement ext (voir 2702)"));
                  break;
               }
         }
      }
   }
}

SampleExternalResourceDBServer.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExternalService;

namespace Revit.SDK.Samples.ExternalResourceDBServer.CS
{

   /// <summary>
   /// <para>Derive from the IExternalResourceServer interface to create a server class
   /// for providing external resources (files or data) to Revit.  This example server
   /// provides both keynote data and Revit links.</para>
   /// <para>The external resource framework has been designed so that the end user
   /// loads external resources by browsing with an "open file" dialog.  When the user
   /// chooses an external resource server, the file dialog will display the contents
   /// of the server's "root folder."  From there, the user can navigate through a system
   /// of folders and files specified by the IExternalResourceServer implementation.  The
   /// files and folders might be the actual contents of a directory tree only accessible
   /// by the server, or might be some other logical structure relevant to the resource
   /// that is being loaded.</para>
   /// <para>To demonstrate the flexibility of the framework, this example server retrieves
   /// keynote data from a fictitious database for users in France and Germany, and from
   /// files for all other users.  Revit link models are also obtained from files.  To keep
   /// the demonstration simple, the "root" folder for file-based resources is assumed to be
   /// \SampleResourceServerRoot, located immediately under the directory where the DLL for
   /// this project is placed.  See the separate *.rtf file for additional documentation.</para>
   /// </summary>
   public class SampleExternalResourceDBServer : IExternalResourceServer
   {

      /// <summary>
      /// Default constructor
      /// </summary>
      public SampleExternalResourceDBServer()
      { }



      // Methods that must be implemented by a server for any of Revit's external services
      #region IExternalServer Implementation

      /// Indicate which of Revit's external services this server supports.
      /// Servers derived from IExternalResourceServer *must* return
      /// ExternalServices.BuiltInExternalServices.ExternalResourceService.
      public ExternalServiceId GetServiceId()
      {
         return ExternalServices.BuiltInExternalServices.ExternalResourceService;
      }

      /// Uniquely identifies this server to Revit's ExternalService registry
      public System.Guid GetServerId()
      {
         return new Guid("5F3CAA13-F073-4F93-BDC2-B7F4B806CDAF");
      }


      /// <summary>
      /// Implement this method to return the name of the server.
      /// </summary>
      public System.String GetName()
      {
         return "SDK Sample ExtRes Server";
      }


      /// <summary>
      /// # Implement this method to return the id of the vendor of the server.   
      /// </summary>
      public System.String GetVendorId()
      {
         return "ADSK";
      }

      /// <summary>
      /// Provide a short description of the server for display to the end user.
      /// </summary>
      public System.String GetDescription()
      {
         return "A Revit SDK sample external resource server which provides keynote data and Revit links.";
      }

      #endregion IExternalServer Implementation




      ///  Methods implemented specifically by servers for the ExternalResource service
      #region IExternalResourceServer Implementation

      public string GetShortName()
      {
         return GetName();
      }


      /// <summary>
      /// Returns a URL address of the provider of this Revit add-in.
      /// </summary>
      public virtual String GetInformationLink()
      {
         return "http://www.autodesk.com";
      }

      /// <summary>
      /// Specify an image to be displayed in browser dialogs when the end user is selecting a resource to load into Revit.
      /// </summary>
      /// <returns>A string containing the full path to an icon file containing 48x48, 32x32 and 16x16 pixel images.</returns>
      public string GetIconPath()
      {
         return string.Empty;
      } 


      /// <summary>
      /// IExternalResourceServer classes can support more than one type of external resource.
      /// This one supports keynotes and Revit links.
      /// </summary>
      public bool SupportsExternalResourceType(ExternalResourceType resourceType)
      {
         return (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable
                    ||
                 resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.RevitLink);
      }


      /// <summary>
      /// Return a list of resources and sub folders under a folder.
      /// The Revit user always loads external resources by browsing with a file navigation
      /// dialog, much like they would when selecting files on a locally-accessible drive.
      /// The SetupBrowserData method allows the server implementer to simulate an organized
      /// system of files and folders to support browsing for external resources.
      /// Purely for demonstration purposes, this sample server obtains its keynote data from
      /// a "database," and creates a "fake" directory structure for either German or French users
      /// to browse keynote data.  However, for all other users - and for Revit Links, file browsing
      /// data is generated using actual files on in a folder location under the directory containing
      /// this DLL.
      /// </summary>
      public void SetupBrowserData(ExternalResourceBrowserData browserData)
      {
         ExternalResourceMatchOptions matchOptions = browserData.GetMatchOptions();

         ExternalResourceType resourceType = matchOptions.ResourceType;

         CultureInfo currentCulture = CultureInfo.CurrentCulture;
         String currentCultureName = currentCulture.ToString();

         // Revit will call SupportsExternalResourceType() before calling this method, so we
         // can assume that resourceType will be KeynoteTable or RevitLink.
         if (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable
                &&
              (currentCultureName == "de-DE" || currentCultureName == "fr-FR"))
         {
            // French and German Keynote Data Are Obtained From a "Database"
            SetupKeynoteDatabaseBrowserData(browserData, currentCultureName);
         }
         else
         {
            // Keynote Data in Other Languages, and Revit Links, are Obtained From File
            SetupFileBasedBrowserData(browserData);
         }
      }


      /// <summary>
      /// Checks whether the given ExternalResourceReference is formatted correctly for this server.
      /// The format should match one of the formats created in the SetupBrowserData method.
      /// </summary>
      public bool IsResourceWellFormed(ExternalResourceReference extRef)
      {
         if ( extRef.ServerId != GetServerId() )
            return false;
         if ( !extRef.HasValidDisplayPath() )
            return false;

         // Either the ExternalResourceReference has a valid database key (German/French keynote case) ...
         IDictionary<string, string> refMap = extRef.GetReferenceInformation();
         if (refMap.ContainsKey(RefMapDBKeyEntry))
         {
            return KeynotesDatabase.IsValidDBKey(refMap[RefMapDBKeyEntry]);
         }
         else if (refMap.ContainsKey(RefMapLinkPathEntry))  // ... OR it is a Revit link file
         {
            return File.Exists(GetFullServerLinkFilePath(extRef));
         }

         // ... OR it is a keynote file (non- French/German cases).
         return File.Exists(GetFullServerKeynoteFilePath(extRef));
      }


      /// <summary>
      /// Implement this method to compare two ExternalResourceReferences.
      /// </summary>
      /// <param name="referenceInformation_1">The string-string IDictionary of reference information stored in one ExternalResourceReference</param>
      /// <param name="referenceInformation_2">The string-string IDictionary of reference information stored in a second ExternalResourceReference</param>
      /// <returns></returns>
      public virtual bool AreSameResources(IDictionary<string, string> referenceInformation_1, IDictionary<string, string> referenceInformation_2)
      {
         bool same = true;
         if (referenceInformation_1.Count != referenceInformation_2.Count)
         {
            same = false;
         }
         else
         {
            foreach (string key in referenceInformation_1.Keys)
            {
               if (!referenceInformation_2.ContainsKey(key) || referenceInformation_1[key] != referenceInformation_2[key])
               {
                  same = false;
                  break;
               }
            }
         }

         return same;
      }


      /// <summary>
      /// Servers can override the name for UI purposes, but here we just return the names that we
      /// used when we first created the Resources in SetupBrowserData().
      /// </summary>        
      public String GetInSessionPath(ExternalResourceReference err, String savedPath)
      {
         return savedPath;
      }


      /// <summary>
      /// Loads the resources.
      /// </summary>
      /// <param name="loadRequestId">A GUID that uniquely identifies this resource load request from Revit.</param>
      /// <param name="resourceType">The type of external resource that Revit is asking the server to load.</param>
      /// <param name="resourceReference">An ExternalResourceReference identifying which particular resource to load.</param>
      /// <param name="loadContext">Context information, including the name of Revit document that is calling the server,
      /// </param>the resource that is currently loaded and the type of load operation (automatic or user-driven).
      /// <param name="loadContent">An ExternalResourceLoadContent object that will be populated with load data by the
      /// server.  There are different subclasses of ExternalResourceLoadContent for different ExternalResourceTypes.</param>
      public void LoadResource(Guid loadRequestId, ExternalResourceType resourceType, ExternalResourceReference resourceReference, ExternalResourceLoadContext loadContext, ExternalResourceLoadContent loadContent)
      {
         loadContent.LoadStatus = ExternalResourceLoadStatus.Failure;

         // The following checks can help with testing.  However, they should not be necessary, since Revit checks all input paramters
         // before calling this method.
         if (loadRequestId == null)
            throw new ArgumentNullException("loadRequestId");;
         if (resourceType == null)
            throw new ArgumentNullException("resourceType");;
         if (resourceReference == null)
            throw new ArgumentNullException("resourceReference");;
         if (loadContext == null)
            throw new ArgumentNullException("loadContext");;
         if (loadContent == null)
            throw new ArgumentNullException("loadContent");;
         if (!SupportsExternalResourceType(resourceType))
            throw new ArgumentOutOfRangeException("resourceType", "The specified resource type is not supported by this server.");


         // The server indicates what version of the resource is being loaded.
         loadContent.Version = GetCurrentlyAvailableResourceVersion(resourceReference);


         // resourceReference is for Keynote Data
         if (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable)
         {
            LoadKeynoteDataResource(resourceReference, loadContent);
         }
         else   // resourceReference is a Revit Link
         {
            LoadRevitLink(resourceReference, loadContent);
         }
      }


      /// <summary>
      /// Indicates whether the given version of a resource is the most current
      /// version of the data.
      /// </summary>
      /// <param name="extRef">The ExternalResourceReference to check.</param>
      /// <returns>An enum indicating whether the resource is current, out of date, or of unknown status</returns>
      public ResourceVersionStatus GetResourceVersionStatus(ExternalResourceReference extRef)
      {
         // Determine whether currently loaded version is out of date, and return appropriate status.
         String currentlyLoadedVersion = extRef.Version;

         if (currentlyLoadedVersion == String.Empty)
            return ResourceVersionStatus.Unknown;

         return currentlyLoadedVersion == GetCurrentlyAvailableResourceVersion(extRef) ? ResourceVersionStatus.Current
                                                                                       : ResourceVersionStatus.OutOfDate;
      }


      /// <summary>
      /// Implement this to extend the base IExternalResourceServer interface with additional methods
      /// that are specific to particular types of external resource (for example, Revit Links).
      /// NOTE: There are no extension methods required for keynote resources.
      /// </summary>
      /// <param name="extensions">An ExternalResourceServerExtensions object that can be populated with
      /// sub-interface objects which can perform operations related to specific types of External Resource.</param>
      public virtual void GetTypeSpecificServerOperations(ExternalResourceServerExtensions extensions)
      {
         RevitLinkOperations revitLinkOps = extensions.GetRevitLinkOperations();
         revitLinkOps.SetGetLocalPathForOpenCallback(new GetLinkPathForOpen());
         revitLinkOps.SetOnLocalLinkSharedCoordinatesSavedCallback(new LocalLinkSharedCoordinatesSaved());
      }

      #endregion IExternalResourceServer Implementation



      #region SampleExternalResourceDBServer Implementation

      /// <summary>
      /// <para>Returns the path of the server's root folder.  The contents of this folder will be displayed
      /// when the user first selects this server while browsing to load keynote data or a Revit link
      /// (unless the user loading keynote data is in France or Germany).</para>
      /// <para>For this example server, the root folder is simply a directory immediately under the folder
      /// where the DLL for this assembly is located.</para>
      /// </summary>
      private static String RootFolder
      {
         get
         {
            string assemblyFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            string rootFolderName = assemblyFolder + "\\SampleResourceServerRoot";
            DirectoryInfo rootFolder = new DirectoryInfo(rootFolderName);
            if (!rootFolder.Exists)
               rootFolder.Create();

            return rootFolderName;
         }
      }

      /// <summary>
      /// Returns the string used to access the French and German keynotes database key that is
      /// stored in an ExternalResourceReference's reference information string-string Dictionary.
      /// See the SetupKeynoteDatabaseBrowserData method.
      /// </summary>
      private static String RefMapDBKeyEntry
      {
         get
         {
            return "DBKey";
         }
      }

      /// <summary>
      /// Returns the string used to access the server-based relative path to the Revit link file
      /// that is stored in an ExternalResourceReference's reference information string-string Dictionary.
      /// </summary>
      private static String RefMapLinkPathEntry
      {
         get
         {
            return "Path";
         }
      }


      /// <summary>
      /// Returns a string specifying the version of an external resource available from the server.
      /// </summary>
      /// <param name="extResRef">The ExternalResourceReference of the resource whose version is requested.</param>
      /// <returns>A string containing the version of the specified resource.</returns>
      private String GetCurrentlyAvailableResourceVersion(ExternalResourceReference extResRef)
      {
         IDictionary<string, string> refMap = extResRef.GetReferenceInformation();
         if (refMap.ContainsKey(RefMapDBKeyEntry))
         {
            return KeynotesDatabase.CurrentVersion;
         }
         else if (refMap.ContainsKey(RefMapLinkPathEntry))  // ... OR it is a Revit link file
         {
            String serverLinkPath = GetFullServerLinkFilePath(extResRef);
            return GetFileVersion(serverLinkPath);
         }

         // ... OR it is a keynote file (non- French/German cases).
         String serverKeynoteFilePath = GetFullServerKeynoteFilePath(extResRef);
         return GetFileVersion(serverKeynoteFilePath);
      }


      /// <summary>
      /// Provides Revit's file browser dialog with information for navigating the
      /// fictitious database containing French and German keynotes data.
      /// </summary>
      private void SetupKeynoteDatabaseBrowserData(ExternalResourceBrowserData browserData, String currentCultureName)
      {
         string folderPath = browserData.FolderPath;

         // To make this demonstration simple and clear, this code creates two sub-folders with names hard-coded
         // in French and German, and one keynote data source (must have the extension *.txt) under each of
         // these sub-folders.
         // To make subsequent recognition of these resources easier (in the server's LoadResource method and
         // elsewhere), a database "key" is stored in each resource's string-string Dictionary.
         if (currentCultureName == "de-DE")
         {
            if (folderPath == "/")  // Top-level
            {
               browserData.AddSubFolder("Unterordner1");
               browserData.AddSubFolder("Unterordner2");
            }
            else if (folderPath.EndsWith("/Unterordner1"))
            {
               var refMap = new Dictionary<String, String>();
               refMap[RefMapDBKeyEntry] = "1";
               browserData.AddResource("Keynotes1_de-DE.txt", KeynotesDatabase.CurrentVersion, refMap);
            }
            else if (folderPath.EndsWith("/Unterordner2"))
            {
               var refMap = new Dictionary<String, String>();
               refMap[RefMapDBKeyEntry] = "2";
               browserData.AddResource("Keynotes2_de-DE.txt", KeynotesDatabase.CurrentVersion, refMap);
            }
         }
         else if (currentCultureName == "fr-FR")
         {
            if (folderPath == "/")  // Top-level
            {
               browserData.AddSubFolder("Sous-dossier1");
               browserData.AddSubFolder("Sous-dossier2");
            }
            else if (folderPath.EndsWith("/Sous-dossier1"))
            {
               var refMap = new Dictionary<String, String>();
               refMap[RefMapDBKeyEntry] = "3";
               browserData.AddResource("Keynotes1_fr-FR.txt", KeynotesDatabase.CurrentVersion, refMap);
            }
            else if (folderPath.EndsWith("/Sous-dossier2"))
            {
               var refMap = new Dictionary<String, String>();
               refMap[RefMapDBKeyEntry] = "4";
               browserData.AddResource("Keynotes2_fr-FR.txt", KeynotesDatabase.CurrentVersion, refMap);
            }
         }
      }


      /// <summary>
      /// A convenience utility method.  For resources obtained from files on the server, we consider
      /// the resource version to be the last-modified date of the file.
      /// </summary>
      /// <param name="filePath">The full path of a file on disk.</param>
      /// <returns>The version (last-modified data) of the specified file.</returns>
      private String GetFileVersion(String filePath)
      {
         FileInfo fileInfo = new FileInfo(filePath);
         DateTime lastModifiedTime = fileInfo.LastWriteTimeUtc;
         CultureInfo enUs = new CultureInfo("en-us");
         return lastModifiedTime.ToString(enUs);
      }


      /// <summary>
      /// Computes the full path of a ExternalResourceReference's keynote data file location on the server.
      /// </summary>
      /// <param name="extRef">An ExternalResourceReference for a keynote data file stored on this server.</param>
      /// <returns>A string representing the full path to the Revit link resource on the server drive.</returns>
      private String GetFullServerKeynoteFilePath(ExternalResourceReference extRef)
      {
         String inSessionPath = extRef.InSessionPath;
         String serverName = GetName();
         // As an alternative to using the inSessionPath to determine the actual file path, the
         // preferred method is to store the relative server path of the file in the
         // ExternalResourceReference's referenceInformation (as is done for links).
         // This would be particularly true if you were overriding the default in-session path in
         // the GetInSessionPath() method.
         String serverKeynoteFilePath = inSessionPath.Replace(serverName + "://", RootFolder + "\\");
         return serverKeynoteFilePath;
      }


      /// <summary>
      /// <para>Provides Revit's file browser dialog with information for navigating a
      /// directory stucture of files available from the server.</para>
      /// <para>In this example implementation, the server simply echoes the directories
      /// and files that it locates under its RootFolder (subject to the appropriate file
      /// type filter pattern). </para>
      /// </summary>
      private void SetupFileBasedBrowserData(ExternalResourceBrowserData browserData)
      {
         ExternalResourceMatchOptions matchOptions = browserData.GetMatchOptions();
         // filter some resources out by specified filter pattern
         ExternalResourceType resourceType = matchOptions.ResourceType;
         string filterPattern = resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable
                                 ? "*.txt"
                                 : "*.rvt";

         // Expose a "real" directory structure for the user to browse.
         // The server's root folder is specified in the RootFolder property.
         string folderPath = browserData.FolderPath;
         string path = RootFolder + folderPath.Replace('/', '\\');
         if (Directory.Exists(path))
         {
            DirectoryInfo dir = new DirectoryInfo(path);
            DirectoryInfo[] subDirs = dir.GetDirectories();
            foreach (DirectoryInfo subDir in subDirs)
            {
               browserData.AddSubFolder(subDir.Name);
            }
            FileInfo[] subFiles = dir.GetFiles(filterPattern, SearchOption.TopDirectoryOnly);
            foreach (FileInfo file in subFiles)
            {
               if (resourceType == ExternalResourceTypes.BuiltInExternalResourceTypes.KeynoteTable)
                  browserData.AddResource(file.Name, GetFileVersion(file.FullName));
               else
               {
                  var refMap = new Dictionary<String, String>();
                  // Relative Path of Link File is Stored in the ExternalResourceReference that
                  // Will Be Addded to the BrowserData.
                  refMap[RefMapLinkPathEntry] = folderPath.TrimEnd('/') + '/' + file.Name;
                  browserData.AddResource(file.Name, GetFileVersion(file.FullName), refMap);
               }
            }
         }
         else
         {
            throw new ArgumentException("Path is invalid");
         }
      }


      /// <summary>
      /// Loads the keynote data resources, either from the fictitious French/German database, or from a file.
      /// </summary>
      /// <param name="resourceReference">An ExternalResourceReference identifying which particular resource to load.</param>
      /// <param name="loadContent">An ExternalResourceLoadContent object that will be populated with load data by the
      /// server.  There are different subclasses of ExternalResourceLoadContent for different ExternalResourceTypes.</param>
      private void LoadKeynoteDataResource(ExternalResourceReference resourceReference, ExternalResourceLoadContent loadContent)
      {
         KeyBasedTreeEntriesLoadContent kdrlc = (KeyBasedTreeEntriesLoadContent)loadContent;
         if (kdrlc == null)
            throw new ArgumentException("Wrong type of ExternalResourceLoadContent (expecting a KeyBasedTreeEntriesLoadContent) for keynote data.", "loadContent");

         kdrlc.Reset();

         // Either the ExternalResourceReference has a valid database key (German/French case) ...
         IDictionary<string, string> refMap = resourceReference.GetReferenceInformation();
         if (refMap.ContainsKey(RefMapDBKeyEntry))
         {
            try
            {
               KeynotesDatabase.LoadKeynoteEntries(refMap[RefMapDBKeyEntry], ref kdrlc);
               kdrlc.BuildEntries();
               loadContent.LoadStatus = ExternalResourceLoadStatus.Success;
            }
            catch (ArgumentOutOfRangeException)
            {
            }
            catch (ArgumentNullException)
            {
            }
         }
         else
         {
            // ... OR it is a real file (all other cases).
            String serverKeynoteFilePath = GetFullServerKeynoteFilePath(resourceReference);
            bool doesReadingSucceed = KeynoteEntries.LoadKeynoteEntriesFromFile(serverKeynoteFilePath, kdrlc);
            if (doesReadingSucceed)
            {
               kdrlc.BuildEntries();
               loadContent.LoadStatus = ExternalResourceLoadStatus.Success;
            }
         }
      }


      /// <summary>
      /// Loads a specified Revit link external resource.
      /// </summary>
      /// <param name="resourceReference">An ExternalResourceReference identifying which particular resource to load.</param>
      /// <param name="loadContent">An ExternalResourceLoadContent object that will be populated with load data by the
      /// server.  There are different subclasses of ExternalResourceLoadContent for different ExternalResourceTypes.</param>
      private void LoadRevitLink(ExternalResourceReference resourceReference, ExternalResourceLoadContent loadContent)
      {
         LinkLoadContent linkLoadContent = (LinkLoadContent)loadContent;
         if (linkLoadContent == null)
            throw new ArgumentException("Wrong type of ExternalResourceLoadContent (expecting a LinkLoadContent) for Revit links.", "loadContent");

         try
         {
            // Copy the file from the path under the server "root" folder to a secret "cache" folder on the users machine
            String fullCachedPath = GetFullLinkCachedFilePath(resourceReference);
            String cacheFolder = System.IO.Path.GetDirectoryName(fullCachedPath);
            if (!System.IO.Directory.Exists(cacheFolder))
            {
               System.IO.Directory.CreateDirectory(cacheFolder);
            }
            String serverLinkPath = GetFullServerLinkFilePath(resourceReference);
            System.IO.File.Copy(serverLinkPath, fullCachedPath, true);  // Overwrite

            ModelPath linksPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(fullCachedPath);
            linkLoadContent.SetLinkDataPath(linksPath);
            loadContent.LoadStatus = ExternalResourceLoadStatus.Success;
         }
         catch (System.Exception)
         {
         }

      }


      /// <summary>
      /// Computes the full path of a Revit link ExternalResourceReference's location on the server.
      /// </summary>
      /// <param name="resource">An ExternalResourceReference for a Revit link stored on this server.</param>
      /// <returns>A string representing the full path to the Revit link resource on the server drive.</returns>
      public static String GetFullServerLinkFilePath(ExternalResourceReference resource)
      {
         if (resource == null)
            return "";

         IDictionary<String, String> refMap = resource.GetReferenceInformation();
         if (!refMap.ContainsKey(RefMapLinkPathEntry))
            return "";

         return RootFolder + resource.GetReferenceInformation()[RefMapLinkPathEntry].Replace("/", "\\");
      }


      /// <summary>
      /// <para>Returns the path of the root folder of the cache where the server will make
      /// local copies of Revit links.  For this example server, the cache root folder is
      /// simply a directory immediately under the folder where the DLL for this assembly is
      /// located.</para>
      /// </summary>
      private static String LocalLinkCacheFolder
      {
         get
         {
            string assemblyFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            string cacheRootFolderName = assemblyFolder + "\\SampleResourceServerLinkCache";
            DirectoryInfo cacheRootFolder = new DirectoryInfo(cacheRootFolderName);
            if (!cacheRootFolder.Exists)
               cacheRootFolder.Create();

            return cacheRootFolderName;
         }
      }


      /// <summary>
      /// <para>Computes the full path on the end user's local drive where an
      /// ExternalResourceReference's link model will be copied when the user loads the
      /// link from the server.</para>
      ///<para>The paths of the local copies relative to this cache root folder will be the
      /// same as the paths of the original server copies relative to the Revit links
      /// to the root folder on the (compare this method with the GetFullServerLinkFilePath method).</para>
      /// </summary>
      /// <param name="resource">An ExternalResourceReference for a Revit link stored on this server.</param>
      /// <returns>A string representing the full path to the link model on the end user's local drive.</returns>
      public static String GetFullLinkCachedFilePath(ExternalResourceReference resource)
      {
         if (resource == null)
            return "";

         IDictionary<String, String> refMap = resource.GetReferenceInformation();
         if (!refMap.ContainsKey(RefMapLinkPathEntry))
            return "";

         return LocalLinkCacheFolder + resource.GetReferenceInformation()[RefMapLinkPathEntry].Replace("/", "\\");
      }

      #endregion SampleExternalResourceDBServer Implementation


      

      #region SampleExternalResourceDBServer Member Variables
      #endregion SampleExternalResourceDBServer Member Variables
   }
}

ServerInterfaceExtensionsForRevitLinks.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Autodesk.Revit;
using Autodesk.Revit.DB;
namespace Revit.SDK.Samples.ExternalResourceDBServer.CS
{

   /// <summary>
   /// Extension operator to support "Open (and Unload)" command.
   /// Revit will call this method to determine the location of the locally-cached copy
   /// of the linked model, and will open that copy directly.
   /// </summary>
   //=====================================================================================
   public class GetLinkPathForOpen : IGetLocalPathForOpenCallback
   {
      /// <summary>
      /// This implementation simply retrieves the same local file name that the server
      /// uses when first copying the link document to the user's machine.
      /// </summary>
      public string GetLocalPathForOpen(ExternalResourceReference desiredReference)
      {
         return SampleExternalResourceDBServer.GetFullLinkCachedFilePath(desiredReference);
      }
   }



   /// <summary>
   /// Extension operator to support handle updates when the user saves new shared coordinates in the
   /// local copy of a link.
   /// </summary>
   //=====================================================================================
   public class LocalLinkSharedCoordinatesSaved : IOnLocalLinkSharedCoordinatesSavedCallback
   {
      /// <summary>
      /// When the user updates the shared coordinates in the link model, Revit calls this
      /// method.  In this implementation, the updated local version of the link on the user's
      /// machine is uploaded (copied) back to the server location.
      /// </summary>
      public void OnLocalLinkSharedCoordinatesSaved(ExternalResourceReference changedReference)
      {
         string localLinkPath = SampleExternalResourceDBServer.GetFullLinkCachedFilePath(changedReference);
         string fullServerPath = SampleExternalResourceDBServer.GetFullServerLinkFilePath(changedReference);
         String serverDirectoryName = System.IO.Path.GetDirectoryName(fullServerPath);
         if (!System.IO.Directory.Exists(serverDirectoryName))
         {
            System.IO.Directory.CreateDirectory(serverDirectoryName);
         }
         System.IO.File.Copy(localLinkPath, fullServerPath, true);  // Overwrite
      }
   }

}