实现配置文件提供程序(ProfileProvider)

 ASP.NET 配置文件功能使您能够轻松使用不同的提供程序。可以使用 .NET Framework 中包含的 SqlProfileProvider 类,也可以实现自己的提供程序。

可以在下列情况下创建自定义配置文件提供程序:

  • 需要在 .NET Framework 包含的配置文件提供程序不支持的数据源中存储配置文件信息,如在 FoxPro 数据库或在 Oracle 数据库中。

  • 需要使用不同于 .NET Framework 包含的提供程序所使用的数据库架构来管理配置文件信息。常见示例是希望将配置文件信息与现有 SQL Server 数据库中的用户数据相集成。

必需的类

若要实现配置文件提供程序,请创建一个继承 System.Web.Profile.ProfileProvider 抽象类的类。ProfileProvider 抽象类再继承 System.Configuration.SettingsProvider 抽象类,后者又继承 System.Configuration.Provider.ProviderBase 抽象类。由于此继承链的存在,除 ProfileProvider 类的必需成员外,还必须实现 SettingsProvider 和 ProviderBase 类的必需成员。

下面的表描述必须从 ProviderBase、SettingsProvider 和 ProfileProvider 抽象类实现的属性和方法。若要查看每个成员的实现。

ProviderBase 成员

成员 说明

Initialize 方法

接受提供程序实例的名称和配置设置的 NameValueCollection 作为输入。用于设置提供程序实例的选项和属性值,其中包括特定于实现的值和计算机配置或 Web.config 文件中指定的选项。

SettingsProvider 成员

成员 说明

ApplicationName 属性

与每个配置文件一起存储的应用程序名称。配置文件提供程序使用应用程序名称来单独存储每个应用程序的配置文件信息。如果在不同的应用程序中创建相同的用户名,这会使多个 ASP.NET 应用程序在不发生冲突的情况下使用相同的数据源。或者,多个 ASP.NET 应用程序可通过指定相同的应用程序名称来共享配置文件数据源。

GetPropertyValues 方法

接受 SettingsContext 和 SettingsPropertyCollection 对象作为输入。

SettingsContext 提供有关用户的信息。可以将此信息作为主键使用,以检索用户的配置文件属性信息。使用 SettingsContext 对象来获取用户名,并确定用户是经过身份验证的用户还是匿名用户。

SettingsPropertyCollection 包含 SettingsProperty 对象的集合。每个 SettingsProperty 对象都提供属性的名称和类型以及其他信息,如属性的默认值,以及属性是否为只读。GetPropertyValues 方法用基于作为输入提供的 SettingsProperty 对象的 SettingsPropertyValue 对象来填充 SettingsPropertyValueCollection 。来自指定用户的数据源的值被分配给每个 SettingsPropertyValue 对象的 PropertyValue 属性,并返回整个集合。

调用该方法还会将指定的用户配置文件的 LastActivityDate 值更新为当前的日期和时间。

SetPropertyValues 方法

接受 SettingsContext 和 SettingsPropertyValueCollection 对象作为输入。

SettingsContext 提供有关用户的信息。可以将此信息作为主键使用,以检索用户的配置文件属性信息。使用 SettingsContext 对象来获取用户名,并确定用户是经过身份验证的用户还是匿名用户。

SettingsPropertyValueCollection 包含 SettingsPropertyValue 对象的集合。每个 SettingsPropertyValue 对象都提供属性的名称、类型和值以及其他信息,如属性的默认值,以及属性是否为只读。SetPropertyValues 方法为指定用户更新数据源中的配置文件属性值。

调用此方法还会将指定的用户配置文件的 LastActivityDate 和 LastUpdatedDate 值更新为当前的日期和时间。

ProfileProvider 方法

成员 说明

DeleteProfiles 方法

接受用户名的字符串数组作为输入,并在应用程序名称与 ApplicationName 属性值匹配时,从数据源中删除指定名称的所有配置文件信息和属性值。

如果数据源支持事务,建议将所有删除操作包括在事务中,并且如果任何删除操作失败,您可以回滚事务并引发异常。

DeleteProfiles 方法

接受 ProfileInfo 对象的集合作为输入,并在应用程序名称与 ApplicationName 属性值匹配时,从数据源中删除每个配置文件的所有配置文件信息和属性值。

如果数据源支持事务,建议将所有删除操作包括在事务中,并且如果任何删除操作失败,您可以回滚事务并引发异常。

DeleteInactiveProfiles 方法

接受 ProfileAuthenticationOption 值和 DateTime 对象作为输入,并在上一活动日期小于或等于指定的日期和时间,且应用程序名称与 ApplicationName 属性值匹配时,从数据源中删除所有配置文件信息和属性值。ProfileAuthenticationOption 参数指定是仅删除匿名配置文件,仅删除经过身份验证的配置文件,还是删除所有配置文件。

如果数据源支持事务,建议将所有删除操作包括在事务中,并且如果任何删除操作失败,您可以回滚事务并引发异常。

GetAllProfiles 方法

接受的输入包括一个 ProfileAuthenticationOption 值、一个指定页索引的整数、一个指定页大小的整数,以及对一个将设置为配置文件总计数的整数的引用。在应用程序名称与 ApplicationName 属性值匹配时,返回包含数据源中所有配置文件的 ProfileInfo 对象的 ProfileInfoCollection 。ProfileAuthenticationOption 参数指定是仅返回匿名配置文件,仅返回经过身份验证的配置文件,还是返回所有配置文件。

GetAllProfiles 方法返回的结果受页索引和页大小的值的约束。页大小值指定在 ProfileInfoCollection 中返回的 ProfileInfo 对象的最大数。页索引值指定要返回的结果页,其中 1 表示第一页。用于总记录数的参数是一个 out 参数(在 Visual Basic 中可以使用 ByRef),该参数被设置为配置文件的总数。例如,如果数据存储区中包含 13 个用于应用程序的配置文件,页索引值为 2,页大小为 5,则返回的 ProfileInfoCollection 包含第六到第十个配置文件。当该方法返回时,总记录数的值设置为 13。

GetAllInactiveProfiles 方法

接受的输入包括一个 ProfileAuthenticationOption 值、一个 DateTime 对象、一个指定页索引的整数、一个指定页大小的整数,以及对一个将设置为配置文件总计数的整数的引用。在上一活动日期小于或等于指定的 DateTime,且应用程序名称与 ApplicationName 属性值匹配时,返回包含数据源中所有配置文件的 ProfileInfo 对象的 ProfileInfoCollection。ProfileAuthenticationOption 参数指定是仅返回匿名配置文件,仅返回经过身份验证的配置文件,还是返回所有配置文件。

GetAllInactiveProfiles 方法返回的结果受页索引和页大小的值的约束。页大小值指定在 ProfileInfoCollection 中返回的 ProfileInfo 对象的最大数。页索引值指定要返回的结果页,其中 1 表示第一页。用于总记录数的参数是一个 out 参数(在 Visual Basic 中可以使用 ByRef),该参数被设置为配置文件的总数。例如,如果数据存储区中包含 13 个用于应用程序的配置文件,页索引值为 2,页大小为 5,则返回的 ProfileInfoCollection 包含第六到第十个配置文件。当该方法返回时,总记录数的值设置为 13。

FindProfilesByUserName 方法

接受的输入包括一个 ProfileAuthenticationOption 值、一个包含用户名的字符串、一个指定页索引的整数、一个指定页大小的整数,以及对一个将设置为配置文件总计数的整数的引用。在用户名与指定的用户名匹配,且应用程序名称与 ApplicationName 属性值匹配时,返回包含数据源中所有配置文件的 ProfileInfo 对象的 ProfileInfoCollection。ProfileAuthenticationOption 参数指定是仅返回匿名配置文件,仅返回经过身份验证的配置文件,还是返回所有配置文件。

如果数据源支持附加搜索功能(如通配符),则可以为用户名提供更为广泛的搜索功能。

FindProfilesByUserName 方法返回的结果受页索引和页大小的值的约束。页大小值指定在 ProfileInfoCollection 中返回的 ProfileInfo 对象的最大数。页索引值指定要返回的结果页,其中 1 表示第一页。用于总记录数的参数是一个 out 参数(在 Visual Basic 中可以使用 ByRef),该参数被设置为配置文件的总数。例如,如果数据存储区中包含 13 个用于应用程序的配置文件,页索引值为 2,页大小为 5,则返回的 ProfileInfoCollection 包含第六到第十个配置文件。当该方法返回时,总记录数的值设置为 13。

FindInactiveProfilesByUserName 方法

接受的输入包括一个 ProfileAuthenticationOption 值、一个包含用户名的字符串、一个 DateTime 对象、一个指定页索引的整数、一个指定页大小的整数,以及对一个将设置为配置文件总计数的整数的引用。在用户名与指定的用户名匹配,上一活动日期小于或等于指定的 DateTime,且应用程序名称与 ApplicationName 属性值匹配时,返回包含数据源中所有配置文件的 ProfileInfo 对象的 ProfileInfoCollection。ProfileAuthenticationOption 参数指定是仅返回匿名配置文件,仅返回经过身份验证的配置文件,还是返回所有配置文件。

