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

Silverlight 考勤演示(使用 Sterling Silverlight 数据库)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (24投票s)

2010年11月4日

Ms-PL

6分钟阅读

viewsIcon

99088

downloadIcon

3813

一个使用 Silverlight Sterling 数据库和视图模型 (MVVM) 的演示应用程序。

一个 Silverlight 数据库

Silverlight 的一个特性是独立存储。它允许您的 Silverlight 程序将几乎任何类型的数据或对象本地存储在用户的计算机上。基本上,它就像一个非常大的网页 cookie。有些人称之为“大 Cookie”。

您希望这样做的原因是它对用户来说更快。它对于 Windows Phone 7 应用程序也非常有用。独立存储的问题是,与它的交互非常原始。这就是适用于 Silverlight 和 Windows Phone 7 的 Sterling 独立存储数据库与 LINQ 项目能够提供帮助的地方。它允许我们在独立存储中存储和检索数据(几乎)就像在普通关系数据库中一样。

Sterling 独立存储数据库

Jeremy Likness 创建的适用于 Silverlight 和 Windows Phone 7 的 Sterling 独立存储数据库与 LINQ 项目提供了以下功能

  • 它是免费的 - 夫复何求?
  • 它提供完整的源代码 - 因此您的项目没有风险。
  • 出色的文档 - 对于如此复杂的项目,这非常重要。它还附带示例、Silverlight 浏览器外和 Windows Phone 7 项目。
  • 它具有非常快的性能并具有索引 - 使用独立存储的目的是速度(和便利性)。CodePlex 站点上项目下载中的示例项目展示了代码如何快速处理大量数据。
  • 小巧轻便 - 这包括大小和功能。它有很多功能,但它只处理数据的读取和写入,以及与这些目标相关的任何内容。您不必使用视图模型、MVVM 或特定的设计模式。

请注意,您可以通过此链接找到其他免费的开源 Silverlight 对象数据库

使用对象数据库

使用 Sterling 非常简单。您首先定义您的表。接下来,您将数据(对象)保存到您的表中,如下所示

SterlingService.Current.Database.Save(
        new Attendance()
        {
            AttendanceKey = "1-23324353453534",
            EnrollmentId = 1,
            AttendanceDate = new DateTime(2010, 1, 1),
            AttendanceStatus = "P"
        });

要更新对象,只需使用现有对象的键保存对象即可。

现在,要获取您的数据,您在使用像 Sterling 这样的对象数据库时必须调整您的思维方式。您不能只定义一个带字段的对象,然后查询您想要的任何字段来获取数据。

您必须使用实体的键来获取数据

Attendance objAttendanceRecord = 
  SterlingService.Current.Database.Load<Attendance>("1-23324353453534");

或者使用您创建的预定义索引查询它,例如 "ATTENDANCE_ENROLLMENTIDANDDATE"

var result = (from Attendance in SterlingService.Current.Database.Query<Attendance, 
   int, DateTime, string>(AttendanceDatabase.ATTENDANCE_ENROLLMENTIDANDDATE)
   where Attendance.Index.Item1 == EnrollmentId
   where Attendance.Index.Item2 == AttendanceDate
   select Attendance).FirstOrDefault();

示例应用程序:简单考勤

为了演示 Sterling 的功能,我决定使用一个虽小但不简单的示例。这个考勤应用程序很小,但并非没有复杂性。您可以选择一天,并且只会看到当天注册的学生。当您输入考勤状态时,它会立即保存。

在某些情况下,您正在创建新的考勤记录,而在某些情况下,您正在更新考勤记录。程序无缝处理一切,并以您点击鼠标的速度移动。

该应用程序主要由 Student 类、Enrollment 类和 Attendance 类组成。Sterling 没有外键,但正如您稍后将看到的,我们能够创建实体之间的连接。

设置您的项目

Sterling 附带的文档是一流的,所以您会想阅读它。本文将向您介绍在项目中使用 Sterling 所需步骤的总体概念。

第一步是引用 Sterling 项目,或将其程序集包含在您的项目中(您可以从其下载页面下载程序集的安装程序)。

接下来,您创建一个继承自 BaseDatabaseInstance 的类。然后您定义您的表和索引

