{"id":2093,"date":"2020-09-30T10:46:25","date_gmt":"2020-09-30T08:46:25","guid":{"rendered":"http:\/\/web.evertop.pl\/case-study-of-differential-data-querying-in-net-core-web-api\/"},"modified":"2020-10-23T20:57:50","modified_gmt":"2020-10-23T18:57:50","slug":"case-study-of-differential-data-querying-in-net-core-web-api","status":"publish","type":"post","link":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/","title":{"rendered":"Case Study Of Differential Data Querying In .NET Core Web API"},"content":{"rendered":"<p>As you may know we were asked to develop system for attendees of Economic Forum 2020. One of the client\u2019s main requests was to allow mobile applications, communicating with main server, to read only the data that has been changed since last reading. We were given the read-only access to full dataset held on client\u2019s legacy server (stored in XML file). Later that day we came up with a potential solution, shown below.<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img class=\"wp-image-2951\" src=\"https:\/\/www.web.evertop.pl\/wp-content\/uploads\/artykulrafala-1024x456.png\" sizes=\"(max-width: 1024px) 100vw, 1024px\" srcset=\"https:\/\/www.web.evertop.pl\/wp-content\/uploads\/artykulrafala-1024x456.png 1024w, https:\/\/www.web.evertop.pl\/wp-content\/uploads\/artykulrafala-300x134.png 300w, https:\/\/www.web.evertop.pl\/wp-content\/uploads\/artykulrafala-768x342.png 768w, https:\/\/www.web.evertop.pl\/wp-content\/uploads\/artykulrafala-1020x454.png 1020w, https:\/\/www.web.evertop.pl\/wp-content\/uploads\/artykulrafala.png 1157w\" alt=\"\" \/><\/figure>\n<\/div>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>Presented diagram points out two main parts of the entire process.<\/p>\n<ol>\n<li>Efficient (due to frequency of readouts) transforming entire data from legacy server into the structure that allows part 2 do its job.<\/li>\n<li>Allowing users to fetch only the data that has changed after provided timespan.<\/li>\n<\/ol>\n<p>For the API\u2019s technology stack we chose C#, ASP.NET Core, Entity Framework Core and Hangfire as background jobs runner.<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<h2>Part 1 \u2013 cyclic legacy data transformation<\/h2>\n<p>I will ignore the real XML\u2019s content as it\u2019s completely irrelevant to explain the main idea behind the solution. Let\u2019s assume we import list of events from XML below:<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;events&gt;\r\n  &lt;event id=\"1\" startDate=\"2020-01-01 12:00\" endDate=\"2020-01-01 13:45\" topic=\"Topic 1\"&gt;\r\n    &lt;participants&gt;\r\n      &lt;participant fullName=\"Jon Doe\" \/&gt;\r\n      &lt;participant fullName=\"George Wahlin\" \/&gt;\r\n    &lt;\/participants&gt;\r\n  &lt;\/event&gt;\r\n  &lt;!-- more data --&gt;\r\n  &lt;event id=\"10000\" startDate=\"2020-01-11 12:00\" endDate=\"2020-01-11 13:45\" topic=\"Topic 10000\"&gt;\r\n    &lt;participants\/&gt;\r\n  &lt;\/event&gt;\r\n&lt;\/events&gt;\r\n<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>That list contains information for the conference\u2019s agenda, such as the topic, time and participants.<\/p>\n<p>Our entity (which is also the entity in DbContext) representing single entry from incoming XML looks like this.<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>public class Event\r\n{\r\n    public int Id { get; set; }\r\n    public int ExternalId { get; set; }\r\n\r\n    public DateTime StartDate { get; set; }\r\n    public DateTime EndDate { get; set; }\r\n    public string Topic { get; set; }\r\n    public List&lt;string&gt; Participants { get; set; } = new List&lt;string&gt;();\r\n\r\n    public int Hash { get; set; }\r\n\r\n    public DateTime ModifiedDate { get; set; }\r\n    public DateTime? DeletedDate { get; set; }\r\n}\r\n<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>Key factor in our scenario is ability to uniquely identify which entry from XML is which in our database. For that purpose we hold incoming\u00a0<strong><span class=\"has-inline-color\">id<\/span>\u00a0<\/strong>attribute under\u00a0<code>ExternalId<\/code>\u00a0property and use it as the key for this particular process. We could argue whether or not it should be Id of our entity but all this comes down to individual cases we design our system for.<\/p>\n<p>Once we can successfully match incoming entry with our stored one, we need to tell what has changed. Is the entity new? Is it removed? Is it modified? All that can be easily done with set operators, and utilizing the\u00a0<code>ExternalId<\/code>\u00a0and (not yet explained) Hash property.<\/p>\n<p>The idea behind introducing custom hash values is for efficient determining which data kept in our database differs, without producing bulky and time-consuming SQL queries to check all properties or, even worse, potential tree structures of related sub-objects. We need to remember that this process is being triggered every few minutes, so if it takes longer than that, it won\u2019t be able keep up (as it can\u2019t be ran in parallel). Core concept was to work just against two indexed columns to minimize not only the mentioned risk but also memory usage. But why did we introduce new artificial hash values and didn\u2019t override existing ones? We needed to have complete control over marking given entity as different than its sibling in database, but that control was limited only to that process. Overriding provided .NET equality mechanism would make it very easy to introduce bugs very difficult to find.<\/p>\n<p>Next question is: What does \u201cdifferent\u201d mean?. Obviously, the two sibling events differ if, for example, their topics do, but what of participants? Order doesn\u2019t matter in our case, thus our hash calculations must be independent of it.<\/p>\n<p>All things considered, we can write function for hash calculation:<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>public int CalculateHash(Event e)\r\n{          \r\n    var hash = 17;\r\n    var m = 23;\r\n\r\n    unchecked\r\n    {\r\n        hash = hash * m + e.StartDate.GetHashCode();\r\n        hash = hash * m + e.EndDate.GetHashCode();\r\n        hash = hash * m + (e.Topic?.GetHashCode()).GetValueOrDefault(0);\r\n\r\n        foreach (var participant in e.Participants\r\n            .Where(p=&gt;!String.IsNullOrEmpty(p))\r\n            .OrderBy(p =&gt; p))\r\n        {\r\n            hash = hash * m + participant.GetHashCode();\r\n        }\r\n    }\r\n\r\n    return hash;\r\n}<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>We apply this function to all incoming entries from XML (initial values set to hash and m variables are arbitrary prime numbers). Once we have all data mapped and their hash values calculated, we can do three queries to the database to tell easily what\u2019s new, modified and removed. Since we depend on\u00a0<code>ExternalId<\/code>\u00a0as the key in this process (that\u2019s the assumption we have made prior to tackling this topic), we can write three simple queries to categorize incoming data. New data constitutes all entries with no siblings in our database. We don\u2019t even need to check for their hashes at this point. Modified entries are those entries which do have siblings, but their hashes differ. Entries that have been removed are the ones that do not exist in the incoming data, yet they do in our database. Since all three checks are based on the data we have already stored, we can cache it with a single query, and continue from there.<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>var dbDataIds = await DbContext.Events.Select(e =&gt; new { e.ExternalId, e.Hash }).ToListAsync();\r\n\r\nvar inputDataIds = inputEvents.Select(e =&gt; new { e.ExternalId, e.Hash }).ToList();<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>Now that we have these two lists, we can calculate all previously mentioned sets. Note that we don\u2019t fetch all columns from the database, just the bare minimum to do all calculations in memory to make it as fast as possible without unnecessary queries. Since we read only two integers per row, and we know (thanks to business analysis) the possible data volume in this table, we can safely do that and utilize server memory for quick processing.<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>var dataToAddIds = inputDataIds\r\n    .Select(d =&gt; d.ExternalId)\r\n    .Except(dbDataIds.Select(d =&gt; d.ExternalId));\r\n\r\nvar dataToRemoveIds = dbDataIds\r\n    .Select(d =&gt; d.ExternalId)\r\n    .Except(inputDataIds.Select(d =&gt; d.ExternalId));\r\n\r\nvar dataToModifyIds = dbDataIds\r\n    .Where(e =&gt; inputDataIds.Any(d =&gt; d.ExternalId == e.ExternalId &amp;&amp; d.Hash != e.Hash))\r\n    .Select(e =&gt; e.ExternalId);<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>Having these lists of identifiers in place, we can easily find what to add, modify and delete. Except that we don\u2019t actually just delete nor just update\u2026 Remember what the entity looked like? Apart from Hash property, it contains also two\u00a0<code>DateTime<\/code>\u00a0properties. These are the holy grail of point 2 working as intended and we mustn\u2019t forget about setting them with correct values. You can check the entire function comparing incoming data and updating database below.<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>public async Task SyncEvents(IEnumerable&lt;Event&gt; inputData)\r\n{\r\n    var now = DateTime.Now;\r\n\r\n    var dbDataIds = await DbContext.Events.Select(e =&gt; new { e.ExternalId, e.Hash }).ToListAsync();\r\n    var inputDataIds = inputData.Select(e =&gt; new { e.ExternalId, e.Hash }).ToList();\r\n\r\n    var dataToRemoveIds = dbDataIds\r\n        .Select(d =&gt; d.ExternalId)\r\n        .Except(inputDataIds.Select(d =&gt; d.ExternalId));\r\n\r\n    var dataToAddIds = inputDataIds\r\n        .Select(d =&gt; d.ExternalId)\r\n        .Except(dbDataIds.Select(d =&gt; d.ExternalId));\r\n\r\n    var dataToModifyIds = dbDataIds\r\n        .Where(e =&gt; inputDataIds.Any(d =&gt; d.ExternalId == e.ExternalId &amp;&amp; d.Hash != e.Hash))\r\n        .Select(e =&gt; e.ExternalId);\r\n\r\n    var dataToRemove = await DbContext.Events.Where(e =&gt; dataToRemoveIds.Contains(e.ExternalId)).ToListAsync();\r\n\r\n    var dataToModify = await DbContext.Events.Where(e =&gt; dataToModifyIds.Contains(e.ExternalId)).ToListAsync();\r\n\r\n    var dataToAdd = inputData.Where(e =&gt; dataToAddIds.Contains(e.ExternalId)).ToList();\r\n\r\n    foreach (var dbE in dataToRemove)\r\n    {\r\n        dbE.DeletedDate = now;\r\n        dbE.ModifiedDate = now;\r\n    }\r\n\r\n    foreach (var e in dataToAdd)\r\n    {\r\n        e.ModifiedDate = now;\r\n        DbContext.Panels.Add(e);\r\n    }\r\n\r\n    var inputDataLookup = inputData.ToDictionary(e =&gt; e.ExternalId, e =&gt; e);\r\n    foreach (var dbE in dataToModify)\r\n    {\r\n        var e = inputDataLookup[dbE.ExternalId];\r\n\r\n        dbE.Topic = e.Topic;\r\n        dbE.StartDate = e.StartDate;\r\n        dbE.EndDate = e.EndDate;\r\n        dbE.Participants = e.Participants;\r\n\r\n        dbE.Hash = e.Hash;\r\n\r\n        dbE.ModifiedDate = now;\r\n        dbE.DeletedDate = null;\r\n    }\r\n\r\n    await DbContext.SaveChangesAsync();\r\n}<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>Now that we have synchronized the data between XML and our DB, we can move to next point.<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<h2>Part 2 \u2013 utilizing time stamps<\/h2>\n<p>Mobile applications use API method presented below to fetch data<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>public async Task&lt;IActionResult&gt; GetConferenceAgenda(DateTime? modifiedAfter)\r\n{\r\n    modifiedAfter = modifiedAfter.GetValueOrDefault();\r\n    var now = DateTime.Now;\r\n\r\n    var panels = await _conferencesService.GetEvents(e =&gt;                \r\n        e.ModifiedDate &gt;= modifiedAfter &amp;&amp; e.ModifiedDate &lt; now, EventDTO.Selector);\r\n\r\n    return Ok(new ConferenceAgendaData()\r\n    {\r\n        NextModifiedAfter = now,\r\n        Panels = panels,               \r\n    });\r\n}\r\n<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>The idea behind\u00a0<code>ModifiedAfter<\/code>\u00a0parameter is to fetch only the data has that changed since last invocation. This method also has to work for initial request, thus we support null value. Passing in null defaults to 0001-01-01. We execute a simple query to get necessary data (notice we only work against\u00a0<code>ModifiedAfter<\/code>\u00a0property as it\u2019s being set upon every add, update and soft deletion), transform this into DTO (Data Transfer Object), and return the result alongside timespan value the mobile app should use for its next request. Why is that\u00a0<code>NextModifiedAfter<\/code>\u00a0value important? It\u2019s due to possible time differences between mobile and main server on which data resides. Without this synchronization we could hit an issue, when it\u2019s the caller determining next value and its system clock is ahead of server\u2019s one. In this case, if any data had been modified within the time fitting right in between server\u2019s and mobile\u2019s clocks, this data would not be brought back to the caller.<\/p>\n<p>Returned DTO object looks like this:<\/p>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<pre class=\"wp-block-code\"><code>public class EventDTO\r\n{\r\n    public int Id { get; set; }\r\n    public bool IsDeleted { get; set; }\r\n\r\n    \/* more data *\/\r\n\r\n    public static Expression&lt;Func&lt;Event, EventDTO&gt;&gt; Selector =&gt; e =&gt; new EventDTO()\r\n    {\r\n        Id = e.Id,\r\n        IsDeleted = e.DeletedDate != null,\r\n        \r\n        \/* more data *\/\r\n    };\r\n}<\/code><\/pre>\n<div class=\"wp-block-spacer\" aria-hidden=\"true\"><\/div>\n<p>It\u2019s just a simple class, but I would like to make two points.<\/p>\n<p>The first point (irrelevant to this discussion but still worth mentioning) is the Selector property, which encapsulates SELECT done by the database (we return\u00a0<code>Expression&lt;Func&lt;T, TOut&gt;&gt;<\/code>\u00a0instead of just\u00a0<code>Func&lt;T<\/code>,\u00a0<code>TOut&gt;<\/code>).<\/p>\n<p>The second point is the existence of\u00a0<code>IsDeleted<\/code>\u00a0property. Since we need to tell the caller which data was removed, we are enforced to use soft deletion mechanism on the server, use timestamps for differentiation, and bring necessary information back.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As you may know we were asked to develop system for attendees of Economic Forum 2020. One of the client\u2019s main requests was to allow mobile applications, communicating with main server, to read only the data that has been changed since last reading. We were given the read-only access to full dataset held on client\u2019s [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":2087,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[15],"tags":[111,112,23],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v16.8 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Case Study Of Differential Data Querying In .NET Core Web API - Evertop<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/\" \/>\n<meta property=\"og:locale\" content=\"nb_NO\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Case Study Of Differential Data Querying In .NET Core Web API - Evertop\" \/>\n<meta property=\"og:description\" content=\"As you may know we were asked to develop system for attendees of Economic Forum 2020. One of the client\u2019s main requests was to allow mobile applications, communicating with main server, to read only the data that has been changed since last reading. We were given the read-only access to full dataset held on client\u2019s [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/\" \/>\n<meta property=\"og:site_name\" content=\"Evertop\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/EvertopPoland\/\" \/>\n<meta property=\"article:published_time\" content=\"2020-09-30T08:46:25+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-10-23T18:57:50+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/web.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"684\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Skrevet av\" \/>\n\t<meta name=\"twitter:data1\" content=\"Katarzyna Baron\" \/>\n\t<meta name=\"twitter:label2\" content=\"Ansl. lesetid\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutter\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.evertop.pl\/#organization\",\"name\":\"Evertop\",\"url\":\"https:\/\/www.evertop.pl\/\",\"sameAs\":[\"https:\/\/www.facebook.com\/EvertopPoland\/\",\"https:\/\/www.linkedin.com\/company\/evertop-software-development\/\"],\"logo\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/www.evertop.pl\/#logo\",\"inLanguage\":\"nb-NO\",\"url\":\"https:\/\/www.evertop.pl\/wp-content\/uploads\/2021\/04\/logo_new.png\",\"contentUrl\":\"https:\/\/www.evertop.pl\/wp-content\/uploads\/2021\/04\/logo_new.png\",\"width\":582,\"height\":114,\"caption\":\"Evertop\"},\"image\":{\"@id\":\"https:\/\/www.evertop.pl\/#logo\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.evertop.pl\/#website\",\"url\":\"https:\/\/www.evertop.pl\/\",\"name\":\"Evertop\",\"description\":\"we code the future\",\"publisher\":{\"@id\":\"https:\/\/www.evertop.pl\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.evertop.pl\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"nb-NO\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#primaryimage\",\"inLanguage\":\"nb-NO\",\"url\":\"https:\/\/www.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg\",\"contentUrl\":\"https:\/\/www.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg\",\"width\":1024,\"height\":684},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#webpage\",\"url\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/\",\"name\":\"Case Study Of Differential Data Querying In .NET Core Web API - Evertop\",\"isPartOf\":{\"@id\":\"https:\/\/www.evertop.pl\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#primaryimage\"},\"datePublished\":\"2020-09-30T08:46:25+00:00\",\"dateModified\":\"2020-10-23T18:57:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#breadcrumb\"},\"inLanguage\":\"nb-NO\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Strona g\\u0142\\u00f3wna\",\"item\":\"https:\/\/www.evertop.pl\/no\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Case Study Of Differential Data Querying In .NET Core Web API\"}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#webpage\"},\"author\":{\"@id\":\"https:\/\/www.evertop.pl\/#\/schema\/person\/8187ac3407cd931ed1a015a96990e2df\"},\"headline\":\"Case Study Of Differential Data Querying In .NET Core Web API\",\"datePublished\":\"2020-09-30T08:46:25+00:00\",\"dateModified\":\"2020-10-23T18:57:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#webpage\"},\"wordCount\":1140,\"publisher\":{\"@id\":\"https:\/\/www.evertop.pl\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg\",\"keywords\":[\"ASP.NET\",\"C#\",\"programming tips\"],\"articleSection\":[\"Blog\"],\"inLanguage\":\"nb-NO\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.evertop.pl\/#\/schema\/person\/8187ac3407cd931ed1a015a96990e2df\",\"name\":\"Katarzyna Baron\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/www.evertop.pl\/#personlogo\",\"inLanguage\":\"nb-NO\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/208e27a58a39e5d96f459a4802804274?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/208e27a58a39e5d96f459a4802804274?s=96&d=mm&r=g\",\"caption\":\"Katarzyna Baron\"},\"url\":\"https:\/\/www.evertop.pl\/no\/author\/kbaron\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Case Study Of Differential Data Querying In .NET Core Web API - Evertop","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/","og_locale":"nb_NO","og_type":"article","og_title":"Case Study Of Differential Data Querying In .NET Core Web API - Evertop","og_description":"As you may know we were asked to develop system for attendees of Economic Forum 2020. One of the client\u2019s main requests was to allow mobile applications, communicating with main server, to read only the data that has been changed since last reading. We were given the read-only access to full dataset held on client\u2019s [&hellip;]","og_url":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/","og_site_name":"Evertop","article_publisher":"https:\/\/www.facebook.com\/EvertopPoland\/","article_published_time":"2020-09-30T08:46:25+00:00","article_modified_time":"2020-10-23T18:57:50+00:00","og_image":[{"width":1024,"height":684,"url":"http:\/\/web.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg","path":"\/home\/evertop\/web-evertop\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg","size":"full","id":2087,"alt":"","pixels":700416,"type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Skrevet av":"Katarzyna Baron","Ansl. lesetid":"8 minutter"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Organization","@id":"https:\/\/www.evertop.pl\/#organization","name":"Evertop","url":"https:\/\/www.evertop.pl\/","sameAs":["https:\/\/www.facebook.com\/EvertopPoland\/","https:\/\/www.linkedin.com\/company\/evertop-software-development\/"],"logo":{"@type":"ImageObject","@id":"https:\/\/www.evertop.pl\/#logo","inLanguage":"nb-NO","url":"https:\/\/www.evertop.pl\/wp-content\/uploads\/2021\/04\/logo_new.png","contentUrl":"https:\/\/www.evertop.pl\/wp-content\/uploads\/2021\/04\/logo_new.png","width":582,"height":114,"caption":"Evertop"},"image":{"@id":"https:\/\/www.evertop.pl\/#logo"}},{"@type":"WebSite","@id":"https:\/\/www.evertop.pl\/#website","url":"https:\/\/www.evertop.pl\/","name":"Evertop","description":"we code the future","publisher":{"@id":"https:\/\/www.evertop.pl\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.evertop.pl\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"nb-NO"},{"@type":"ImageObject","@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#primaryimage","inLanguage":"nb-NO","url":"https:\/\/www.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg","contentUrl":"https:\/\/www.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg","width":1024,"height":684},{"@type":"WebPage","@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#webpage","url":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/","name":"Case Study Of Differential Data Querying In .NET Core Web API - Evertop","isPartOf":{"@id":"https:\/\/www.evertop.pl\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#primaryimage"},"datePublished":"2020-09-30T08:46:25+00:00","dateModified":"2020-10-23T18:57:50+00:00","breadcrumb":{"@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#breadcrumb"},"inLanguage":"nb-NO","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Strona g\u0142\u00f3wna","item":"https:\/\/www.evertop.pl\/no\/"},{"@type":"ListItem","position":2,"name":"Case Study Of Differential Data Querying In .NET Core Web API"}]},{"@type":"Article","@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#article","isPartOf":{"@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#webpage"},"author":{"@id":"https:\/\/www.evertop.pl\/#\/schema\/person\/8187ac3407cd931ed1a015a96990e2df"},"headline":"Case Study Of Differential Data Querying In .NET Core Web API","datePublished":"2020-09-30T08:46:25+00:00","dateModified":"2020-10-23T18:57:50+00:00","mainEntityOfPage":{"@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#webpage"},"wordCount":1140,"publisher":{"@id":"https:\/\/www.evertop.pl\/#organization"},"image":{"@id":"https:\/\/www.evertop.pl\/no\/case-study-of-differential-data-querying-in-net-core-web-api\/#primaryimage"},"thumbnailUrl":"https:\/\/www.evertop.pl\/wp-content\/uploads\/2020\/10\/rafal-artykul.jpg","keywords":["ASP.NET","C#","programming tips"],"articleSection":["Blog"],"inLanguage":"nb-NO"},{"@type":"Person","@id":"https:\/\/www.evertop.pl\/#\/schema\/person\/8187ac3407cd931ed1a015a96990e2df","name":"Katarzyna Baron","image":{"@type":"ImageObject","@id":"https:\/\/www.evertop.pl\/#personlogo","inLanguage":"nb-NO","url":"https:\/\/secure.gravatar.com\/avatar\/208e27a58a39e5d96f459a4802804274?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/208e27a58a39e5d96f459a4802804274?s=96&d=mm&r=g","caption":"Katarzyna Baron"},"url":"https:\/\/www.evertop.pl\/no\/author\/kbaron\/"}]}},"_links":{"self":[{"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/posts\/2093"}],"collection":[{"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/comments?post=2093"}],"version-history":[{"count":1,"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/posts\/2093\/revisions"}],"predecessor-version":[{"id":2095,"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/posts\/2093\/revisions\/2095"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/media\/2087"}],"wp:attachment":[{"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/media?parent=2093"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/categories?post=2093"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.evertop.pl\/no\/wp-json\/wp\/v2\/tags?post=2093"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}