如果数据源支持附加搜索功能(如通配符),则可以为用户名提供更为广泛的搜索功能。

FindInactiveProfilesByUserName 方法返回的结果受页索引和页大小的值的约束。页大小值指定在 ProfileInfoCollection 中返回的 ProfileInfo 对象的最大数。页索引值指定要返回的结果页,其中 1 表示第一页。用于总记录数的参数是一个 out 参数(在 Visual Basic 中可以使用 ByRef),该参数被设置为配置文件的总数。例如,如果数据存储区中包含 13 个用于应用程序的配置文件,页索引值为 2,页大小为 5,则返回的 ProfileInfoCollection 包含第六到第十个配置文件。当该方法返回时,总记录数的值设置为 13。

GetNumberOfInActiveProfiles 方法

接受 ProfileAuthenticationOption 值和 DateTime 对象作为输入,并在上一活动日期小于或等于指定的 DateTime,且应用程序名称与 ApplicationName 属性值匹配时,返回数据源中所有配置文件的计数。ProfileAuthenticationOption 参数指定是仅计算匿名配置文件,仅计算经过身份验证的配置文件,还是计算所有配置文件。

ApplicationName

因为配置文件提供程序为每个应用程序单独存储配置文件信息,所以必须确保数据架构包含应用程序名称,并确保查询和更新也包含应用程序名称。

如何:生成并运行配置文件提供程序示例

本节中的主题包含 ASP.NET 配置文件提供程序示例的代码。该示例提供程序使用用于 ODBC 的 .NET Framework 数据提供程序连接到 ODBC 数据源。该示例使用一个 Access 数据库作为其数据源。

此主题描述有关实现配置文件提供程序示例的详细信息,并描述如何生成示例以及如何配置 ASP.NET 应用程序以使用该示例提供程序。

Note注意

由于数据源支持不同的 SQL 语法,有些 SQL 命令可能可用于某数据源,而不能用于其他数据源。因此,即使您使用用于 ODBC 的 .NET Framework 数据提供程序或用于 OLEDB 的 .NET Framework 数据提供程序访问数据源,我们也建议专门为数据源创建一个配置文件提供程序。例如,可以创建单独的提供程序,如 SybaseProfileProvider、OracleProfileProvider 等。

创建提供程序数据库

提供程序被设计为对 Profiles、StockSymbols 和 ProfileData 这三个数据库表中的数据进行读写操作。示例配置文件提供程序支持两个配置文件属性:股票符号列表和邮政编码。

每个用户配置文件在数据库的 Profiles 表中进行唯一标识。该表包含配置文件信息,如应用程序名称和上次活动日期。Profiles 表包含一个自动增加的主键,该主键用于唯一标识每行,并将该表与数据库中包含配置文件属性值的其他表关联起来。

StockSymbols 表包含使用 StockSymbols 属性存储和检索的股票符号列表的值。由于一个用户配置文件可以跟踪任意多个股票符号,因此 StockSymbols 表与 Profiles 表具有一对多的关系。

ZipCode 属性值存储在 ProfileData 表中。ProfileData 表与 Profiles 表具有一对一的关系。

提供事件日志访问

如果示例提供程序在使用数据源时遇到异常,则该提供程序会将异常的详细信息写入 Windows 应用程序事件日志,而不是将异常返回给 ASP.NET 应用程序。这是一种安全措施,用于避免在 ASP.NET 应用程序中公开有关数据源的私有信息。

该示例提供程序指定了“OdbcProfileProvider”的事件 Source 。在 ASP.NET 应用程序能够成功写入应用程序事件日志之前,需要创建对事件日志的访问。

Note注意

如果不希望示例提供程序将异常写入事件日志,则可以在 Web.config 文件中的提供程序配置中将 writeExceptionsToEventLog 属性设置为 false。

提供对事件日志的访问

  • 使用 Regedit.exe 或其他 Windows 注册表编辑工具创建以下注册表项:

     CopyCode image复制代码
    HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Eventlog/Application/OdbcProfileProvider

生成示例提供程序

为了使用示例提供程序,必须使此代码对 Web 应用程序可用。有两种方法可以做到这一点:

  • 将示例提供程序源代码放入应用程序的 App_Code 文件夹中。在这种情况下,无需手动编译示例代码。

  • 编译该代码并将生成的程序集放入应用程序的 Bin 文件夹中,或对其进行强命名并放置到全局程序集缓存中。

测试示例提供程序(不手动编译)

  1. 如果应用程序没有一个名为 App_Code 的文件夹,请在应用程序根目录下创建此文件夹。

  2. 根据要使用的编程语言,在 App_Code 文件夹中创建一个名为 OdbcProfileProvider.vb 或 OdbcProfileProvider.cs 的文本文件。

    Note注意

    如果应用程序的 App_Code 目录中已经有源代码,则请使用与目录中现有代码相同的语言编写的示例提供程序版本。当首次请求 ASP.NET 应用程序中的某页面时,ASP.NET 将对该提供程序进行编译。

  3. 复制示例源代码(用适当的语言编写)并将其粘贴到相应的文本文件中,然后保存该文件。

     

编译示例提供程序

  1. 创建一个文件夹,用于存放自定义控件和相关类的源文件。

  2. 根据要使用的编程语言,在源代码文件夹中创建一个名为 OdbcProfileProvider.vb 或 OdbcProfileProvider.cs 的文本文件。

    Note注意

    如果还要手动编译该示例并将其程序集添加到 Bin 文件夹中,则不要将源代码放入 App_Code 文件夹中。否则,提供程序的类型将同时在于已编译的程序集和由 ASP.NET 从 App_Code 文件夹创建的动态生成的程序集中。当加载该提供程序时将创建不明确的引用,并且引用该类型的任何代码将生成编译器错误。

  3. 将每个示例的源代码复制并粘贴到相应的文本文件,然后保存该文件。

  4. 从源代码文件夹运行以下命令,以便将控件和相关类编译到程序集中。

    Visual Basic CopyCode image复制代码
    vbc /out:OdbcProfileProvider.dll /t:library OdbcProfileProvider.vb /r:System.Web.dll /r:System.Configuration.dll

     

    C# CopyCode image复制代码
    csc /out:OdbcProfileProvider.dll /t:library OdbcProfileProvider.cs /r:System.Web.dll /r:System.Configuration.dll

    /t:library 编译器选项让编译器创建一个库(而不是可执行程序集)。/out 选项可为程序集提供名称,而 /r 选项则可列出链接到您的程序集的那些程序集。

    Note注意

    如果无法执行编译器命令,则在运行命令之前必须将 .NET Framework 安装路径添加到 PATH 变量中。在 Windows 中,右击“我的电脑”,单击“属性”,单击“高级”选项卡,再单击“环境变量”按钮。在“系统变量”列表中,双击 Path 变量。在“变量值”文本框中,将一个分号 (;) 添加到文本框中的现有值的末尾,然后键入您的 .NET Framework 安装的路径。.NET Framework 通常安装在位于 /Microsoft.NET/Framework/versionNumber 的 Windows 安装文件夹中。

在 ASP.NET 应用程序中配置示例提供程序

若要在 ASP.NET Web 应用程序中使用示例提供程序,则必须对该应用程序进行配置以注册该提供程序。

此示例配置假定您的网站设置为使用 Forms 身份验证,并包含允许用户登录并创建用户标识的、名为 Login.aspx 的 ASP.NET 页。此示例配置还支持匿名身份验证,使用户无需登录即可使用。

配置应用程序以使用示例提供程序

  1. 创建一个名为 Login.aspx 的 ASP.NET 页并执行以下操作之一:

    • 如果应用程序已配置为使用 ASP.NET 成员资格,请向该页面添加一个 Login 控件。

    • 创建一个登录窗体并使用 Forms 身份验证对用户进行身份验证。有关详细信息,请参见如何:实现简单的 Forms 身份验证

      Note注意

      Forms 身份验证所需的配置元素在步骤 4 中进行说明。

  2. 在计算机上创建一个名为“OdbcProfile”的 DSN 并对其进行配置,使其包含到前面创建的 Access 数据库的连接信息。

  3. 如果 ASP.NET 应用程序没有一个名为 Web.config 的文本文件,则请在应用程序的根目录中创建此文件。

  4. 在 Web.config 文件中添加以下元素:

     CopyCode image复制代码
    <configuration>
      <connectionStrings>
        <add name="OdbcProfile" connectionString="DSN=OdbcProfile;" />
      </connectionStrings>
    
      <system.web>
        <authentication mode="Forms" >
          <forms loginUrl="login.aspx"
            name=".ASPXFORMSAUTH" />
        </authentication>
    
        <anonymousIdentification enabled="true" />
    
        <profile defaultProvider="OdbcProvider">
          <providers>
            <add
              name="OdbcProvider"
              type="Samples.AspNet.Profile.OdbcProfileProvider" 
              connectionStringName="OdbcProfile" /> 
          </providers>
    
          <properties>
            <add name="ZipCode" 
              allowAnonymous="true" />
            <add name="CityAndState" 
              provider="AspNetSqlProfileProvider" 
              allowAnonymous="true" />
            <add name="StockSymbols" 
              type="System.Collections.ArrayList" 
              allowAnonymous="true" />
          </properties>
        </profile>
      </system.web>
    </configuration>