protected override List<ITableDefinition> _RegisterTables()
{
    return new List<ITableDefinition>
    {
        CreateTableDefinition<Student, int>(fg => fg.StudentId).WithIndex<Student, 
            string, int>(STUDENT_NAME, fg => fg.Name),
        CreateTableDefinition<Enrollment, 
            int>(fd => fd.EnrollmentId).WithIndex<Enrollment, 
            int, int>(ENROLLMENT_ENROLLMENTID, fd =>fd.EnrollmentId).WithIndex<Enrollment, 
            DateTime, int>(ENROLLMENT_STARTDATE, fd =>fd.StartDate).WithIndex<Enrollment, 
            DateTime, int>(ENROLLMENT_STOPDATE, fd =>fd.StopDate),
        CreateTableDefinition<Attendance, 
            string>(fd => fd.AttendanceKey).WithIndex<Attendance, 
            int, DateTime, string>(ATTENDANCE_ENROLLMENTIDANDDATE,
            fd =>
            Tuple.Create(fd.EnrollmentId, fd.AttendanceDate)),
    };
}

您可以将 SterlingService 类复制并粘贴到您的项目中,只需更改一行即可注册您刚刚创建的数据库类(在此示例中为 AttendanceDatabase

public void Starting()
{
    if (DesignerProperties.IsInDesignTool) return;

    _engine.Activate();

    // Put the type of the Database Class after RegisterDatabase<
    Database = _engine.SterlingDatabase.RegisterDatabase<AttendanceDatabase>();
}

然后将以下行添加到您的 app.xaml 页面以启动服务

<Application.ApplicationLifetimeObjects>
    <SimpleAttendance:SterlingService/>
</Application.ApplicationLifetimeObjects>

我发现如果有人禁用他们的独立存储,应用程序将无法工作(人们有权禁用独立存储,就像他们可以禁用网页浏览器 cookie 一样),所以我将此包含在 app.xaml.cs 文件中

public App()
{
    this.Startup += this.Application_Startup;
    UnhandledException += Application_UnhandledException;

    try
    {
        // Test for Isolated Storage
        using (IsolatedStorageFile oStore =
                IsolatedStorageFile.GetUserStoreForApplication())
        {
            InitializeComponent();
        }
    }
    catch (IsolatedStorageException)
    {
        MessageBox.Show("You need to right-click and " + 
                        "enable Isolated Storage to use this application");
    }
}

就这样,您已经设置完毕!

给我一些示例数据!

此示例应用程序使用视图模型 (MVVM) 设计模式。它加载时所做的第一件事是 InvokeCommandAction 行为(设置为当用户控件“加载”时触发)调用 LoadDatabaseCommand (ICommand)。

如果 LoadData 方法检测到学生数据库表为空,它会调用 SetupData 方法

public void LoadData()
{
    bool hasKeys = false;
    foreach (var item in SterlingService.Current.Database.Query<Student, int>())
    {
        hasKeys = true;
        break;
    }

    if (!hasKeys)
    {
        SetupData();
    }

SetupData 创建所有Student及其Enrollment。它只创建一个示例Attendance记录

private void SetupData()
{
    // Students
    var StudentData = new List<Student>()
    {
        new Student() { StudentId = 1 , Name = "Student One" },
        new Student() { StudentId = 2 , Name = "Student Two" },
        new Student() { StudentId = 3 , Name = "Student Three" },
        new Student() { StudentId = 4 , Name = "Student Four" },
        new Student() { StudentId = 5 , Name = "Student Five" },
        new Student() { StudentId = 6 , Name = "Student Six" },
        new Student() { StudentId = 7 , Name = "Student Seven" },
        new Student() { StudentId = 8 , Name = "Student Eight" },
        new Student() { StudentId = 9 , Name = "Student Nine" },
        new Student() { StudentId = 10 , Name = "Student Ten" },
    };

    foreach (var item in StudentData)
    {
        SterlingService.Current.Database.Save(item);
    }

    // Enrollments
    var EnrollmentData = new List<Enrollment>()
    {
        new Enrollment() {EnrollmentId = 1, StudentId = 1, 
            StartDate = new DateTime(2010,1,1), 
            StopDate = new DateTime(2010,2,1) },
        new Enrollment() {EnrollmentId = 2, StudentId = 2, 
            StartDate = new DateTime(2010,1,1), 
            StopDate = new DateTime(2010,2,11) },
        new Enrollment() {EnrollmentId = 3, StudentId = 3, 
            StartDate = new DateTime(2010,1,3), 
            StopDate = new DateTime(2010,2,1) },
        new Enrollment() {EnrollmentId = 4, StudentId = 4, 
            StartDate = new DateTime(2010,1,3), 
            StopDate = new DateTime(2010,2,1) },
        new Enrollment() {EnrollmentId = 5, StudentId = 5, 
            StartDate = new DateTime(2010,1,10), 
            StopDate = new DateTime(2010,2,15) },
        new Enrollment() {EnrollmentId = 6, StudentId = 6, 
            StartDate = new DateTime(2010,1,3), 
            StopDate = new DateTime(2010,2,1) },
        new Enrollment() {EnrollmentId = 7, StudentId = 7, 
            StartDate = new DateTime(2010,1,22), 
            StopDate = new DateTime(2010,2,1) },
        new Enrollment() {EnrollmentId = 8, StudentId = 8, 
            StartDate = new DateTime(2010,1,1), 
            StopDate = new DateTime(2010,2,1) },
        new Enrollment() {EnrollmentId = 9, StudentId = 9, 
            StartDate = new DateTime(2010,1,1), 
            StopDate = new DateTime(2010,2,1) },
        new Enrollment() {EnrollmentId = 10, StudentId = 10, 
            StartDate = new DateTime(2010,1,1), 
            StopDate = new DateTime(2010,2,1) }
    };

    foreach (var item in EnrollmentData)
    {
        SterlingService.Current.Database.Save(item);
    }

    // Sample Attendance
    SterlingService.Current.Database.Save(new Attendance()
    {
        AttendanceKey = String.Format("{0}-{1}", "1", 
                        new DateTime(2010, 1, 1).Ticks.ToString()),
        EnrollmentId = 1,
        AttendanceDate = new DateTime(2010, 1, 1),
        AttendanceStatus = "P"
    });
}

请注意,对于 Attendance 记录,我们不简单地使用整数作为计数器字段。我们创建一个由 Enrollment 和以 Tick 表示的 Date 组成的键(String.Format("{0}-{1}", "1", new DateTime(2010, 1, 1).Ticks.ToString()))。这是真正的键,因为我们绝不希望数据库中同一天有相同 EnrollmentAttendance 记录。

回到 LoadData 方法中,我们将学生和注册“”的内容加载到视图模型中的公共集合中。这将允许我们使用 LINQ to Objects 轻松查询数据。

foreach (var item in SterlingService.Current.Database.Query<Student, int>())
{
    Students.Add(item.LazyValue.Value);
}

foreach (var item in SterlingService.Current.Database.Query<Enrollment, int>())
{
    Enrollments.Add(item.LazyValue.Value);
}

IsDataLoaded = true;
GetAttendanceForDay();

但是,我们不会加载 Attendance 记录,因为即使像这样的小演示,也可能有很多 Attendance 记录。我们使用此代码检索所选日期的已注册学生

private void GetAttendanceForDay()
{
    colAttendanceForDay.Clear();

    var result = from Enrollment in Enrollments
                    from Student in Students
                    where Enrollment.StudentId == Student.StudentId
                    where Enrollment.StartDate >= AttendanceDate
                    where Enrollment.StopDate <= AttendanceDate
                    select new AttendanceForDay
                    {
                        AttendanceKey = String.Format("{0}-{1}", 
                          Enrollment.EnrollmentId, AttendanceDate.Ticks.ToString()),
                          AttendanceStatus = 
                          GetAttendanceStatus(Enrollment.EnrollmentId, AttendanceDate),
                          EnrollmentId = Enrollment.EnrollmentId,
                          Name = Student.Name,
                          StudentId = Student.StudentId
                    };

    foreach (var item in result)
    {
        // Add an instance of this View Model to the object
        // This will allow us to get an instance of the View Model
        // to raise events such as the UpdateAttendanceCommand
        item.objMainPageModel = this;
        colAttendanceForDay.Add(item);
    }
}

该查询调用此方法,该方法针对我们为表定义的 Index (ATTENDANCE_ENROLLMENTIDANDDATE) 进行查询,以获取考勤状态

private static string GetAttendanceStatus(int EnrollmentId, DateTime AttendanceDate)
{
    var result = (from Attendance in SterlingService.Current.Database.Query<Attendance, 
                  int, DateTime, string>(AttendanceDatabase.ATTENDANCE_ENROLLMENTIDANDDATE)
                  where Attendance.Index.Item1 == EnrollmentId
                  where Attendance.Index.Item2 == AttendanceDate
                  select Attendance).FirstOrDefault();

    if (result != null)
    {
        return result.LazyValue.Value.AttendanceStatus;
    }
    else
    {
        return "";
    }

当我们需要考勤数据时,我们只从 Sterling 数据库中一次拉取一条记录。但是,它非常快,因为数据位于用户的计算机上。

关于单选按钮的注意事项

所有单选按钮都绑定到同一个字段 (AttendanceStatus)。它们各自使用两个值转换器。一个为其 GroupName 创建一个唯一名称(在使用带视图模型 (MVVM) 的单选按钮时需要),另一个将其绑定到一个可能的单值(例如“P”表示“Present”)。

在绑定中,我们指定值转换器,并指定一个转换器参数。每个单选按钮使用不同的参数。这是 IsChecked 字段的值转换器

public class AttendanceConverter : IValueConverter
{
    #region Methods
    public object Convert(object value, Type targetType, object parameter, 
           System.Globalization.CultureInfo culture)
    {
        if (value == null || parameter == null)
        {
            return value;
        }

        return value.ToString() == parameter.ToString();
    }
    public object ConvertBack(object value, Type targetType, object parameter, 
           System.Globalization.CultureInfo culture)
    {
        if (value == null || parameter == null)
        {
            return value;
        }

        return (String)parameter;
    }
    #endregion Methods
}

另请注意,DataGrid 必须设置为只读,以防止用户单击 DataGrid 而不是单选按钮时发生绑定错误(这样一来,单行中可以选中多个单选按钮)。

每个按钮上的一个行为通过调用以下 ICommand 来更新考勤

#region UpdateAttendanceCommand
public ICommand UpdateAttendanceCommand { get; set; }
public void UpdateAttendance(object param)
{
    AttendanceForDay objAttendanceForDay = (AttendanceForDay)param;

    // Get the Attendnace record if there is one
    Attendance objAttendanceRecord = 
        SterlingService.Current.Database.Load<Attendance>(
        objAttendanceForDay.AttendanceKey);

    if (objAttendanceRecord == null) // New Record
    {
        // If Attendance Record was not found create a new record
        Attendance NewAttendanceRecord = new Attendance();
        NewAttendanceRecord.AttendanceKey = objAttendanceForDay.AttendanceKey;
        NewAttendanceRecord.AttendanceDate = AttendanceDate;
        NewAttendanceRecord.EnrollmentId = objAttendanceForDay.EnrollmentId;
        NewAttendanceRecord.AttendanceStatus = objAttendanceForDay.AttendanceStatus;
        SterlingService.Current.Database.Save(NewAttendanceRecord);
        SterlingService.Current.Database.Flush();
    }
    else // Update Record
    {
        // Set Attendanace status and save the record
        objAttendanceRecord.AttendanceStatus = objAttendanceForDay.AttendanceStatus;
        SterlingService.Current.Database.Save(objAttendanceRecord);
        SterlingService.Current.Database.Flush();
    }
}

强大的工具可以节省时间并减少错误

使用像 Sterling 这样的数据库的一个好方法是允许用户输入考勤,然后点击一个按钮将其传输到中央服务器。任何冲突都可以在辅助步骤中解决。也许会弹出一个屏幕显示任何冲突,并允许用户决定是否要覆盖记录。好处是应用程序会运行得非常快,并且用户永远不会“超时”。如果他们的计算机断电,他们也不会丢失任何记录。

像这样的应用程序展示了 Silverlight 在业务线 (LOB) 应用程序方面的真正强大功能。

© . All rights reserved.