65.9K
CodeProject 正在变化。 阅读更多。
Home

创建日期处理库和 WCF 服务以进行日期和年龄处理

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.60/5 (3投票s)

2007 年 7 月 19 日

CPOL

7分钟阅读

viewsIcon

39997

downloadIcon

218

这是三期项目中的第二期。本期项目访问第一期创建和加载的数据库,以公开一个常用的业务日期函数 API。

1. 引言/目的

本项目的目的是以一种易于使用的方式,为在 .NET 2.0 和 SQL Server 2005 平台(或 WCF 版本为 .NET 3.0)下运行的定制业务应用程序公开常用的日期功能。在本系列的第一期中,我们实现了一个子系统来加载包含所有预编译逻辑的 SQL Server 数据库表,以便能够针对数据库执行调用来执行常用的逻辑日期函数。下面的方法当然可以在仅包含小型节假日表的系统中创建,而没有日期差数据 - 在这种情况下,应用程序代码可以逐例计算天数。但由于第一期已经完成了大量工作,因此这里大多数函数的实现都非常简单,因为大多数方法所依据的逻辑已经预编译到数据库数据中了。

设置说明

  • 要执行下载的代码,您必须下载、设置并运行第一期的项目:构建营业日和节假日数据库子系统
  • zip 下载中包含两个解决方案。第二个解决方案需要 .NET 3.0 Framework 以及 .NET 3.0 的 Windows SDK 和 Visual Studio Orcas beta1 或更高版本。在下载文件中的 _readme.txt_ 文件(每个解决方案一个)中可以找到更多详细信息。

2. 日期方法

以下是 API 公开的方法列表

  • BusinessDaysUntil (date1, date2)
  • IsBusinessDate(date1)
  • IsBusinessHolidayDate(date1)
  • IsFederalHolidayDate(date1)
  • IsHolidayDate(date1)
  • IsWeekendDate(date1)
  • NextBusinessDate (date1)
  • NextNonBusinessDate(date1)
  • NextWeekendDate(date1)
  • PreviousBusinessDate(date1)
  • PreviousNonBusinessDate(date1)
  • PreviousWeekendDate(date1)

更多详细信息可在源代码接口和方法头中找到。

3. 访问 API - 关于设计的思考

访问此 API 的最佳或最简单的方法是什么?答案实际上取决于您正在构建什么以及构建何种类型的应用程序。在 SOA 上下文中,通过 WCF 服务公开和使用这些方法(如我在第二个下载中所示)非常有意义。WCF 服务在这里效果很好,因为应用程序需要关心的唯一持久状态是数据库中的这些日期方法。但在许多客户端-服务器情况下,只需将 _DateAgingData.DLL_ 库添加到需要它的每个应用程序程序集中,可能是一种更简单、更可取的选择。您可以清楚地看到 WCF 解决方案包含更多的代码行以及配置,如果可以避免,这并不是好事。

4. DateAgingApp1 解决方案 - 简单的 .NET 程序集访问

第一层:针对 DateAgingDemo 数据库的 ADO.NET 数据访问

我考虑为每个方法创建存储过程,就像我在第一部分中为数据加载部分所做的那样(在这种情况下有充分的理由)——但我认为对于这些简单的 SQL 查询,直接从 C# 数据库执行 SQL 是更好、更易于测试的方法。我绝对认为,如果这些方法要由 WCF 服务公开和访问,存储过程层对我来说没有任何实际的好处。

这里唯一稍微复杂的方法是 _BusinessDaysUntil()_。我需要包含一些额外的逻辑,因为数据库注册表表中存储的日期间隔大于 _MAXDAYSDIFF_ 值,实际上并未存储在数据库中(下面的代码片段中已删除 XML 头注释)。

public short BusinessDaysUntil(DateTime dt1, DateTime dt2)
{
    TimeSpan diff1 = dt2 - dt1;
    if (diff1.Days == 0)
    return 0;

    bool positive = (diff1.Days >= 0);

    short ret;
    short diffmodify = 0;
    short diffchange = 0;
    bool done = false;

    //a swap is simplest way to deal with negative days between,
    //because the same processing logic
    //can be used - just negate value when done if negative
    if (!positive)
    {
    DateTime dtTemp;
    dtTemp = dt2;
    dt2 = dt1;
    dt1 = dtTemp;
    }
    DateTime dt1Adjusted = dt1;

    do
    {
    string sql = "SELECT DateDifference FROM AllDatesDiff WHERE Date1 = @dt1"
     + " AND Date2 = @dt2";
    ret = Convert.ToInt16(this.SqlCommandExecDateParamReturnScalar(sql,
        "@dt1", dt1Adjusted, "@dt2", dt2));
    if (ret == 0)
    {
        this.GetNextDate(dt1Adjusted, ref dt1Adjusted, ref diffchange);
        diffmodify = Convert.ToInt16(diffmodify + diffchange);
    }
    else
    {
        diffmodify = Convert.ToInt16(diffmodify + ret);
        done = true;
    }

    } while (!done);

    if (positive)
    return diffmodify;
    else
    return Convert.ToInt16(-diffmodify);
}

上面的 _SqlCommandExecDateParamReturnScalar_ 方法是我创建的一个数据访问包装器方法,以及一些其他类似的方法,这些方法使许多其他方法调用保持简短。为了这个项目并减少依赖性,我在数据访问类中将这些方法设为 _private_。另外,为了让上面的方法更具可读性和可维护性,我将第一个查询中获取下一个日期(返回值为 0)的部分拆分到了第二个 _private_ 方法中(同样,下面的代码片段中已删除 XML 头注释)。