配置文件提供程序实现示例

示例

Visual Basic CopyCode image复制代码
Imports System.Web.Profile
Imports System.Configuration.Provider
Imports System.Collections.Specialized
Imports System
Imports System.Data
Imports System.Data.Odbc
Imports System.Configuration
Imports System.Diagnostics
Imports System.Web
Imports System.Collections
Imports Microsoft.VisualBasic

'
'
' This provider works with the following schema for the table of user data.
' 
' CREATE TABLE Profiles
' (
'   UniqueID AutoIncrement NOT NULL PRIMARY KEY,
'   Username Text (255) NOT NULL,
'   ApplicationName Text (255) NOT NULL,
'   IsAnonymous YesNo, 
'   LastActivityDate DateTime,
'   LastUpdatedDate DateTime,
'     CONSTRAINT PKProfiles UNIQUE (Username, ApplicationName)
' )
' 
' CREATE TABLE StockSymbols
' (
'   UniqueID Integer,
'   StockSymbol Text (10),
'     CONSTRAINT FKProfiles1 FOREIGN KEY (UniqueID)
'       REFERENCES Profiles
' )
' 
' CREATE TABLE ProfileData
' (
'   UniqueID Integer,
'   ZipCode Text (10),
'     CONSTRAINT FKProfiles2 FOREIGN KEY (UniqueID)
'       REFERENCES Profiles
' )
' 
' 


