2008年11月18日,星期二

的SharePoint列表的简单数据访问模式

因此,您处于项目的早期阶段,编码已经开始。已经很明显,访问一些键列表中的数据需要某种抽象,但是您不确定要做什么。当然,这个问题没有“千篇一律”的答案,但是让我们来看一些选项:

在我看来,这个问题的最佳答案可能是前两个问题之一,尤其是在SharePoint之上构建类似于传统应用程序(即具有与数据关联的域模型和实体)的项目上,而不是正在说的项目,简单的WCM。但是,像我一样,也许您仍然没有花费大量时间在研究LINQ4SP(也许对原始LinqToSharePoint化身的记忆还不够成熟),或者与P处于同一位置&P个样品或认为它看起来有点 企业满足您当前的需求。在一个开发周期短的小项目中,我不会怪你。

因此,就其他选项而言-就构建自己的DAL而言当然是有可能的,但是,如果您的项目规模较小,那么大概会花时间为所有数据需求手动编码抽象将很棘手。另外,我不太喜欢这种方法-我刚刚看到它多次被其他人做得很差,导致图层执行不佳/无法缩放,因为对象未正确放置/未提供方便的目的。最后,就“不做任何事情”选项而言,尽管我们可能不会为最终模式而努力, 如果我们允许项目中的每个开发人员以他们喜欢的方式使用数据,都将试图避免重复,不一致和维护方面的噩梦。

我的建议:-

我使用过几次,是一种“介于两者之间”的方法-具体来说,就是在无所事事和构建复杂的DAL之间。我在这里显示的内容与我使用的不完全一样(我在进行一些“改进”!),但是我认为它是一种非常快速,轻巧的模式,至少可以帮助减少最严重的问题:

public static class Employee
{
public static readonly string ListName = "Employees";

public static class Fields
{
public static string PersonTitle = "Person_x0020_title";
public static string FirstName = "First_x0020_name";
public static string LastName = "Last_x0020_name";
public static string StartDate = "Start_x0020_date";
public static string Division = "Division";
public static string ID = "ID";
public static string Salary = "Salary";
}

/// <summary>
/// Fetches employee list item - 不ice that 呼叫者 has to supply SPWeb object, but all other implementation
/// details (list name, fields names) 是 taken care of..
/// </summary>
public static SPListItem GetEmployeeListItem(int EmployeeID, SPWeb Web)
{
SPListItemCollection queryResult = executeEmployeeLookup(EmployeeID, Web);

return queryResult[0];
}

/// <summary>
/// Fetches employee DataRow for 'read-only' situations, or where we want to cache/serialize etc.
/// </summary>
public static DataRow GetEmployeeDataRow(int EmployeeID, SPWeb Web)
{
SPListItem employeeItem = GetEmployeeListItem(EmployeeID, Web);
DataRow drEmployee = SPListItemCollectionHelper.CreateDataRowFromListItem(employeeItem);

return drEmployee;
}

/// <summary>
/// Private method which does actual query.
/// </summary>
private static SPListItemCollection executeEmployeeLookup(int employeeID, SPWeb web)
{
SPList employeeList = web.Lists[Employee.ListName];

// query employee list..
SPQuery employeeQuery = new SPQuery();
employeeQuery.Query = string.Format("<Where><Eq><FieldRef Name=\"{0}\" /><Value Type=\"Text\">{1}</Value></Eq></Where>",
Employee.Fields.ID, employeeID);

SPListItemCollection employees = employeeList.GetItems(employeeQuery);

return employees;
}

/// <summary>
/// Example of another method related to employees.
/// </summary>
public static SPListItemCollection FetchAllHiresSince(DateTime StartDate, SPWeb web)
{
SPList employeeList = web.Lists[Employee.ListName];

// query employee list..
SPQuery employeeQuery = new SPQuery();
employeeQuery.Query = string.Format("<Where><Geq><FieldRef Name=\"{0}\" /><Value Type=\"Text\">{1}</Value></Geq></Where>",
Employee.Fields.StartDate, SPUtility.CreateISO8601DateTimeFromSystemDateTime(StartDate));

SPListItemCollection employees = employeeList.GetItems(employeeQuery);

return employees;
}
}


注意事项:

  • 我们已经集中了员工数据的存储库详细信息,例如 列表名称栏位名称

  • 现在,对呼叫者来说,掌握Employee列表项是一行,他们不需要知道如何查找数据

  • 必须传递SPWeb对象,这意味着调用方有责任获取和处置-稍后将对此进行详细介绍

  • 调用方具有“获取为DataRow”方法-您可能会觉得不需要此方法,但我认为它可以是有用的API函数。与SPListItem相比,DataRow与SharePoint完全断开连接,因此,没有任何非托管的SPRequest对象挂在它上面需要处理。这意味着它可以被缓存/序列化,等等,并且可以在方法的深层堆栈中传递,而调用代码不必负责处理(顺便说一下,这需要在Final模块中完成,这样处理即使发生异常也会发生)

  • 对于“获取为DataRow”方法,将使用一个帮助程序类将SPListItemCollection转换为DataTable,将SPListItem转换为DataRow-这主要是因为现有的SPListItemCollection.ToDataTable()方法存在错误。 (注:这是故意的 以扩展方法的形式表示清楚!)

  • 该类不是实例类,因此我们不执行任何操作,例如用属性包装每个列表字段。这对于调用者来说可能不那么方便,但是这意味着我们不必担心数据项是否为“脏”数据

  • 更新操作留给调用者

这意味着调用代码(在SharePoint Web上下文中)如下所示:



public void GetReadOnlyEmployee()
{
DataRow drEmployee = Employee.GetEmployeeDataRow(56, SPContext.Current.Web);

// do something/pass happily around codebase..
}