private void GetNextDate(DateTime dt1, ref DateTime NextDate, ref short datedifference)
{
    string sql = "SELECT MAX(date2), datedifference FROM alldatesdiff WHERE date1 = @dt1"
        + " AND datedifference = "
        + " (SELECT MAX(datedifference) FROM alldatesdiff"
        + " WHERE date1 = @dt1) GROUP BY datedifference";

    SqlConnection connection = new SqlConnection(m_ConnectionString);
    connection.Open();
    SqlCommand cmm = new SqlCommand(sql, connection);

    using (connection)
    {
        cmm.Parameters.AddWithValue("@dt1", dt1.ToString("d"));
        SqlDataReader dr = cmm.ExecuteReader();
        while (dr.Read())
        {
            NextDate = Convert.ToDateTime(dr.GetValue(0));
            datedifference = Convert.ToInt16(dr.GetValue(1));
        }
        dr.Close();
    }
    return;
} 

第二层:DLL 库

DLL 库由一个执行每个方法的控制台应用程序直接引用和调用。这是输出

Screenshot - dateagingcli_output1.png

5. DateAgingApp2 解决方案 - 通过 Windows Communication Foundation 主机 (WCF) 访问

第一层:请参见 DateAgingApp1

两个应用程序的数据访问层的源代码是相同的。

第二层 - WCF 服务

我创建了一个尽可能简单的服务,由一个控制台应用程序托管。截至目前,我对 WCF 还很陌生,这是我使用这项技术创建的第一个服务——这部分代码更多地演示了概念,而不是健壮、熟练的实现。我已经了解到有很多可能的调整选项;因此,WCF 远不如创建或使用“老式”ASMX 容易。

下面是服务的接口,已移除 XML 头

[ServiceContract(Namespace = "https://:8000/DateAgingDataService")]
public interface IDateAgingDataService
{
    [OperationContract]
    short BusinessDaysUntil(DateTime dt1, DateTime dt2);

    [OperationContract]
    bool IsBusinessDate(DateTime dt);

    [OperationContract]
    bool IsBusinessHolidayDate(DateTime dt);

    [OperationContract]
    bool IsFederalHolidayDate(DateTime dt);

    [OperationContract]
    bool IsHolidayDate(DateTime dt);

    [OperationContract]
    bool IsWeekendDate(DateTime dt);

    [OperationContract]
    DateTime NextBusinessDate(DateTime dt);

    [OperationContract]
    DateTime NextNonBusinessDate(DateTime dt);

    [OperationContract]
    DateTime NextWeekendDate(DateTime dt);

    [OperationContract]
    DateTime PreviousBusinessDate(DateTime dt);

    [OperationContract]
    DateTime PreviousNonBusinessDate(DateTime dt);

    [OperationContract]
    DateTime PreviousWeekendDate(DateTime dt);
}

这是实现类的一个片段

// Service class which implements the service contract.
public class DateAgingDataService : IDateAgingDataService
{
    public bool IsBusinessDate(DateTime dt)
    {
        DateAgingDataAccessLib objDate = DataConnect();
        return objDate.IsBusinessDate(dt);
    }

    public bool IsHolidayDate(DateTime dt)
    {
        DateAgingDataAccessLib objDate = DataConnect();
        return objDate.IsHolidayDate(dt);
    }
//etc...
}

我认为使用 WCF 的难点不在于编写接口和方法,而在于为项目设置适当的配置。

您可以看到我正在使用 .NET _DateTime_ 数据类型。我不确定这是否是最佳方法,考虑到 _DateTime_ 在处理时区变化时存在的问题,以及 _DateTime_ 的时间部分在这里并不相关。其他选项可以是一个可识别日期格式(mm/dd/yyyy)的 _string_ 或自定义 _Date_ 对象(WCF 支持自定义复杂的 .NET 类型)。

无论如何,如果您想在实际项目中托管服务,您将需要进行一些阅读和研究,以确定最佳的托管方式。您可以在 IIS 或 Windows 服务中托管 WCF 服务;这两种都是比我在这里使用的方法更好的生产部署选项(控制台应用程序需要用户登录到机器才能启动服务。此外,基于控制台的服务很容易被意外的按键或鼠标点击关闭)。

at this point, I won't go into any more detail on WCF, except to point out that there are many good resources and tutorials available on the subject here on CodeProject and elsewhere, as well as a number of new books (check the ratings at Amazon.com).

第三层 - 客户端应用程序

这是一个与 _DateAgingApp1_ 中的控制台应用程序类似的应用程序——唯一的区别在于 _DateAgingApp2_ 客户端已设置为引用和利用 WCF 服务,而不是直接链接到 _DateAgingData.dll_。在 _DateAgingApp1_ 和 _DateAgingApp2_ 客户端中,被调用方法的签名和输出是相同的。

注意:如果您想对 WCF 服务中的代码或 _app.config_ 进行任何更改,您必须执行 _svcutil_out.bat_ 文件(如果您更改了命名空间,也请相应修改它),以使用更新的 _app.config_ 和生成的代理(_generatedClient.cs_)来更新客户端源代码。总结:如果您无法运行这个小东西,或者想更改配置或命名空间,请考虑这是一个通过有效的“反复碰壁”技术学习该技术的好机会!我在 _readme.txt_ 文件中包含了详细的步骤,希望这能有所帮助。

6. 结论

希望您觉得这些文章和代码能为您处理应用程序中的日期功能提供有用的想法。

即将推出!项目第三期将演示如何利用预编译数据进行大规模数据的高性能报告——这是第一期花费心思创建数据的主要原因。

7. 历史

  • 2007-07-19:发布到 CodeProject
  • 2007-07-27:对 _DateAgingDataAccessLib.cs_ 进行了更改 - 移除了实例级连接变量,并在方法中添加了 _using_ 子句。_ServiceHost.cs_ - 修改了布尔方法以提高简洁性。
© . All rights reserved.