Namespace Samples.AspNet.Profile

 Public NotInheritable Class OdbcProfileProvider
  Inherits ProfileProvider
  
  '
  ' Global connection string, generic exception message, event log info.
  '

  Private eventSource As String = "OdbcProfileProvider"
  Private eventLog As String = "Application"
  Private exceptionMessage As String = "An exception occurred. Please check the event log."
  Private connectionString As String


  '
  ' If false, exceptions are thrown to the caller. If true,
  ' exceptions are written to the event log.
  '

  Private pWriteExceptionsToEventLog As Boolean

  Public Property WriteExceptionsToEventLog As Boolean
    Get
      Return pWriteExceptionsToEventLog
    End Get
    Set
      pWriteExceptionsToEventLog = value
    End Set
  End Property


  '
  ' System.Configuration.Provider.ProviderBase.Initialize Method
  '

  Public Overrides Sub Initialize(name As String, config As NameValueCollection)

    '
    ' Initialize values from web.config.
    '

    If config Is Nothing Then _
      Throw New ArgumentNullException("config")

     If name Is Nothing OrElse name.Length = 0 Then _
      name = "OdbcProfileProvider"

    If String.IsNullOrEmpty(config("description")) Then
      config.Remove("description")
      config.Add("description", "Sample ODBC Profile provider")
    End If

    ' Initialize the abstract base class.
    MyBase.Initialize(name, config)


    If config("applicationName") Is Nothing OrElse config("applicationName").Trim() = "" Then
      pApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath
    Else
      pApplicationName = config("applicationName")
    End If


    '
    ' Initialize connection string.
    '

    Dim pConnectionStringSettings As ConnectionStringSettings = _
      ConfigurationManager.ConnectionStrings(config("connectionStringName"))

    If pConnectionStringSettings Is Nothing OrElse _
        pConnectionStringSettings.ConnectionString.Trim() = "" _
    Then
      Throw New ProviderException("Connection String cannot be blank.")
    End If

    connectionString = pConnectionStringSettings.ConnectionString
  End Sub


  '
  ' System.Configuration.SettingsProvider.ApplicationName
  '

  Private pApplicationName As String

  Public Overrides Property ApplicationName As String  
    Get
      Return pApplicationName
    End Get
    Set
      pApplicationName = value
    End Set
  End Property



  '
  ' System.Configuration.SettingsProvider methods.
  '

  '
  ' SettingsProvider.GetPropertyValues
  '

  Public Overrides Function GetPropertyValues(context As SettingsContext, _
                ppc As SettingsPropertyCollection) _
                As SettingsPropertyValueCollection

    ' The serializeAs attribute is ignored in this provider implementation.
   
    Dim username As String = CType(context("UserName"), String)
    Dim isAuthenticated As Boolean = CType(context("IsAuthenticated"), Boolean)

    Dim svc As SettingsPropertyValueCollection = New SettingsPropertyValueCollection()

    For Each prop As SettingsProperty In ppc    
      Dim pv As SettingsPropertyValue = New SettingsPropertyValue(prop)

      Select Case prop.Name      
        Case "StockSymbols"
          pv.PropertyValue = GetStockSymbols(username, isAuthenticated)
        Case "ZipCode"
          pv.PropertyValue = GetZipCode(username, isAuthenticated)
        Case Else
          Throw New ProviderException("Unsupported property.")
      End Select

      svc.Add(pv)
    Next

    UpdateActivityDates(username, isAuthenticated, True)

    Return svc

  End Function



  '
  ' SettingsProvider.SetPropertyValues
  '

  Public Overrides Sub SetPropertyValues(context As SettingsContext, _
                 ppvc As SettingsPropertyValueCollection)

    ' The serializeAs attribute is ignored in this provider implementation.
  
    Dim username As String = CType(context("UserName"), String)
    Dim isAuthenticated As Boolean = CType(context("IsAuthenticated"), Boolean)

    Dim uniqueID As Integer = GetUniqueID(username, isAuthenticated, False)
    If uniqueID = 0 Then uniqueID = CreateProfileForUser(username, isAuthenticated)

    For Each pv As SettingsPropertyValue In ppvc    
      Select Case pv.Property.Name      
        Case "StockSymbols"
          SetStockSymbols(uniqueID, CType(pv.PropertyValue, ArrayList))
        Case "ZipCode"
          SetZipCode(uniqueID, CType(pv.PropertyValue, String))
        Case Else
          Throw New ProviderException("Unsupported property.")
      End Select
    Next

    UpdateActivityDates(username, isAuthenticated, False)
  End Sub


  '
  ' UpdateActivityDates
  '   Updates LastActivityDate and LastUpdatedDate when profile properties are accessed
  ' by GetPropertyValues and SetPropertyValues. Specifying activityOnly as true will update
  ' only the LastActivityDate.
  '

  Private Sub UpdateActivityDates(username As String, isAuthenticated As Boolean, activityOnly As Boolean)
  
    Dim activityDate As DateTime = DateTime.Now

    Dim conn As OdbcConnection = New OdbcConnection(connectionString)
    Dim cmd As OdbcCommand = New OdbcCommand()
    cmd.Connection = conn

    If activityOnly Then    
      cmd.CommandText = "UPDATE Profiles Set LastActivityDate = ? " & _
            "WHERE Username = ? AND ApplicationName = ? AND IsAnonymous = ?"
      cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = activityDate
      cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username
      cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName
      cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = Not isAuthenticated
    Else
      cmd.CommandText = "UPDATE Profiles Set LastActivityDate = ?, LastUpdatedDate = ? " & _
            "WHERE Username = ? AND ApplicationName = ? AND IsAnonymous = ?"
      cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = activityDate
      cmd.Parameters.Add("@LastUpdatedDate", OdbcType.DateTime).Value = activityDate
      cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username
      cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName
      cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = Not isAuthenticated
    End If

    Try
      conn.Open()

      cmd.ExecuteNonQuery()
    Catch e As OdbcException
      If WriteExceptionsToEventLog Then
        WriteToEventLog(e, "UpdateActivityDates")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      conn.Close()
    End Try
  End Sub


  '
  ' GetStockSymbols
  ' Retrieves stock symbols from the database during the 
  ' call to GetPropertyValues.
  '

  Private Function GetStockSymbols(username As String, isAuthenticated As Boolean) As ArrayList
  
   Dim conn As OdbcConnection = New OdbcConnection(connectionString)
   Dim cmd As OdbcCommand = New OdbcCommand("SELECT StockSymbol FROM Profiles " & _
        "INNER JOIN StockSymbols ON Profiles.UniqueID = StockSymbols.UniqueID " & _
        "WHERE Username = ? AND ApplicationName = ? And IsAnonymous = ?", conn)
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName
    cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = Not isAuthenticated

    Dim outList As ArrayList = New ArrayList()

    Dim reader As OdbcDataReader = Nothing

    Try
      conn.Open()

      reader = cmd.ExecuteReader()

      Do While reader.Read()      
          outList.Add(reader.GetString(0))
      Loop
    Catch e As OdbcException
      If WriteExceptionsToEventLog Then      
        WriteToEventLog(e, "GetStockSymbols")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
          If Not reader Is Nothing Then reader.Close()

      conn.Close()
    End Try

    Return outList
  End Function



  '
  ' SetStockSymbols
  ' Inserts stock symbol values into the database during 
  ' the call to SetPropertyValues.
  '

  Private Sub SetStockSymbols(uniqueID As Integer, stocks As ArrayList)
    Dim conn As OdbcConnection = New OdbcConnection(connectionString)
    Dim cmd As OdbcCommand = New OdbcCommand("DELETE FROM StockSymbols WHERE UniqueID = ?", conn)
    cmd.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID

    Dim cmd2 As OdbcCommand =  New OdbcCommand("INSERT INTO StockSymbols (UniqueID, StockSymbol) " & _
                       "Values(?, ?)", conn)
    cmd2.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID
    cmd2.Parameters.Add("@StockSymbol", OdbcType.VarChar, 10)

    Dim tran As OdbcTransaction = Nothing

    Try
      conn.Open()
      tran = conn.BeginTransaction()
      cmd.Transaction = tran
      cmd2.Transaction = tran

      ' Delete any existing values.
      cmd.ExecuteNonQuery()

      For Each o As Object In Stocks
        cmd2.Parameters("@StockSymbol").Value = o.ToString()
        cmd2.ExecuteNonQuery()
      Next

      tran.Commit()
    Catch e As OdbcException
      Try
        tran.Rollback()
      Catch
      End Try

      If WriteExceptionsToEventLog Then
        WriteToEventLog(e, "SetStockSymbols")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      conn.Close()
    End Try
  End Sub


  '
  ' GetZipCode
  ' Retrieves ZipCode value from the database during 
  ' the call to GetPropertyValues.
  '

  Private Function GetZipCode(username As String, isAuthenticated As Boolean) As String
  
    Dim zipCode As String = ""

    Dim conn As OdbcConnection = New OdbcConnection(connectionString)
    Dim cmd As OdbcCommand = New OdbcCommand("SELECT ZipCode FROM Profiles " & _
          "INNER JOIN ProfileData ON Profiles.UniqueID = ProfileData.UniqueID " & _
          "WHERE Username = ? AND ApplicationName = ? And IsAnonymous = ?", conn)
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName
    cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = Not isAuthenticated

    Try
      conn.Open()
      
      zipCode = CType(cmd.ExecuteScalar(), String)
    Catch e As OdbcException
      If WriteExceptionsToEventLog Then
        WriteToEventLog(e, "GetZipCode")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      conn.Close()
    End Try

    Return zipCode
  End Function


  '
  ' SetZipCode
  ' Inserts the zip code value into the database during 
  ' the call to SetPropertyValues.
  '

  Private Sub SetZipCode(uniqueID As Integer, zipCode As String)
    If zipCode Is Nothing Then zipCode = String.Empty

    Dim conn As OdbcConnection = New OdbcConnection(connectionString)

    Dim cmd As OdbcCommand = New OdbcCommand("DELETE FROM ProfileData WHERE UniqueID = ?", conn)
    cmd.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID

    Dim cmd2 As OdbcCommand= New OdbcCommand("INSERT INTO ProfileData (UniqueID, ZipCode) " & _
                  "Values(?, ?)", conn)
    cmd2.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID
    cmd2.Parameters.Add("@ZipCode", OdbcType.VarChar, 10).Value = zipCode

    Dim tran As OdbcTransaction = Nothing

    Try
      conn.Open()
      tran = conn.BeginTransaction()
      cmd.Transaction = tran
      cmd2.Transaction = tran
      
      ' Delete any existing values.
      cmd.ExecuteNonQuery()
      cmd2.ExecuteNonQuery()

      tran.Commit()
    Catch e As OdbcException
      Try
        tran.Rollback()
      Catch
      End Try

      If WriteExceptionsToEventLog Then
        WriteToEventLog(e, "SetZipCode")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      conn.Close()
    End Try
  End Sub


  '
  ' GetUniqueID
  ' Retrieves the uniqueID from the database for 
  ' the current user and application.
  '

  Private Function GetUniqueID(username As String, isAuthenticated As Boolean, _
          ignoreAuthenticationType As Boolean) As Integer
  
    Dim conn As OdbcConnection = New OdbcConnection(connectionString)
    Dim cmd As OdbcCommand = New OdbcCommand("SELECT UniqueID FROM Profiles " & _
            "WHERE Username = ? AND ApplicationName = ?", conn)
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName

    If Not ignoreAuthenticationType Then
      cmd.CommandText &= " AND IsAnonymous = ?"
      cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = Not isAuthenticated
    End If

    Dim uniqueID As Integer = 0
    Dim reader As OdbcDataReader = Nothing

    Try
      conn.Open()

      reader = cmd.ExecuteReader(CommandBehavior.SingleRow)
      If reader.HasRows Then _
        uniqueID = reader.GetInt32(0)
    Catch e As OdbcException
      If WriteExceptionsToEventLog Then      
        WriteToEventLog(e, "GetUniqueID")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      If reader IsNot Nothing Then reader.Close()
      conn.Close()
    End Try

    Return uniqueID
  End Function


  '
  ' CreateProfileForUser
  ' If no user currently exists in the database, 
  ' a user record is created during the call to the 
  ' GetUniqueID Private method.
  '

  Private Function CreateProfileForUser(username As String, isAuthenticated As Boolean) As Integer
    ' Check for valid user name.

    If username Is Nothing Then _
      Throw New ArgumentNullException("username")
    If username.Length > 255 Then _
      Throw New ArgumentException("User name exceeds 255 characters.")
    If username.IndexOf(",") > 0 Then _
      Throw New ArgumentException("User name cannot contain a comma (,).")


    Dim conn As OdbcConnection = New OdbcConnection(connectionString)
    Dim cmd As OdbcCommand = New OdbcCommand("INSERT INTO Profiles (Username, " & _
            "ApplicationName, LastActivityDate, LastUpdatedDate, "  & _
            "IsAnonymous) Values(?, ?, ?, ?, ?)", conn)
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName
    cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = DateTime.Now
    cmd.Parameters.Add("@LastUpdatedDate", OdbcType.VarChar).Value = DateTime.Now
    cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = Not isAuthenticated

    Dim cmd2 As OdbcCommand = New OdbcCommand("SELECT @@IDENTITY", conn)

    Dim uniqueID As Integer = 0

    Try
      conn.Open()

      cmd.ExecuteNonQuery()

      uniqueID = CType(cmd2.ExecuteScalar(), Integer)
    Catch e As OdbcException
      If WriteExceptionsToEventLog Then      
        WriteToEventLog(e, "CreateProfileForUser")
        Throw New HttpException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      conn.Close()
    End Try

    Return uniqueID
  End Function


  '
  ' ProfileProvider.DeleteProfiles(ProfileInfoCollection)
  '

  Public Overrides Function DeleteProfiles(profiles As ProfileInfoCollection) As Integer
    Dim deleteCount As Integer = 0

    Dim conn As OdbcConnection  = New OdbcConnection(connectionString)
    Dim tran As OdbcTransaction = Nothing

    Try
      conn.Open()
      tran = conn.BeginTransaction()
    
      For Each p As ProfileInfo In profiles    
        If DeleteProfile(p.UserName, conn, tran) Then deleteCount += 1
      Next

      tran.Commit()
    Catch e As Exception
      Try
        tran.Rollback()
      Catch
      End Try

      If WriteExceptionsToEventLog Then
        WriteToEventLog(e, "DeleteProfiles(String())")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      conn.Close()
    End Try

    Return deleteCount
  End Function


  '
  ' ProfileProvider.DeleteProfiles(string())
  '

  Public Overrides Function DeleteProfiles(usernames As String()) As Integer
    Dim deleteCount As Integer = 0

    Dim conn As OdbcConnection  = New OdbcConnection(connectionString)
    Dim tran As OdbcTransaction = Nothing

    Try
      conn.Open()
      tran = conn.BeginTransaction()
    
      For Each user As String In usernames
        If (DeleteProfile(user, conn, tran)) Then deleteCount += 1
      Next

      tran.Commit()
    Catch e As Exception
      Try
        tran.Rollback()
      Catch
      End Try

      If WriteExceptionsToEventLog Then
        WriteToEventLog(e, "DeleteProfiles(String())")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      conn.Close()
    End Try

    Return deleteCount
  End Function



  '
  ' ProfileProvider.DeleteInactiveProfiles
  '

  Public Overrides Function DeleteInactiveProfiles( _
    authenticationOption As ProfileAuthenticationOption, _
    userInactiveSinceDate As DateTime) As Integer
  
    Dim conn As OdbcConnection = New OdbcConnection(connectionString)
    Dim cmd As OdbcCommand = New OdbcCommand("SELECT Username FROM Profiles " & _
            "WHERE ApplicationName = ? AND " & _
            " LastActivityDate <= ?", conn)
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName
    cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = userInactiveSinceDate

    Select Case authenticationOption    
      Case ProfileAuthenticationOption.Anonymous
        cmd.CommandText &= " AND IsAnonymous = ?"
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = True
      Case ProfileAuthenticationOption.Authenticated
        cmd.CommandText &= " AND IsAnonymous = ?"
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = False
    End Select

    Dim reader As OdbcDataReader = Nothing
    Dim usernames As String = ""

    Try
      conn.Open()

      reader = cmd.ExecuteReader()

      Do While reader.Read()      
        usernames &= reader.GetString(0) + ","
      Loop
    Catch e As OdbcException
      If WriteExceptionsToEventLog Then      
        WriteToEventLog(e, "DeleteInactiveProfiles")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      If Not reader Is Nothing Then reader.Close()

      conn.Close()
    End Try

    If usernames.Length > 0 Then    
      ' Remove trailing comma.
      usernames = usernames.Substring(0, usernames.Length - 1)
    End If


    ' Delete profiles.
    Return DeleteProfiles(usernames.Split(CChar(",")))
  End Function


  '
  '
  ' DeleteProfile
  ' Deletes profile data from the database for the specified user name. Expects an OdbcConnection and 
  ' an OdbcTransaction as it supports deleting multiple profiles in a transaction.
  '

  Private Function DeleteProfile(username As String, conn As OdbcConnection, tran As OdbcTransaction) As Boolean
    ' Check for valid user name.
    If username Is Nothing Then _
      Throw New ArgumentNullException("username")
    If username.Length > 255 Then _
      Throw New ArgumentException("User name exceeds 255 characters.")
    If username.IndexOf(",") > 0 Then _
      Throw New ArgumentException("User name cannot contain a comma (,).")

    Dim uniqueID As Integer = GetUniqueID(username, False, True)

    Dim cmd1 As OdbcCommand = New OdbcCommand("DELETE * FROM ProfileData WHERE UniqueID = ?", conn)
    cmd1.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID
    Dim cmd2 As OdbcCommand = New OdbcCommand("DELETE * FROM StockSymbols WHERE UniqueID = ?", conn)
    cmd2.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID
    Dim cmd3 As OdbcCommand = New OdbcCommand("DELETE * FROM Profiles WHERE UniqueID = ?", conn)
    cmd3.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID

    cmd1.Transaction = tran
    cmd2.Transaction = tran
    cmd3.Transaction = tran

    Dim numDeleted As Integer = 0

    ' Exceptions will be caught by the calling method.
    numDeleted += cmd1.ExecuteNonQuery()
    numDeleted += cmd2.ExecuteNonQuery()
    numDeleted += cmd3.ExecuteNonQuery()

    If numDeleted = 0 Then
      Return False
    Else
      Return True
    End If
  End Function


  '
  ' ProfileProvider.FindProfilesByUserName
  '

  Public Overrides Function FindProfilesByUserName( _
    authenticationOption As ProfileAuthenticationOption, _
    usernameToMatch As String, _
    pageIndex As Integer, _
    pageSize As Integer, _
    ByRef totalRecords As Integer) As ProfileInfoCollection
  
    CheckParameters(pageIndex, pageSize)

    Return GetProfileInfo(authenticationOption, usernameToMatch, Nothing, _ 
          pageIndex, pageSize, totalRecords)
  End Function


  '
  ' ProfileProvider.FindInactiveProfilesByUserName
  '

  Public Overrides Function FindInactiveProfilesByUserName( _
    authenticationOption As ProfileAuthenticationOption, _
    usernameToMatch As String, _
    userInactiveSinceDate As DateTime, _
    pageIndex As Integer, _
    pageSize As Integer, _
    ByRef totalRecords As Integer) As ProfileInfoCollection
  
    CheckParameters(pageIndex, pageSize)

    Return GetProfileInfo(authenticationOption, usernameToMatch, userInactiveSinceDate, _
          pageIndex, pageSize, totalRecords)
  End Function


  '
  ' ProfileProvider.GetAllProfiles
  '

  Public Overrides Function GetAllProfiles( _
    authenticationOption As ProfileAuthenticationOption, _
    pageIndex As Integer, _
    pageSize As Integer, _
    ByRef totalRecords As Integer) As ProfileInfoCollection
  
    CheckParameters(pageIndex, pageSize)

    Return GetProfileInfo(authenticationOption, Nothing, Nothing, _
          pageIndex, pageSize, totalRecords)
  End Function


  '
  ' ProfileProvider.GetAllInactiveProfiles
  '

  Public Overrides Function GetAllInactiveProfiles( _
    authenticationOption As ProfileAuthenticationOption, _
    userInactiveSinceDate As DateTime, _
    pageIndex As Integer, _
    pageSize As Integer, _
    ByRef totalRecords As Integer) As ProfileInfoCollection
  
    CheckParameters(pageIndex, pageSize)

    Return GetProfileInfo(authenticationOption, Nothing, userInactiveSinceDate, _ 
          pageIndex, pageSize, totalRecords)
  End Function



  '
  ' ProfileProvider.GetNumberOfInactiveProfiles
  '

  Public Overrides Function GetNumberOfInactiveProfiles( _
    authenticationOption As ProfileAuthenticationOption, _
    userInactiveSinceDate As DateTime) As Integer
  
    Dim inactiveProfiles As Integer = 0

    Dim profiles As ProfileInfoCollection =  _
      GetProfileInfo(authenticationOption, Nothing, userInactiveSinceDate, _
          0, 0, inactiveProfiles)

    Return inactiveProfiles
  End Function



  '
  ' CheckParameters
  ' Verifies input parameters for page size and page index. 
  ' Called by GetAllProfiles, GetAllInactiveProfiles, 
  ' FindProfilesByUserName, and FindInactiveProfilesByUserName.
  '

  Private Sub CheckParameters(pageIndex As Integer, pageSize As Integer)
    If pageIndex < 0 Then _
      Throw New ArgumentException("Page index must 0 or greater.")
    If pageSize < 1 Then _
      Throw New ArgumentException("Page size must be greater than 0.")
  End Sub


  '
  ' GetProfileInfo
  ' Retrieves a count of profiles and creates a 
  ' ProfileInfoCollection from the profile data in the database. 
  ' Called by GetAllProfiles, GetAllInactiveProfiles,
  ' FindProfilesByUserName, FindInactiveProfilesByUserName, 
  ' and GetNumberOfInactiveProfiles.
  ' Specifying a pageIndex of 0 retrieves a count of the results only.
  '

  Private Function GetProfileInfo( _
    authenticationOption As ProfileAuthenticationOption, _
    usernameToMatch As String, _
    userInactiveSinceDate As Object, _
    pageIndex As Integer, _
    pageSize As Integer, _
    ByRef totalRecords As Integer) As ProfileInfoCollection
  
    Dim conn As OdbcConnection = New OdbcConnection(connectionString)

    ' Command to retrieve the total count.

    Dim cmd As OdbcCommand = New OdbcCommand("SELECT COUNT(*) FROM Profiles WHERE ApplicationName = ? ", conn)
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName


    ' Command to retrieve the profile data.

    Dim cmd2 As OdbcCommand = New OdbcCommand("SELECT Username, LastActivityDate, LastUpdatedDate, " & _
            "IsAnonymous FROM Profiles WHERE ApplicationName = ? ", conn)
    cmd2.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName


    ' If searching for a user name to match, 
    ' add the command text and parameters.

    If Not usernameToMatch Is Nothing Then    
      cmd.CommandText &= " AND Username LIKE ? "
      cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = usernameToMatch
      
      cmd2.CommandText &= " AND Username LIKE ? "
      cmd2.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = usernameToMatch
    End If


    ' If searching for inactive profiles, 
    ' add the command text and parameters.

    If Not userInactiveSinceDate Is Nothing Then
      cmd.CommandText &= " AND LastActivityDate <= ? "
      cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = _
          CType(userInactiveSinceDate, DateTime)
      
      cmd2.CommandText &= " AND LastActivityDate <= ? "
      cmd2.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = _
          CType(userInactiveSinceDate, DateTime)
    End If


    ' If searching for a anonymous or authenticated profiles, add the command text 
    ' and parameters.

    Select Case authenticationOption    
      Case ProfileAuthenticationOption.Anonymous
        cmd.CommandText &= " AND IsAnonymous = ?"
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = True
        cmd2.CommandText &= " AND IsAnonymous = ?"
        cmd2.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = True
      Case ProfileAuthenticationOption.Authenticated
        cmd.CommandText &= " AND IsAnonymous = ?"
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = False
        cmd2.CommandText &= " AND IsAnonymous = ?"
        cmd2.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = False
    End Select


    ' Get the data.

    Dim reader As OdbcDataReader = Nothing
    Dim profiles As ProfileInfoCollection = New ProfileInfoCollection()

    Try
      conn.Open()
      ' Get the profile count.
      totalRecords = CType(cmd.ExecuteScalar(), Integer) 
      ' No profiles found.
      If totalRecords <= 0 Then Return profiles
      ' Count profiles only.
      If pageSize = 0 Then Return profiles


      reader = cmd2.ExecuteReader()

      Dim counter As Integer = 0
      Dim startIndex As Integer = pageSize * (pageIndex - 1)
      Dim endIndex As Integer = startIndex + pageSize - 1

      Do While reader.Read()      
        If counter >= startIndex Then        
          Dim p As ProfileInfo = GetProfileInfoFromReader(reader)
          profiles.Add(p)
        End If

        If counter >= endIndex Then cmd.Cancel()

        counter += 1
      Loop
    Catch e As OdbcException
      If WriteExceptionsToEventLog Then
        WriteToEventLog(e, "GetProfileInfo")
        Throw New ProviderException(exceptionMessage)
      Else
        Throw e
      End If
    Finally
      If Not reader Is Nothing Then reader.Close()

      conn.Close()
    End Try

    Return profiles
  End Function

  '
  ' GetProfileInfoFromReader
  ' Takes the current row from the OdbcDataReader
  ' and populates a ProfileInfo object from the values. 
  '

  Private Function GetProfileInfoFromReader(reader As OdbcDataReader) As ProfileInfo 
  
    Dim username As String = reader.GetString(0)

    Dim lastActivityDate As DateTime = New DateTime()
    If Not reader.GetValue(1) Is DBNull.Value Then _
      lastActivityDate = reader.GetDateTime(1)

    Dim lastUpdatedDate As DateTime = New DateTime()
    If Not reader.GetValue(2) Is DBNull.Value Then _
      lastUpdatedDate = reader.GetDateTime(2)

    Dim isAnonymous As Boolean = reader.GetBoolean(3)
      
    ' ProfileInfo.Size not currently implemented.
    Dim p As ProfileInfo = New ProfileInfo(username, _
        isAnonymous,lastActivityDate,lastUpdatedDate, 0)

    Return p
  End Function


  '
  ' WriteToEventLog
  ' A helper function that writes exception detail to the
  ' event log. Exceptions are written to the event log as
  ' a security measure to prevent Private database details 
  ' from being returned to the browser. If a method does not 
  ' return a status or Boolean value indicating whether the action 
  ' succeeded or failed, the caller also throws a generic exception.
  '

  Private Sub WriteToEventLog(e As Exception, action As String)
  
    Dim log As EventLog = New EventLog()
    log.Source = eventSource
    log.Log = eventLog

    Dim message As String = "An exception occurred communicating with the data source." & vbCrLf & vbCrLf
    message &= "Action: " & action & vbCrLf & vbCrLf
    message &= "Exception: " & e.ToString()

    log.WriteEnTry(message)
  End Sub
 End Class