public void GetEmployeeForUpdate()
{
SPListItem employee = Employee.GetEmployeeListItem(56, SPContext.Current.Web);
employee[Employee.Fields.Salary] = 50000;
employee.Update();
}

对我来说,这种方法很好"bang for buck"因为它的实施速度非常快,但确实可以使团队发展情况大为改善。 

传递SPWeb对象是关键:

在我的文章中 处置SharePoint对象-它们不会告诉您什么,我强调了跟踪对象以放置在复杂的类库中的困难。但是,我现在开始在这里看到用作示例的代码是一种反模式。 我用来演示该问题的简化代码示例为:



public void DoSomething()
{
bool bDisposalsRequired = false;

// get list from SPContext if we have one..
SPList list = getListFromContext();
if (list == null)
{
// otherwise get list from objects we create..
list = getInstantiatedList();
bDisposalsRequired = true;
}

// do something with list..
foreach (SPListItem item in list.Items)
{
processItem(item);
}

if (bDisposalsRequired)
{
list.ParentWeb.Dispose();
list.ParentWeb.Site.Dispose();
}
}

private SPList getInstantiatedList()
{
// can't dispose of these objects here if we're returning a list - we'll be 在 tempting to use
// objects which have already been disposed of..
SPSite site = new SPSite("http://cob.blog.dev");
SPWeb web = site.OpenWeb("/MyWeb");
SPList list = web.Lists["MyList"];

return list;
}

private SPList getListFromContext()
{
SPContext currentContext = SPContext.Current;
SPList list = null;

if (currentContext != null)
{
list = currentContext.Site.AllWebs["MyWeb"].Lists["MyList"];
}

return list;
}


在这种结构中,内部API代码(即DoSomething()方法)负责获取查找列表所需的SPWeb对象,这通常意味着它还负责处理列表。这就是出现困难的地方。但是,如果 呼叫者 必须提供IDisposable对象,然后它可以负责调用Dispose(),因为它知道何时不再需要它们。通常,调用者只是传递对SPContext.Current.Web的引用,但是如果调用者恰好是功能接收者,则将传递SPFeatureProperties.ListItem.Parent.Web,或者如果没有现有的SPWeb对象可供调用者使用(例如,控制台应用程序)必须实例化并传递一个新的SPWeb对象。这样,处置将变得更加简单,因为它们始终可以与任何实例化都在同一位置发生。

结论:-

我敢肯定那里有更好的模式,但是即使像这样的简单方法也提供了比将其全部留给调用者更多的结构(特别是当调用代码由不同的开发人员编写时!)。对我来说,关键是要标准化代码库中的代码如何访问列表数据-比在此处,各处和各处点缀字段和列表名称要好得多。一件事的方法 特别考虑的是单元测试-模式中的示例项目&实践的东西在这里看起来很有用,我会越来越熟悉的!

附言谢谢 罗伯·博格 为了帮助我明确这些想法和道歉 塞西·科穆尔 不能像我承诺的那样尽早寄出草案!

5条评论:

塞西·科穆尔说过...

>>并向Sezai Komur致歉,因为我没有答应不尽早寄出草稿!


没问题的伴侣-热爱您的工作!

...而且我喜欢这种模式,它好看又简单,比什么都不做要好得多。人们似乎最常做的开发任务是从列表中获取数据。这样的简单模式可以提供很好的抽象,而实现起来却不会过于复杂。通过将处理工作留给调用者,我们可以对执行DAL工作的静态类进行编码,而不必担心由它们引起的内存泄漏。

杰里米·塔克(Jeremy Thake)说过...

很棒的帖子克里斯,我想我会在这篇文章中写一篇完整的评论,但现在我的脑海中浮现了一些东西。

这正在走向SQL,.NET,ASP.NET Web项目的时代,在那里您拥有一个具有模式的SQL数据库和存储的proc,以执行所有CRUD,然后是一个.NET层来构造所有这一切,然后是一个ASP.NET页。展示一切。 .NET层可以具有数据层和业务对象层。
根据您的建议,基本上SharePoint将从SQL,存储的proc和.net数据层接管。

这可能是自动生成的,尽管MyGeneration / CodeSmith之类的东西曾经用于使用外键来理解现在是列表的表之间的关系……而SharePoint在关系数据方面并不是那么出色。它可以很好地处理数据类型和必填字段。

总而言之...您真的可以在SharePoint列表中走得更远吗?

克里斯·奥'Brien说过...

@杰里米

确实-您提到的代码生成方法是使Linq方法可用于SharePoint的关键方面之一-LINQ4SP和LinqToSharePoint实现都使用它。我猜这唯一的问题是,每当添加/更改此列表中的任何内容时,您都必须返回并重新生成代理类(并重新编译/部署)。但是想必您还是在更改代码,这正是促使您更改列表的原因。

因此,是的,我认为对于正在寻找访问SharePoint列表数据的“模式”方法的实现,这里有一些不错的“ ORM”选项。我渴望很快在实际项目中使用LINQ4SP。

同时,希望我的建议之类的内容可以作为对默认值的快速增强。会注意您的帖子。

干杯,

克里斯。

匿名 said...

克里斯,你好如果列表是未知的(直到调用代码调用才知道),我想知道如何构造显示的数据访问模式。

您只是将SPWeb对象换成SPList吗?

非常感谢您的示例专门针对一个列表的数据访问而设计,但是我来自内容类型包装器。

谢谢。很棒的文章。

克里斯·奥'Brien说过...

@匿名,

由于SPList没有实现IDisposable,因此不一定是相同的问题。我认为您仍然需要传递SPWeb对象,以便调用方可以对其进行处理。但是,我的头上没有看到通用方法使用它自己的逻辑来获取实际列表的任何问题,因为它不需要处理。

HTH,

克里斯。