Może zainteresują Cię pozostałe posty z cyklu Mapowanie SPListItem na obiekt:
- Mapowanie SPListItem na obiekt – Wprowadzenie
- Mapowanie SPListItem na obiekt – Wrapper
- Mapowanie SPListItem na obiekt – implicit/explicit conversion
- Mapowanie SPListItem na obiekt – LINQ to SharePoint (aktualnie czytasz)
- Mapowanie SPListItem na obiekt – AutoMapper
- Mapowanie SPListItem na obiekt – Field Mapper
Przez ostatnie dwa tygodnie miałem trochę czasu na zastanowieniem się nad sensem opisywania kolejnych typów mapowań w tym LINQ to SharePoint. Doszedłem do wniosku, że jednakże warto o nich wspomnieć.
LINQ to SharePoint
LINQ jest potężnym rozszerzeniem .NET, umożliwiającym nam wykonywanie zapytań na kolekcjach obiektów co za tym idzie wybranie elmentu o ID 10 z kolekcji procownicy, staje się banalnie proste. Jednakże by LINQ zadziałało to musi istnieć jakiś provider, który przetłumaczy dane zapytanie na określony język – LINQ2SQL, LINQ2nHibernate itp. itd. Takich rozszerzeń jest masa i Charlie stara się trzymać ich listę up-to-date. Czy są to wszystkie aktualnie dostępne providery? Na pewno nie, jednakże lista jest pokaźna i zawiera to co nas najbardziej interesuje, link do projektu na CodePlex LINQ to SharePoint.
Projekt niestety od 2007 roku nie był rozszerzany i aktualizowany, co właśnie skłoniło mnie do zastanowienia się nad sensem promowania tego typu modelowania obiektów.
Mimo wszystko, projekt działa prawie na tej samej zasadzie co LINQ w SPS 2010, mianowicie by rozpocząć pracę najpierw należy wygenerować klasy dla list, co można zrobić wykorzystując narzędzie SPMetal dostępne w rozwiązaniu (dla ciekowości podam, że w SPS 2010 narzędzie to też się zwie SPMetal). Nie zdziwiłoby mnie gdyby się okazało, że to ten sam Developer pisał SPMetal dla SharePoint 2010, głównie dlatego, że od października 2007 pracuje on w MS, a ostatni release LINQ 2 SharePoint datuje się na listopad 2007.
Po wykonaniu polecanie SPMetal dostajemy klasę z atrybutami określającymi ID pola, nazwę i typ, plusem jest to, że pola MultiChoice zamieniane są od razu na numeratory – przydatny bajer.
Dla przykładu:
using BdsSoft.SharePoint.Linq; using System; using System.Collections.Generic; /// <summary> /// Test /// </summary> [global::BdsSoft.SharePoint.Linq.ListAttribute("Test", Id="1bbbed4a-4c06-4f8f-8c03-4e91181efa6c", Version=0, Path="/Lists/Test")] public partial class Test : global::System.ComponentModel.INotifyPropertyChanged, global::System.ComponentModel.INotifyPropertyChanging { private int _ID = default(int); private string _ContentType = default(string); private string _Title; private global::System.Nullable<System.DateTime> _Modified = default(global::System.Nullable<System.DateTime>); private global::System.Nullable<System.DateTime> _Created = default(global::System.Nullable<System.DateTime>); private string _Version = default(string); partial void OnTitleChanging(string value); partial void OnTitleChanged(); /// <summary> /// ID /// </summary> [global::BdsSoft.SharePoint.Linq.FieldAttribute("ID", global::BdsSoft.SharePoint.Linq.FieldType.Counter, Id="1d22ea11-1e32-424e-89ab-9fedbadb6ce1", PrimaryKey=true, ReadOnly=true, Storage="_ID")] public int ID { get { return this._ID; } } /// <summary> /// Content Type /// </summary> [global::BdsSoft.SharePoint.Linq.FieldAttribute("ContentType", global::BdsSoft.SharePoint.Linq.FieldType.Text, Id="c042a256-787d-4a6f-8a8a-cf6ab767f12d", ReadOnly=true, Storage="_ContentType")] public string ContentType { get { return this._ContentType; } } /// <summary> /// Title /// </summary> [global::BdsSoft.SharePoint.Linq.FieldAttribute("Title", global::BdsSoft.SharePoint.Linq.FieldType.Text, Id="fa564e0f-0c70-4ab9-b863-0177e6ddd247", Storage="_Title")] public string Title { get { return this._Title; } set { if ((this._Title != value)) { this.OnTitleChanging(value); this.SendPropertyChanging("Title"); this._Title = value; this.SendPropertyChanged("Title"); this.OnTitleChanged(); } } } /// <summary> /// Modified /// </summary> [global::BdsSoft.SharePoint.Linq.FieldAttribute("Modified", global::BdsSoft.SharePoint.Linq.FieldType.DateTime, Id="28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f", ReadOnly=true, Storage="_Modified")] public global::System.Nullable<System.DateTime> Modified { get { return this._Modified; } } /// <summary> /// Created /// </summary> [global::BdsSoft.SharePoint.Linq.FieldAttribute("Created", global::BdsSoft.SharePoint.Linq.FieldType.DateTime, Id="8c06beca-0777-48f7-91c7-6da68bc07b69", ReadOnly=true, Storage="_Created")] public global::System.Nullable<System.DateTime> Created { get { return this._Created; } } /// <summary> /// Version /// </summary> [global::BdsSoft.SharePoint.Linq.FieldAttribute("_UIVersionString", global::BdsSoft.SharePoint.Linq.FieldType.Text, Id="dce8262a-3ae9-45aa-aab4-83bd75fb738a", ReadOnly=true, Storage="_Version")] public string Version { get { return this._Version; } } public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] protected void SendPropertyChanging(string propertyName) { if ((this.PropertyChanging != null)) { this.PropertyChanging(this, new global::System.ComponentModel.PropertyChangingEventArgs(propertyName)); } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] protected void SendPropertyChanged(string propertyName) { if ((this.PropertyChanged != null)) { this.PropertyChanged(this, new global::System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } }
Teraz mając tak wygenerowaną klasę możemy spokojnie zadać zapytanie:
using System; using System.Linq; using BdsSoft.SharePoint.Linq; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { var ctx = new SharePointDataContext(new Uri("http://gutek-pc")); ctx.Log = Console.Out; var testData = ctx.GetList<Test>(); var res = from t in testData orderby t.Title descending where t.ItsOk.HasValue && t.ItsOk == ItsOk.Yes select t; foreach (var t in res) Console.WriteLine(t.Title); } } }
Przypisanie ctx.Log do Console.Out wyświetli nam treść zapytania CAML – przydatna rzecz jeżeli chcemy logować takie dane.
I na tym się kończy wsparcie LINQ. Tak jak w poprzednim przypadku (implicit/explicit) nie ma możliwości dodania elementu, trzeba to samemu oprogramować. Jednakże, jeżeli nasze dane mają być głównie pobierane i potem przetwarzane to chyba ten sposób jeden jednym z najszybszych.
Wady
- Nie można dodać elementu do listy, można jedynie go pobrać;
- Kod jest w wersji Alpha, można napotkać na problemy, które w trakcie trwania projektu mogą spowodować duże opóźnienia;
- Kod wymaga URL do strony :( co w niektórych wypadkach może być ciężkie do uzyskania.
Zalety
- Generowanie klas;
- Od generowania do rozpoczęcia pisania kodu mija zaledwie 10 sekund więc jesteśmy wstanie szybko aktualizować nasz kod i od razu widzieć tego efekty;
- Posiada wsparcie dla VS 2008 (należy ściągnąć aktualizację z grudnia i ją z kompilować :))
- Na podstawie kodu, można rozszerzyć możliwości LINQ to SharePoint tak by spełniały one nasze oczekiwania;
- Zapytania są nawet zoptymalizowane chociaż samemu nie testowałem tego na dużych listach.
Macie jakieś własne doświadczenia z LINQ to SharePoint? To nie licząc jednego projektu nie i jestem ciekaw innych opinii :)