End Namespace
C# CopyCode image复制代码
using System.Web.Profile;
using System.Configuration.Provider;
using System.Collections.Specialized;
using System;
using System.Data;
using System.Data.Odbc;
using System.Configuration;
using System.Diagnostics;
using System.Web;
using System.Collections;

/*

This provider works with the following schema for the table of user data.

CREATE TABLE Profiles
(
  UniqueID AutoIncrement NOT NULL PRIMARY KEY,
  Username Text (255) NOT NULL,
  ApplicationName Text (255) NOT NULL,
  IsAnonymous YesNo, 
  LastActivityDate DateTime,
  LastUpdatedDate DateTime,
    CONSTRAINT PKProfiles UNIQUE (Username, ApplicationName)
)

CREATE TABLE StockSymbols
(
  UniqueID Integer,
  StockSymbol Text (10),
    CONSTRAINT FKProfiles1 FOREIGN KEY (UniqueID)
      REFERENCES Profiles
)

CREATE TABLE ProfileData
(
  UniqueID Integer,
  ZipCode Text (10),
    CONSTRAINT FKProfiles2 FOREIGN KEY (UniqueID)
      REFERENCES Profiles
)

*/


namespace Samples.AspNet.Profile
{

 public sealed class OdbcProfileProvider: ProfileProvider
 {
  //
  // Global connection string, generic exception message, event log info.
  //

