In previous blog post I briefly touched OData protocol by showing quick usage of OData feed and then implemented ever simple WCF data service with using Entity Framework.
Now, let’s imagine that we have some data in memory and would like to expose it. Is it hard or easy?
If you can represent your data as IQueryable then, for most cases, you are fine. There is an extension method to IEnumerable called AsQueryable, so as long as your data can be accessed as IEnumerable you can make it IQueryable.
Now, let’s create some sample data:
private IList<Sport> _sports = new List<Sport>(); private IList<League> _leagues = new List<League>(); private void Populate() { _sports = new List<Sport>(); _leagues = new List<League>(); _sports.Add(new Sport() { Id = 1, Name = "Swimming"}); _sports.Add(new Sport() { Id = 2, Name = "Skiing"}); var sport = new Sport() { Id = 3, Name = "Football"}; var league = new League() { Id = 1, Name = "EURO2012", Region = "Poland&Ukraine" }; sport.AddLeague(league); var league1 = new League() { Id = 2, Name = "UK Premier League", Region = "UK" }; sport.AddLeague(league1); _sports.Add(sport); var league2 = new League() { Id = 3, Name = "Austria Premier League", Region = "Austria" }; _leagues.Add(league); _leagues.Add(league1); _leagues.Add(league2); _sports.Add(new Sport() { Id = 4, Name = "Tennis"}); _sports.Add(new Sport() { Id = 5, Name = "Volleyball"}); }
Absolutely nothing smart or difficult there, but at least to give you an idea about dataset we have.
To expose sports and leagues we would need to add public properties to our data service implementation like below:
public IQueryable<Sport> Sports { get { return _sports.AsQueryable(); } } public IQueryable<League> Leagues { get { return _leagues.AsQueryable(); } }
Another important thing about exposing data is that you have to indicate key for your entities with DataServiceKey attribute.
To make our service bit more realistic I’m going to add caching as well.
public class SportsWcfDataService : DataService<SportsData> { public static void InitializeService(DataServiceConfiguration config) { config.SetEntitySetAccessRule("*", EntitySetRights.AllRead); config.SetEntitySetAccessRule("*", EntitySetRights.AllRead); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2; // To know if there are issues with your data model config.UseVerboseErrors = true; } protected override void HandleException(HandleExceptionArgs args) { // Put breakpoint here to see possible problems while accessing data base.HandleException(args); } protected override SportsData CreateDataSource() { return SportsData.Instance; } protected override void OnStartProcessingRequest(ProcessRequestArgs args) { base.OnStartProcessingRequest(args); var cache = HttpContext.Current.Response.Cache; cache.SetCacheability(HttpCacheability.ServerAndPrivate); cache.SetExpires(HttpContext.Current.Timestamp.AddSeconds(120)); cache.VaryByHeaders["Accept"] = true; cache.VaryByHeaders["Accept-Charset"] = true; cache.VaryByHeaders["Accept-Encoding"] = true; cache.VaryByParams["*"] = true; } } public class SportsData { static readonly SportsData _instance = new SportsData(); public static SportsData Instance { get { return _instance; } } private SportsData() { Populate(); } private void Populate() { // Population of data // Above in this post } private IList<Sport> _sports = new List<Sport>(); public IQueryable<Sport> Sports { get { return _sports.AsQueryable(); } } private IList<League> _leagues = new List<League>(); public IQueryable<League> Leagues { get { return _leagues.AsQueryable(); } } } [DataServiceKey("Id")] public class Sport { public int Id { get; set; } public string Name { get; set; } public void AddLeague(League league) { _leagues.Add(league); } private IList<League> _leagues = new List<League>(); public IEnumerable<League> Leagues { get { return _leagues; } } } [DataServiceKey("Id")] public class League { public int Id { get; set; } public string Name { get; set; } public string Region { get; set; } }
With this URL http://localhost:49936/SportsService.svc/ service can be seen:
<service xml:base="http://localhost:49936/SportsService.svc/"> <workspace> <atom:title>Default</atom:title> <collection href="Sports"><atom:title>Sports</atom:title></collection> <collection href="Leagues"><atom:title>Leagues</atom:title></collection> </workspace> </service>
Now, you can access data via URL or by writing C# Linq queries if connected with client app or LinqPad. Following request:
http://localhost:49936/SportsService.svc/Leagues()?$filter=Name eq ‘EURO2012’&$select=Region
Would produce result containing this:
If you need your data to be updatable, your SportsData would need to implement System.Data.Services.IUpdatable interface.
What if you have very special data source, or you cannot simply keep your data in memory like collections of some data? This could be tricky or very tricky, depending on your data source, of course. But anyway you would need to implement interface IQueryable by hand if not provided by your data source.
Here is step-by-step walkthrough on msdn. Only by size of article you could imagine it is not trivial task to do. But if you managed to do it you can be proud, because you can implement your own Linq provider. (Do you remember NHibernate introducing support of Linq? It was big feature for product, roughly all they did was implementation of IQueryably by their NhQueryable.)
First of all I have to investigate if my team can use it. (Depending on outcome you might see more posts on this topic from me.) And another reason is that it is very exciting topic, about which I knew so little.
Very disappointing to admit that I start to really understand things when I touch them for reason and that reading dozen of blog posts on topic is useless unless I do something meaningful related to matters in those posts.
So, my Dear Reader, if you are reading this post and have read my previous post, but never tried OData I would be really pleased if you invest 10-30 or more minutes of your time to play with OData.
Markdown | Result |
---|---|
*text* | text |
**text** | text |
***text*** | text |
`code` | code |
~~~ more code ~~~~ |
more code |
[Link](https://www.example.com) | Link |
* Listitem |
|
> Quote | Quote |
До речі можу порадити, якщо є бажання то частину посту на блоггері можна забирати "під кат" для того щоб на головній було простіше розрізняти різні пости.
Для того щоб забрати пост під кат потрібно вставити спец. комент Все що після нього піде під кат.
І якщно не секрет, то в якому сценарії ви в своїй команді збираєтесь викроистовувати WCF Data Services?
Я знаю про цю можливість, але покищо не певен чи воно мені потрібно на блозі. Можливо… Дякую.
At the moment I work in Sports domain. We develop API for our client applications so they can fetch Sport/League/Events/etc.. and lot of surrounding data. With time it is more hard to satisfy each client's needs. We can give them more power to allow fetching exactly what they want.
Where can i get more information on OData protocol please?
Dot Net Training in Chennai