  private string eventSource = "OdbcProfileProvider";
  private string eventLog = "Application";
  private string exceptionMessage = "An exception occurred. Please check the event log.";
  private string connectionString;


  //
  // If false, exceptions are thrown to the caller. If true,
  // exceptions are written to the event log.
  //

  private bool pWriteExceptionsToEventLog;

  public bool WriteExceptionsToEventLog
  {
    get { return pWriteExceptionsToEventLog; }
    set { pWriteExceptionsToEventLog = value; }
  }



  //
  // System.Configuration.Provider.ProviderBase.Initialize Method
  //

  public override void Initialize(string name, NameValueCollection config)
  {

    //
    // Initialize values from web.config.
    //

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

    if (name == null || name.Length == 0)
      name = "OdbcProfileProvider";

    if (String.IsNullOrEmpty(config["description"]))
    {
      config.Remove("description");
      config.Add("description", "Sample ODBC Profile provider");
    }

    // Initialize the abstract base class.
    base.Initialize(name, config);


    if (config["applicationName"] == null || config["applicationName"].Trim() == "")
    {
      pApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
    }
    else
    {
      pApplicationName = config["applicationName"];
    }


    //
    // Initialize connection string.
    //

    ConnectionStringSettings pConnectionStringSettings = ConfigurationManager.
        ConnectionStrings[config["connectionStringName"]];

    if (pConnectionStringSettings == null || 
        pConnectionStringSettings.ConnectionString.Trim() == "")
    {
      throw new ProviderException("Connection string cannot be blank.");
    }

    connectionString = pConnectionStringSettings.ConnectionString;
  }


  //
  // System.Configuration.SettingsProvider.ApplicationName
  //

  private string pApplicationName;

  public override string ApplicationName
  {
    get { return pApplicationName; }
    set { pApplicationName = value; }
  } 



  //
  // System.Configuration.SettingsProvider methods.
  //

  //
  // SettingsProvider.GetPropertyValues
  //

  public override SettingsPropertyValueCollection 
        GetPropertyValues(SettingsContext context,
              SettingsPropertyCollection ppc)
  {
    string username = (string)context["UserName"];
    bool isAuthenticated = (bool)context["IsAuthenticated"];

    // The serializeAs attribute is ignored in this provider implementation.

    SettingsPropertyValueCollection svc = 
        new SettingsPropertyValueCollection();

    foreach (SettingsProperty prop in ppc)
    {
      SettingsPropertyValue pv = new SettingsPropertyValue(prop);

      switch (prop.Name)
      {
        case "StockSymbols":
          pv.PropertyValue = GetStockSymbols(username, isAuthenticated);
          break;
        case "ZipCode":
          pv.PropertyValue = GetZipCode(username, isAuthenticated);
          break;
        default:
          throw new ProviderException("Unsupported property.");
      }

      svc.Add(pv);
    }

    UpdateActivityDates(username, isAuthenticated, true);

    return svc;
  }



  //
  // SettingsProvider.SetPropertyValues
  //

  public override void SetPropertyValues(SettingsContext context,
                 SettingsPropertyValueCollection ppvc)
  {
    // The serializeAs attribute is ignored in this provider implementation.

    string username = (string)context["UserName"];
    bool isAuthenticated = (bool)context["IsAuthenticated"];
    int uniqueID = GetUniqueID(username, isAuthenticated, false);
    if (uniqueID == 0)
      uniqueID = CreateProfileForUser(username, isAuthenticated);

    foreach (SettingsPropertyValue pv in ppvc)
    {
      switch (pv.Property.Name)
      {
        case "StockSymbols":
          SetStockSymbols(uniqueID, (ArrayList)pv.PropertyValue);
          break;
        case "ZipCode":
          SetZipCode(uniqueID, (string)pv.PropertyValue);
          break;
        default:
          throw new ProviderException("Unsupported property.");
      }
    }

    UpdateActivityDates(username, isAuthenticated, false);
  }


  //
  // UpdateActivityDates
  // Updates the LastActivityDate and LastUpdatedDate values 
  // when profile properties are accessed by the
  // GetPropertyValues and SetPropertyValues methods. 
  // Passing true as the activityOnly parameter will update
  // only the LastActivityDate.
  //

  private void UpdateActivityDates(string username, bool isAuthenticated, bool activityOnly)
  {
    DateTime activityDate = DateTime.Now;

    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand();
    cmd.Connection = conn;

    if (activityOnly)
    {
      cmd.CommandText = "UPDATE Profiles Set LastActivityDate = ? " + 
            "WHERE Username = ? AND ApplicationName = ? AND IsAnonymous = ?";
      cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = activityDate;
      cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
      cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;
      cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = !isAuthenticated;
      
    }
    else
    {
      cmd.CommandText = "UPDATE Profiles Set LastActivityDate = ?, LastUpdatedDate = ? " + 
            "WHERE Username = ? AND ApplicationName = ? AND IsAnonymous = ?";
      cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = activityDate;
      cmd.Parameters.Add("@LastUpdatedDate", OdbcType.DateTime).Value = activityDate;
      cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
      cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;
      cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = !isAuthenticated;
    }

    try
    {
      conn.Open();

      cmd.ExecuteNonQuery();
    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "UpdateActivityDates");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      conn.Close();
    }
  }


  //
  // GetStockSymbols
  //   Retrieves stock symbols from the database during the call to GetPropertyValues.
  //

  private ArrayList GetStockSymbols(string username, bool isAuthenticated)
  {
    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new 
      OdbcCommand("SELECT StockSymbol FROM Profiles " +
        "INNER JOIN StockSymbols ON Profiles.UniqueID = StockSymbols.UniqueID " +
        "WHERE Username = ? AND ApplicationName = ? And IsAnonymous = ?", conn);
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;
    cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = !isAuthenticated;

    ArrayList outList = new ArrayList();

    OdbcDataReader reader = null;

    try
    {
      conn.Open();

      reader = cmd.ExecuteReader();

      while (reader.Read())
      {
          outList.Add(reader.GetString(0));
      }
    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "GetStockSymbols");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      if (reader != null) { reader.Close(); }

      conn.Close();
    }

    return outList;
  }



  //
  // SetStockSymbols
  // Inserts stock symbol values into the database during 
  // the call to SetPropertyValues.
  //

  private void SetStockSymbols(int uniqueID, ArrayList stocks)
  {
    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand("DELETE FROM StockSymbols WHERE UniqueID = ?", conn);
    cmd.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID;

    OdbcCommand cmd2 =  new OdbcCommand("INSERT INTO StockSymbols (UniqueID, StockSymbol) " +
               "Values(?, ?)", conn);
    cmd2.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID;
    cmd2.Parameters.Add("@StockSymbol", OdbcType.VarChar, 10);

    OdbcTransaction tran = null;

    try
    {
      conn.Open();
      tran = conn.BeginTransaction();
      cmd.Transaction = tran;
      cmd2.Transaction = tran;

      // Delete any existing values;
      cmd.ExecuteNonQuery();    
      foreach (object o in stocks)
      {
        cmd2.Parameters["@StockSymbol"].Value = o.ToString();
        cmd2.ExecuteNonQuery();
      }

      tran.Commit();
    }
    catch (OdbcException e)
    {
      try
      {
        tran.Rollback();
      }
      catch
      {
      }

      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "SetStockSymbols");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      conn.Close();
    }
  }

  //
  // GetZipCode
  // Retrieves ZipCode value from the database during 
  // the call to GetPropertyValues.
  //

  private string GetZipCode(string username, bool isAuthenticated)
  {
    string zipCode = "";

    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand("SELECT ZipCode FROM Profiles " +
          "INNER JOIN ProfileData ON Profiles.UniqueID = ProfileData.UniqueID " +
          "WHERE Username = ? AND ApplicationName = ? And IsAnonymous = ?", conn);
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;
    cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = !isAuthenticated;

    try
    {
      conn.Open();
      
      zipCode = (string)cmd.ExecuteScalar();
    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "GetZipCode");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      conn.Close();
    }

    return zipCode;
  }

  //
  // SetZipCode
  // Inserts the zip code value into the database during 
  // the call to SetPropertyValues.
  //

  private void SetZipCode(int uniqueID, string zipCode)
  {
    if (zipCode == null) { zipCode = String.Empty; }

    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand("DELETE FROM ProfileData WHERE UniqueID = ?", conn);
    cmd.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID;

    OdbcCommand cmd2 = new OdbcCommand("INSERT INTO ProfileData (UniqueID, ZipCode) " +
               "Values(?, ?)", conn);
    cmd2.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID;
    cmd2.Parameters.Add("@ZipCode", OdbcType.VarChar, 10).Value = zipCode;

    OdbcTransaction tran = null;

    try
    {
      conn.Open();
      tran = conn.BeginTransaction();
      cmd.Transaction = tran;
      cmd2.Transaction = tran;
      
      // Delete any existing values.
      cmd.ExecuteNonQuery();    
      cmd2.ExecuteNonQuery();

      tran.Commit();
    }
    catch (OdbcException e)
    {
      try
      {
        tran.Rollback();
      }
      catch
      {
      }

      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "SetZipCode");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      conn.Close();
    }
  }


  //
  // GetUniqueID
  //   Retrieves the uniqueID from the database for the current user and application.
  //

  private int GetUniqueID(string username, bool isAuthenticated, bool ignoreAuthenticationType)
  {
    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand("SELECT UniqueID FROM Profiles " +
            "WHERE Username = ? AND ApplicationName = ?", conn);
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;

    if (!ignoreAuthenticationType)
    {
      cmd.CommandText += " AND IsAnonymous = ?";
      cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = !isAuthenticated;
    }

    int uniqueID = 0;
    OdbcDataReader reader = null;

    try
    {
      conn.Open();

      reader = cmd.ExecuteReader(CommandBehavior.SingleRow);
      if (reader.HasRows)
        uniqueID = reader.GetInt32(0);
    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "GetUniqueID");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      if (reader != null) { reader.Close(); }
      conn.Close();
    }

    return uniqueID;
  }


  //
  // CreateProfileForUser
  // If no user currently exists in the database, 
  // a user record is created during
  // the call to the GetUniqueID private method.
  //

  private int CreateProfileForUser(string username, bool isAuthenticated)
  {
    // Check for valid user name.

    if (username == null)
      throw new ArgumentNullException("User name cannot be null.");
    if (username.Length > 255)
      throw new ArgumentException("User name exceeds 255 characters.");
    if (username.IndexOf(",") > 0)
      throw new ArgumentException("User name cannot contain a comma (,).");


    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand("INSERT INTO Profiles (Username, " +
            "ApplicationName, LastActivityDate, LastUpdatedDate, " +
            "IsAnonymous) Values(?, ?, ?, ?, ?)", conn);
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;
    cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = DateTime.Now;
    cmd.Parameters.Add("@LastUpdatedDate", OdbcType.VarChar).Value = DateTime.Now;
    cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = !isAuthenticated;

    OdbcCommand cmd2 = new OdbcCommand("SELECT @@IDENTITY", conn);

    int uniqueID = 0;

    try
    {
      conn.Open();

      cmd.ExecuteNonQuery();

      uniqueID = (int)cmd2.ExecuteScalar();
    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "CreateProfileForUser");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      conn.Close();
    }

    return uniqueID;
  }


  //
  // ProfileProvider.DeleteProfiles(ProfileInfoCollection)
  //

  public override int DeleteProfiles(ProfileInfoCollection profiles)
  {
    int deleteCount = 0;

    OdbcConnection  conn = new OdbcConnection(connectionString);
    OdbcTransaction tran = null;

    try
    {
      conn.Open();
      tran = conn.BeginTransaction();
    
      foreach (ProfileInfo p in profiles)
      {
        if (DeleteProfile(p.UserName, conn, tran))
          deleteCount++;
      }

      tran.Commit();
    }
    catch (Exception e)
    {
      try
      {
        tran.Rollback();
      }
      catch
      {
      }

      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "DeleteProfiles(ProfileInfoCollection)");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }  
    }
    finally
    {
      conn.Close();
    }

    return deleteCount;
  }


  //
  // ProfileProvider.DeleteProfiles(string[])
  //

  public override int DeleteProfiles(string[] usernames)
  {
    int deleteCount = 0;

    OdbcConnection  conn = new OdbcConnection(connectionString);
    OdbcTransaction tran = null;

    try
    {
      conn.Open();
      tran = conn.BeginTransaction();
    
      foreach (string user in usernames)
      {
        if (DeleteProfile(user, conn, tran))
          deleteCount++;
      }

      tran.Commit();
    }
    catch (Exception e)
    {
      try
      {
        tran.Rollback();
      }
      catch
      {
      }

      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "DeleteProfiles(String())");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }  
    }
    finally
    {
      conn.Close();
    }

    return deleteCount;
  }



  //
  // ProfileProvider.DeleteInactiveProfiles
  //

  public override int DeleteInactiveProfiles(
    ProfileAuthenticationOption authenticationOption,
    DateTime userInactiveSinceDate)
  {
    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand("SELECT Username FROM Profiles " +
            "WHERE ApplicationName = ? AND " +
            " LastActivityDate <= ?", conn);
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;
    cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = userInactiveSinceDate;

    switch (authenticationOption)
    {
      case ProfileAuthenticationOption.Anonymous:
        cmd.CommandText += " AND IsAnonymous = ?";
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = true;
        break;
      case ProfileAuthenticationOption.Authenticated:
        cmd.CommandText += " AND IsAnonymous = ?";
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = false;
        break;
      default:
        break;
    }

    OdbcDataReader reader = null;
    string usernames = "";

    try
    {
      conn.Open();

      reader = cmd.ExecuteReader();

      while (reader.Read())
      {
        usernames += reader.GetString(0) + ",";
      }
    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "DeleteInactiveProfiles");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      if (reader != null) { reader.Close(); }

      conn.Close();
    }

    if (usernames.Length > 0)
    {
      // Remove trailing comma.
      usernames = usernames.Substring(0, usernames.Length - 1);
    }


    // Delete profiles.

    return DeleteProfiles(usernames.Split(','));
  }


  //
  // DeleteProfile
  // Deletes profile data from the database for the 
  // specified user name.
  //

  private bool DeleteProfile(string username, OdbcConnection conn, OdbcTransaction tran)
  {
    // Check for valid user name.
    if (username == null)
      throw new ArgumentNullException("User name cannot be null.");
    if (username.Length > 255)
      throw new ArgumentException("User name exceeds 255 characters.");
    if (username.IndexOf(",") > 0)
      throw new ArgumentException("User name cannot contain a comma (,).");


    int uniqueID = GetUniqueID(username, false, true);

    OdbcCommand cmd1 = new OdbcCommand("DELETE * FROM ProfileData WHERE UniqueID = ?", conn);
    cmd1.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID;
    OdbcCommand cmd2 = new OdbcCommand("DELETE * FROM StockSymbols WHERE UniqueID = ?", conn);
    cmd2.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID;
    OdbcCommand cmd3 = new OdbcCommand("DELETE * FROM Profiles WHERE UniqueID = ?", conn);
    cmd3.Parameters.Add("@UniqueID", OdbcType.Int).Value = uniqueID;

    cmd1.Transaction = tran;
    cmd2.Transaction = tran;
    cmd3.Transaction = tran;

    int numDeleted = 0;

    // Exceptions will be caught by the calling method.
    numDeleted += cmd1.ExecuteNonQuery();
    numDeleted += cmd2.ExecuteNonQuery();
    numDeleted += cmd3.ExecuteNonQuery();

    if (numDeleted == 0)
      return false;
    else
      return true;
  }


  //
  // ProfileProvider.FindProfilesByUserName
  //

  public override ProfileInfoCollection FindProfilesByUserName(
    ProfileAuthenticationOption authenticationOption,
    string usernameToMatch,
    int pageIndex,
    int pageSize,
    out int totalRecords)
  {
    CheckParameters(pageIndex, pageSize);

    return GetProfileInfo(authenticationOption, usernameToMatch, 
        null, pageIndex, pageSize, out totalRecords);
  }


  //
  // ProfileProvider.FindInactiveProfilesByUserName
  //

  public override ProfileInfoCollection FindInactiveProfilesByUserName(
    ProfileAuthenticationOption authenticationOption,
    string usernameToMatch,
    DateTime userInactiveSinceDate,
    int pageIndex,
    int pageSize,
    out int totalRecords)
  {
    CheckParameters(pageIndex, pageSize);

    return GetProfileInfo(authenticationOption, usernameToMatch, userInactiveSinceDate, 
          pageIndex, pageSize, out totalRecords);
  }


  //
  // ProfileProvider.GetAllProfiles
  //

  public override ProfileInfoCollection GetAllProfiles(
    ProfileAuthenticationOption authenticationOption,
    int pageIndex,
    int pageSize,
    out int totalRecords)
  {
    CheckParameters(pageIndex, pageSize);

    return GetProfileInfo(authenticationOption, null, null, 
          pageIndex, pageSize, out totalRecords);
  }


  //
  // ProfileProvider.GetAllInactiveProfiles
  //

  public override ProfileInfoCollection GetAllInactiveProfiles(
    ProfileAuthenticationOption authenticationOption,
    DateTime userInactiveSinceDate,
    int pageIndex,
    int pageSize,
    out int totalRecords)
  {
    CheckParameters(pageIndex, pageSize);

    return GetProfileInfo(authenticationOption, null, userInactiveSinceDate, 
          pageIndex, pageSize, out totalRecords);
  }



  //
  // ProfileProvider.GetNumberOfInactiveProfiles
  //

  public override int GetNumberOfInactiveProfiles(
    ProfileAuthenticationOption authenticationOption,
    DateTime userInactiveSinceDate)
  {
    int inactiveProfiles = 0;

    ProfileInfoCollection profiles = 
      GetProfileInfo(authenticationOption, null, userInactiveSinceDate, 
          0, 0, out inactiveProfiles);

    return inactiveProfiles;
  }



  //
  // CheckParameters
  // Verifies input parameters for page size and page index. 
  // Called by GetAllProfiles, GetAllInactiveProfiles, 
  // FindProfilesByUserName, and FindInactiveProfilesByUserName.
  //

  private void CheckParameters(int pageIndex, int pageSize)
  {
    if (pageIndex < 0)
      throw new ArgumentException("Page index must 0 or greater.");
    if (pageSize < 1)
      throw new ArgumentException("Page size must be greater than 0.");
  }


  //
  // GetProfileInfo
  // Retrieves a count of profiles and creates a 
  // ProfileInfoCollection from the profile data in the 
  // database. Called by GetAllProfiles, GetAllInactiveProfiles,
  // FindProfilesByUserName, FindInactiveProfilesByUserName, 
  // and GetNumberOfInactiveProfiles.
  // Specifying a pageIndex of 0 retrieves a count of the results only.
  //

  private ProfileInfoCollection GetProfileInfo(
    ProfileAuthenticationOption authenticationOption,
    string usernameToMatch,
    object userInactiveSinceDate,
    int pageIndex,
    int pageSize,
    out int totalRecords)
  {
    OdbcConnection conn = new OdbcConnection(connectionString);


    // Command to retrieve the total count.

    OdbcCommand cmd = new OdbcCommand("SELECT COUNT(*) FROM Profiles WHERE ApplicationName = ? ", conn);
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;


    // Command to retrieve the profile data.

    OdbcCommand cmd2 = new OdbcCommand("SELECT Username, LastActivityDate, LastUpdatedDate, " +
            "IsAnonymous FROM Profiles WHERE ApplicationName = ? ", conn);
    cmd2.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;


    // If searching for a user name to match, add the command text and parameters.

    if (usernameToMatch != null)
    {
      cmd.CommandText += " AND Username LIKE ? ";
      cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = usernameToMatch;
      
      cmd2.CommandText += " AND Username LIKE ? ";
      cmd2.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = usernameToMatch;
    }


    // If searching for inactive profiles, 
    // add the command text and parameters.

    if (userInactiveSinceDate != null)
    {
      cmd.CommandText += " AND LastActivityDate <= ? ";
      cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = (DateTime)userInactiveSinceDate;
      
      cmd2.CommandText += " AND LastActivityDate <= ? ";
      cmd2.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = (DateTime)userInactiveSinceDate;
    }


    // If searching for a anonymous or authenticated profiles,    
    // add the command text and parameters.

    switch (authenticationOption)
    {
      case ProfileAuthenticationOption.Anonymous:
        cmd.CommandText += " AND IsAnonymous = ?";
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = true;
        cmd2.CommandText += " AND IsAnonymous = ?";
        cmd2.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = true;
        break;
      case ProfileAuthenticationOption.Authenticated:
        cmd.CommandText += " AND IsAnonymous = ?";
        cmd.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = false;
        cmd2.CommandText += " AND IsAnonymous = ?";
        cmd2.Parameters.Add("@IsAnonymous", OdbcType.Bit).Value = false;
        break;
      default:
        break;
    }


    // Get the data.

    OdbcDataReader reader = null;
    ProfileInfoCollection profiles = new ProfileInfoCollection();

    try
    {
      conn.Open();
      // Get the profile count.
      totalRecords = (int)cmd.ExecuteScalar();  
      // No profiles found.
      if (totalRecords <= 0) { return profiles; }  
      // Count profiles only.
      if (pageSize == 0) { return profiles; }    

      reader = cmd2.ExecuteReader();

      int counter = 0;
      int startIndex = pageSize * (pageIndex - 1);
      int endIndex = startIndex + pageSize - 1;

      while (reader.Read())
      {
        if (counter >= startIndex)
        {
          ProfileInfo p = GetProfileInfoFromReader(reader);
          profiles.Add(p);
        }

        if (counter >= endIndex)
        {
          cmd.Cancel();
          break;
        }

        counter++;
      }

    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "GetProfileInfo");
        throw new ProviderException(exceptionMessage);
      }
      else
      {
        throw e;
      }
    }
    finally
    {
      if (reader != null) { reader.Close(); }

      conn.Close();
    }

    return profiles;
  }

  //
  // GetProfileInfoFromReader
  //  Takes the current row from the OdbcDataReader
  // and populates a ProfileInfo object from the values. 
  //

  private ProfileInfo GetProfileInfoFromReader(OdbcDataReader reader)
  {
    string username = reader.GetString(0);

    DateTime lastActivityDate = new DateTime();
    if (reader.GetValue(1) != DBNull.Value)
      lastActivityDate = reader.GetDateTime(1);

    DateTime lastUpdatedDate = new DateTime();
    if (reader.GetValue(2) != DBNull.Value)
      lastUpdatedDate = reader.GetDateTime(2);

    bool isAnonymous = reader.GetBoolean(3);
      
    // ProfileInfo.Size not currently implemented.
    ProfileInfo p = new ProfileInfo(username,
        isAnonymous, lastActivityDate, lastUpdatedDate,0);  

    return p;
  }


  //
  // WriteToEventLog
  // A helper function that writes exception detail to the event 
  // log. Exceptions are written to the event log as a security 
  // measure to prevent private database details from being 
  // returned to the browser. If a method does not return a 
  // status or Boolean value indicating whether the action succeeded 
  // or failed, the caller also throws a generic exception.
  //

  private void WriteToEventLog(Exception e, string action)
  {
    EventLog log = new EventLog();
    log.Source = eventSource;
    log.Log = eventLog;

    string message = "An exception occurred while communicating with the data source./n/n";
    message += "Action: " + action + "/n/n";
    message += "Exception: " + e.ToString();

    log.WriteEntry(message);
  }
 }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值