diff --git a/Changelog.txt b/Changelog.txt index 005a2ca..46099a4 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,10 @@ +3.0.0 - July 18, 2018 +* Break change: All entities types which is used in Delta or DeltaCollection must be declared in DeltaConfig.Init(). +* Break change: excluded property and 'ignore null value' option must be declared in DeltaConfig.Init(). +* Added global and properties mapping functions. +* Added support to add property using expressions in Delta.Add method. +* Added unit tests. + 1.2.3 - Oct 13, 2017 * Added ability to ignore null value for specified properties. diff --git a/README-v1.x.md b/README-v1.x.md new file mode 100644 index 0000000..2e06511 --- /dev/null +++ b/README-v1.x.md @@ -0,0 +1,201 @@ +SimplePatch +

+ +A simple library for partial entity changes in ASP.NET and ASP.NET Core. + + +**ATTENTION:** This documentation refers to the 1.x version of SimplePatch. Check out the new version [here](https://github.com/OmarMuscatello/SimplePatch). + +### Summary +- [Introduction](#introduction) +- [Install](#install) +- [How to use](#how-to-use) +- [Integration with the Entity Framework](#integration-with-entity-framework) +- [Configuration](#configuration) + +## Introduction + +### The problem +Partial modification of entities is one of the common issues when implementing a RESTful service in ASP.NET Web API. The client, in fact, must specify the value for all entity properties, including those properties whose value has not been changed. Typically, to solve this problem, you use these solutions with their own problems: +- [`Delta`](https://msdn.microsoft.com/en-us/library/jj890572(v=vs.118).aspx) (part of Microsoft ASP.NET WebAPI OData): it has some problems with numbers when using JSON (see [this answer](https://stackoverflow.com/a/14734273/7772490)). You also need to install the package with all its non-trivial dependencies; +- [JSON Patch](http://jsonpatch.com/): the client must organize the data per operation and the size of the request is not optimized. + +##### Demonstrative example +The client must set the `Enabled` property of the `User` entity. The latter, however, also exposes the `Name` property. The client is forced to pass both the values of the `Enabled` and `Name` properties in the request body. + +*Request body* +``` +{ "Enabled": true, "Name": "User1" } +``` + +In a real case, however, the properties of an entity are more than two, making the problem more pronounced. +``` +{ "Enabled": true, "Name": "User1", "Prop1": "Value1", "Prop2": "Value2", "Prop3": "Value3", ... } +``` + +### The solution +The ideal solution is to allow the client to make a request with the only properties to modify. +Returning to the example shown in the *[Problem](#the-problem)* section, the request body for changing the value of the `Enabled` property will be: +``` +{ "Enabled": true } +``` +If the entity has more than one property, the request body will remain the same. + +*SimplePatch* allows you to implement this solution in ASP.NET Web API and ASP.NET Core Web API. + +## Install +Launch the following command from *Package Manager Console*: +``` +Install-Package SimplePatch +``` + +## How to use + +See [*examples* folder](https://github.com/OmarMuscatello/SimplePatch/tree/master/examples) to learn of to use this library with ASP.NET and ASP.NET Core. + +##### Patching a single entity + [HttpPatch] + public IHttpActionResult PatchOne(int id, Delta person) + { + // Determines the entity to be updated according to the id parameter + var personToPatch = TestData.People.FirstOrDefault(x => x.Id == id); + if (personToPatch == null) return BadRequest("Person not found"); + + // Apply the changes specified to the original entity + person.Patch(personToPatch); + + // Now the personToPatch variable is updated + + return Ok(personToPatch); + } +##### Patching multiple entities + [HttpPatch] + public IHttpActionResult PatchMultiple(DeltaCollection people) + { + foreach (var person in people) + { + // Try to get the value of the Id property + if (person.TryGetPropertyValue(nameof(Person.Id), out var id)) + { + // Determines the entity to be updated according to the specified id + var personToPatch = TestData.People.FirstOrDefault(x => x.Id == Convert.ToInt32(id)); + if (personToPatch == null) return BadRequest("Person not found (Id = " + id + ")"); + + // Apply the specified changes to the original entity + person.Patch(personToPatch); + } + else + { + // The Id property was not specified for the person represented by the person variable + return BadRequest("Id property not found for a person"); + } + } + + return Ok(); + } + +## Integration with Entity Framework +##### Patching a single entity +``` +[HttpPatch] +public async Task PatchOne(int id, Delta person) +{ + // Determines the entity to be updated according to the id parameter + var personToPatch = await db.People.FindAsync(id); + if (personToPatch == null) return BadRequest("Person not found"); + + // Apply the specified changes to the original entity + person.Patch(personToPatch); + + // Mark the entity as modified + db.Entry(personToPatch).State = EntityState.Modified; + + // Now the personToPatch variable is updated + + // Save the changes + await db.SaveChangesAsync(); + + return Ok(personToPatch); +} +``` + +##### Patching multiple entities +``` +[HttpPatch] +public async Task PatchMultiple(DeltaCollection people) +{ + foreach (var person in people) + { + // Try to get the value of the Id property + if (person.TryGetPropertyValue(nameof(PersonEF.Id), out var id)) + { + // Determines the entity to be updated according to the id parameter + var personToPatch = await db.People.FindAsync(Convert.ToInt32(id)); + if (personToPatch == null) return BadRequest("Person not found (Id = " + id + ")"); + + // Apply the specified changes to the original entity + person.Patch(personToPatch); + + // Mark the entity as modified + db.Entry(personToPatch).State = EntityState.Modified; + } + else + { + // The Id property was not specified for the person represented by the person variable + return BadRequest("Id property not found for a person"); + } + } + + // Save the changes + await db.SaveChangesAsync(); + + return Ok(); +} +``` + +## Configuration + +#### Exclude properties +You can exclude one or more properties of an entity while applying the changes to the original entity to preserve the original value of the property. This might be useful for properties used to uniquely identify the entity. + +**Global.asax** or **Startup.cs** +``` +DeltaConfig.Init((cfg) => +{ + // Exclude the Id property of the Person entity. + cfg.ExcludeProperties(x => x.Id); +}); +``` + +**Note:** When a property is marked as *excluded* it will still be present in the `Delta ` object, but it will be ignored when the changes are applied (`Patch` method) to the original entity. + +#### Ignore letter case for property names +You can ignore letter case for property names. This is useful when you have different name convention between client code and server code. +For example, the property `name` sent by the client wouldn't be detected as part of an entity which has a property named `Name` (note the first letter is **upper case**). + +**Global.asax** or **Startup.cs** +``` +DeltaConfig.Init((cfg) => +{ + cfg.IgnoreLetterCase(); +}); +``` + +#### Ignore null value for specified properties +You can ignore null value for specified properties of an entity. + +This is particularly useful in two cases: + +- when your property is a value type (like `int` and `DateTime`) and your client still send a null value for that property. Ignoring null value will avoid exception. +- when your property is a reference type (which allows null) but you don't want that `null` overwrites your previous stored data. + +**Global.asax** or **Startup.cs** +``` +DeltaConfig.Init(cfg => +{ + cfg.IgnoreNullValue(x => x.Date); + + // Multiple properties + // cfg.IgnoreNullValue(x => x.Date1, x => x.Date2); +}); +``` \ No newline at end of file diff --git a/README.md b/README.md index 05c2cd4..0ab309e 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,34 @@ -SimplePatch +SimplePatch +

+[![NuGet](https://img.shields.io/nuget/v/SimplePatch.svg)](https://www.nuget.org/packages/SimplePatch/) [![GitHub license](https://img.shields.io/github/license/OmarMuscatello/SimplePatch.svg)](LICENSE) + A simple library for partial entity changes in ASP.NET and ASP.NET Core. -**Help me!** Improve this translation with a pull request. +[SimplePatch **v1.x** documentation](README-v1.x.md) -##### Summary +## Summary +- [Quick Start](#quick-start) - [Introduction](#introduction) - [Install](#install) - [How to use](#how-to-use) -- [Integration with the Entity Framework](#integration-with-entity-framework) - [Configuration](#configuration) + - [Globals](#globals) + - [Adding entities](#adding-entities) + - [Ignore letter case for property names](#ignore-letter-case-for-property-names) + - [(Global) Mapping functions](#(global)-mapping-functions) + - [Properties Configuration](#properties-configuration) + - [Exclude properties](#exclude-properties) + - [Ignore null value](#ignore-null-value) + - [Mapping functions](#mapping-functions) + +## Quick Start +``` +PM> Install-Package SimplePatch +``` +Jump to [How to use](#how-to-use). + ## Introduction @@ -38,66 +56,75 @@ Returning to the example shown in the *[Problem](#the-problem)* section, the req ``` { "Enabled": true } ``` -If the entity has more property, the request body will remain the same. +If the entity has more than one property, the request body will remain the same. -*SimplePatch* allows you to implement this solution in the ASP.NET Web API. +*SimplePatch* allows you to implement this solution in ASP.NET Web API and ASP.NET Core Web API. ## Install Launch the following command from *Package Manager Console*: ``` -Install-Package SimplePatch +PM> Install-Package SimplePatch ``` ## How to use +See [*examples* folder](https://github.com/OmarMuscatello/SimplePatch/tree/master/examples) to learn how to use this library with ASP.NET and ASP.NET Core. -See [*examples* folder](https://github.com/OmarMuscatello/SimplePatch/tree/master/examples) to learn of to use this library with ASP.NET and ASP.NET Core. +##### Add target entity +In your `Startup.cs` file or in the `Global.asax` file, add the entity/ies which you plan to use. +``` +DeltaConfig.Init(cfg => { + cfg.AddEntity(); +}); +``` ##### Patching a single entity - [HttpPatch] - public IHttpActionResult PatchOne(int id, Delta person) - { - // Determines the entity to be updated according to the id parameter - var personToPatch = TestData.People.FirstOrDefault(x => x.Id == id); - if (personToPatch == null) return BadRequest("Person not found"); +``` +[HttpPatch("{id}")] +public async Task PatchOne(int id, Delta person) +{ + // Determines the entity to be updated according to the id parameter + var personToPatch = await TestData.People.FirstOrDefault(x => x.Id == deskid); + if (personToPatch == null) return BadRequest("Person not found"); - // Apply the changes specified to the original entity - person.Patch(personToPatch); + // Apply the changes specified to the original entity + person.Patch(personToPatch); - // Now the personToPatch variable is updated + // Now the personToPatch variable is updated - return Ok(personToPatch); - } + return Ok(personToPatch); +} +``` ##### Patching multiple entities - [HttpPatch] - public IHttpActionResult PatchMultiple(DeltaCollection people) +``` +[HttpPatch] +public async Task PatchMultiple(DeltaCollection people) +{ + foreach (var person in people) { - foreach (var person in people) + // Try to get the value of the Id property + if (person.TryGetPropertyValue(nameof(Person.Id), out var id)) { - // Try to get the value of the Id property - if (person.TryGetPropertyValue(nameof(Person.Id), out var id)) - { - // Determines the entity to be updated according to the specified id - var personToPatch = TestData.People.FirstOrDefault(x => x.Id == Convert.ToInt32(id)); - if (personToPatch == null) return BadRequest("Person not found (Id = " + id + ")"); - - // Apply the specified changes to the original entity - person.Patch(personToPatch); - } - else - { - // The Id property was not specified for the person represented by the person variable - return BadRequest("Id property not found for a person"); - } - } + // Determines the entity to be updated according to the specified id + var personToPatch = TestData.People.FirstOrDefault(x => x.Id == Convert.ToInt32(id)); + if (personToPatch == null) return BadRequest("Person not found (Id = " + id + ")"); - return Ok(); + // Apply the specified changes to the original entity + person.Patch(personToPatch); + } + else + { + // The Id property was not specified for the person represented by the person variable + return BadRequest("Id property not found for a person"); + } } -## Integration with Entity Framework -##### Patching a single entity + return Ok(); +} +``` +##### Patching a single entity (Entity Framework) ``` [HttpPatch] -public async Task PatchOne(int id, Delta person) +public async Task PatchOne(int id, Delta person) { // Determines the entity to be updated according to the id parameter var personToPatch = await db.People.FindAsync(id); @@ -106,11 +133,12 @@ public async Task PatchOne(int id, Delta person) // Apply the specified changes to the original entity person.Patch(personToPatch); + // Now the personToPatch variable is updated + + // Mark the entity as modified db.Entry(personToPatch).State = EntityState.Modified; - // Now the personToPatch variable is updated - // Save the changes await db.SaveChangesAsync(); @@ -118,10 +146,10 @@ public async Task PatchOne(int id, Delta person) } ``` -##### Patching multiple entities +##### Patching multiple entities (Entity Framework) ``` [HttpPatch] -public async Task PatchMultiple(DeltaCollection people) +public async Task PatchMultiple(DeltaCollection people) { foreach (var person in people) { @@ -153,35 +181,111 @@ public async Task PatchMultiple(DeltaCollection peo ``` ## Configuration +All the configuration options can be specified through the `DeltaConfing.Init` function parameter. This function should be called at application startup, typically on the `Startup.cs` or `Global.asax` file. -#### Exclude properties -You can exclude one or more properties of an entity while applying the changes to the original entity to preserve the original value of the property. This might be useful for properties used to uniquely identify the entity. +For example: +``` +public Startup(IConfiguration configuration) +{ + DeltaConfig.Init(cfg => { + cfg.AddEntity() + .Property(x => x.Id).Exclude(); + }); +} +``` -**Global.asax** or **Startup.cs** +### Globals +#### - Adding entities +To be able to use an entity you must add it using the `AddEntity` method of the configuration object. ``` -DeltaConfig.Init((cfg) => +DeltaConfig.Init(cfg => { - // Exclude the Id property of the Person entity. - cfg.ExcludeProperties(x => x.Id); + cfg.AddEntity(); + cfg.AddEntity(); }); ``` -**Note:** When a property is marked as *excluded* it will still be present in the `Delta ` object, but it will be ignored when the changes are applied (`Patch` method) to the original entity. +#### - Ignore letter case for property names +You can ignore letter case for property names. This is useful when you have different name conventions between client code and server code. -#### Ignore letter case for property names -You can ignore letter case for property names. This is useful when you have different name convention between client code and server code. -For example, the property `name` sent by the client wouldn't be detected as part of an entity which has a property named `Name` (note the first letter is **upper case**). +For example, the `name` property sent by the client wouldn't be detected as part of an entity which has a property named `Name` (note the first letter is **upper case**). + +_Usage_ +``` +DeltaConfig.Init(cfg => +{ + cfg.AddEntity(); + + cfg.IgnoreLetterCase(); // <== +}); +``` + +#### - (Global) Mapping functions +Mapping functions allow you to manipulate a value before it is assigned to the property. + +You could use this feature to handle a specific type. + +For example, let's say you want to handle a specific date format (dd/mm/yyyy) for a property whose type is `Nullable`. You could use a global mapping function like this: +``` +DeltaConfig.Init(cfg => +{ + cfg.AddEntity(); + + cfg.AddMapping((propertyType, newValue) => + { + var result = new MapResult(); + + if (propertyType != typeof(DateTime?) || newValue.GetType() != typeof(string)) + { + // No action executed + return result.SkipMap(); + } + + if (DateTime.TryParseExact((string)newValue, "dd/MM/yyyy", new CultureInfo("it-IT"), DateTimeStyles.None, out var date)) + { + // Value which be assigned to the property + result.Value = date; + } + else + { + // Value can be null because the target property is of type Nullable + result.Value = null; + } + + return result; + }); +}); +``` + +You can add as many mapping function as you want. The result returned from the mapping function must be of type `MapResult`. +The latter has a property named `Value` which stores the value which will be assigned to the property. + +If the current mapping function shouldn't handle the property value, you can return the result of the `SkipMap()` method of the `MapResult` instance. + +To better understand how global mapping functions works, please take a look a the diagram below. + +![Global Mapping Functions diagram](diagrams/global-mapping-functions.svg) + +> **Remember** You can assign assign `null` to the `Value` property of the returned `MapResult` instance only if the target property is nullable. + +### Properties configuration +#### - Exclude properties +You can exclude one or more properties of an entity while applying the changes to the original entity to preserve the original value of the property. This might be useful for properties used to uniquely identify the entity. **Global.asax** or **Startup.cs** ``` DeltaConfig.Init((cfg) => { - cfg.IgnoreLetterCase(); + // Exclude the Id property of the Person entity. + cfg.AddEntity().Property(x => x.Id).Exclude(); }); ``` -#### Ignore null value for specified properties -You can ignore null value for specified properties of an entity. +**Note:** When a property is marked as *excluded* it will still be present in the `Delta` object, but it will be ignored when changes are applied (`Patch` method) to the original entity. + + +#### - Ignore null value +You can ignore null value for the specified property of an entity. This is particularly useful in two cases: @@ -192,9 +296,42 @@ This is particularly useful in two cases: ``` DeltaConfig.Init(cfg => { - cfg.IgnoreNullValue(x => x.Date); + cfg.AddEntity().Property(x => x.MyProperty).IgnoreNull(); +}); +``` + +#### - Mapping functions +You can add property specific mapping functions to manipulate the input value before it is assigned to the specified property. +They works like [global mapping functions](global-mapping-functions) but they're applyed only for the specified property. + +Let's say that the client send a two figures number as a string: +``` +{ + "MyNumber": "52" +} +``` +but, you want only the first figure of the number as a `int` (your property is of type `int`). You could use the following mapping function to handle the splitting and conversion: + +**Global.asax** or **Startup.cs** +``` +DeltaConfig.Init(cfg => +{ + cfg.AddEntity().Property(x => x.MyNumber).AddMapping((propType, newValue) => + { + var result = new MapResult(); + + // Ignore non string values + if (newValue.GetType() != typeof(string)) return result.SkipMap(); + + result.Value = Convert.ToInt32(newValue.ToString().Substring(0, 1)); - // Multiple properties - // cfg.IgnoreNullValue(x => x.Date1, x => x.Date2); + return result; + }); }); -``` \ No newline at end of file +``` +To better understand what `SkipMap()` mean, please take a look at the [Global Mapping Functions diagram](#mapping-functions). + +The result type of the specified function must be of the same type of the property for which the mapping function is added to. + +#### Mapping functions order +In order to assign a value to a property, SimplePatch will evaluate the property mapping functions first. If there aren't property mapping functions or they return the result of `SkipMap()`, then the global mapping functions will be evaluated. If there aren't global mapping functions or they return the result of `SkipMap()`, then the default behavior will be used (SimplePatch will try to convert the input value type to the target property type). \ No newline at end of file diff --git a/diagrams/global-mapping-functions.svg b/diagrams/global-mapping-functions.svg new file mode 100644 index 0000000..49ca406 --- /dev/null +++ b/diagrams/global-mapping-functions.svg @@ -0,0 +1,2 @@ + +
A value for a property should be evaluated
A value for a property should be evaluated
Yes
Yes
No
No
Is there any global mapping function?
Is there any global mapping function?
Execute the N global mapping function
Execute the N global mapping function
Yes
Yes
No
No
Has the N global
mapping function returned
the SkipMap() method execution?
[Not supported by viewer]
N = 0
N = 0
Yes
Yes
No
No
Is there any N global
mapping function?
Is there any N global<br>mapping function?
N = N + 1
N = N + 1
Assign to the processed property the value of the 'Value' property of the MapResult<object> instance returned from the mapping function
[Not supported by viewer]
End, value assigned to the property
End, value assigned to the property
Try to assign the input value to the processed property
Try to assign the input value to the processed property
\ No newline at end of file diff --git a/diagrams/global-mapping-functions.xml b/diagrams/global-mapping-functions.xml new file mode 100644 index 0000000..18663ab --- /dev/null +++ b/diagrams/global-mapping-functions.xml @@ -0,0 +1 @@ +5Vpbk5s2FP41TNqHeEDczOOu4ySdaTKdbqZtHmUsAwlGjBDxOr++khAXIXwZG3vtjR92xUHXc75zRYY9Wz9/IDCPP+ElSg1gLp8N+50BgBW4PvvHKduKMvWsihCRZCk7tYSn5CeSRFNSy2SJCqUjxTilSa4SQ5xlKKQKDRKCN2q3FU7VVXMYIY3wFMJUp/6bLGksTwH8lv4RJVFcr2x5QfVmAcPvEcFlJtczgL0Sv+r1GtZzyYMWMVziTYdkzw17RjCmVWv9PEMp523Ntmrc+x1vm30TlNFjBjjVgB8wLVG9Y7Evuq15gZaMNfIRExrjCGcwnbfUR3FexGc02VNM1ylrWqz5DVG6ldKFJcWM1M7wJ8a57Mc2S7b/8fETt378Kqer9sM3sfOIklTgkoSylzwGhSRCspfbsJfBFuE1YouwLgSlkCY/1NmhxE/U9GuG/oUTti4wJdbtwK2GSKRbtqlOUW1BjmolwRqdbbQkIZ9hWQFNVg/NI8c44aty1hCcI0K3Al64ZNBnuORdEO8LKZOUJuM0ZarFZbmJE4qecigYuWHarUpUboFNj573y0PndM2xGvuSYzXDNq2iWa6kxR0lm5q7ZaNwdQ8LLe8m8H4apl0d09a5oD6WcY6pMe4rM9B93jFUUJUlBBXJT7gQHTi3cq4IYiPuo+G+YxSYJlHGCCE7OyKMwNGVMFv8IF+sk+VS8D2FC5Q+NhZ2hlOGeb5ubWP34FN6CrmT1gArPPf2AtecANP0FOy+lY9nGhFfndRRx+PVqkDnGg87uAnkj2/pB7TCNofleK6UHNVwOebFTL1jadL6jF+ZtlWI3Kltb81JYAZn6letUb0Ro2iUq4noj4IDMUaEO1uY8cNEKV7AVER+eZ5kEXfUZRbSBDNev9ckSmK8XpTFlfxwL3JxBhyxM+CILWsMT6w7lPvxxJ5uc3aAeXRHrAcw82cUlhRV0GN/Px+AnYY5hYdXwJ2t4s72B3AHBnDnjQA7YN8x7AIddrt4PTruBjLF1xcAVujYFwAGdaJfexZwnoOqnZ3lqNPaF3BYA7bjvrF/reQHTDXGvbpoDOzPfVg05gVgnGjMuwC29ezmIyyGXaIBvJQLakFYK+KtISfJDklLkvFaycCIatqn70n+Cea//c4dLWJQ552RcMe3EN+Z/kT1tI434Gl9V/e0jfs9K8Lz79jcWANp5Q4NGd3cWHpmwQEsWnXKeUPxG5gCHVWXit+s2yhjnAiq6cuBCvwSBbyDJQUGwl6xzTrPp41bpLuN7GT8It0Q8sep0j0QAredDhKeOyPtXUW8VmjVjCeLUM+TXl2saO9Pk5iaTV3HP0+vLlm5s/R4fqB01w0aj4gYXzjac/tf1dwjP6tZ/Sr2Sd4F3IThOs04NTcMuh/WptfyywOV/ibYEy3AjmVaGjuvHfa5vbDPCa5YtrPvOZcY+kR1tXrxwGeKh6LgDoNfqTHqDDknOERFIdLdzv2B6mV9xQCvGtKbfzjtjdq7856lxX+jomRmE3hwzdEoLChefOPXdWqasKV8s1lBYRYiQ0m7VwSvmwlvsJDd3Eio6xpA14jmdkNXI/wxLK6eCM0zxrVZR15QCFrwUhW1FFhfgV7wEohvB5OA/fzAc53AmR7nu8a4EmLfRlV0/Ji7Rt6oZmeUmNu3epfSDtyp6vU/P0bXc+Evgg1CS2BjHoW6JFle0o5SHTSaL2yVPP+wVRr8rHuCVWKP7VXFShDtfVB7/j8= \ No newline at end of file diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/AppDbContext.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/AppDbContext.cs deleted file mode 100644 index 8064df1..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/AppDbContext.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace SimplePatch.Examples.Core.DAL -{ - public class AppDbContext : DbContext - { - public DbSet People { get; set; } - - public AppDbContext() : base() - { - Init(); - } - - public AppDbContext(DbContextOptions options) : base(options) - { - Init(); - } - - private void Init() - { - Database.EnsureCreated(); - } - - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=SimplePatch.Examples.Core;Trusted_Connection=True;"); - - base.OnConfiguring(optionsBuilder); - } - } -} - - diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914140401_Init.Designer.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914140401_Init.Designer.cs deleted file mode 100644 index ac60be0..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914140401_Init.Designer.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using SimplePatch.Examples.Core.DAL; - -namespace SimplePatch.Examples.Core.DAL.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20170914140401_Init")] - partial class Init - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { - modelBuilder - .HasAnnotation("ProductVersion", "1.1.2") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("SimplePatch.Examples.Core.DAL.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Age"); - - b.Property("Name") - .IsRequired(); - - b.Property("Surname") - .IsRequired(); - - b.HasKey("Id"); - - b.ToTable("People"); - }); - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142601_BirthDate.Designer.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142601_BirthDate.Designer.cs deleted file mode 100644 index 8bfb99d..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142601_BirthDate.Designer.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using SimplePatch.Examples.Core.DAL; - -namespace SimplePatch.Examples.Core.DAL.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20170914142601_BirthDate")] - partial class BirthDate - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { - modelBuilder - .HasAnnotation("ProductVersion", "1.1.2") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("SimplePatch.Examples.Core.DAL.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Age"); - - b.Property("BirthDate"); - - b.Property("Name") - .IsRequired(); - - b.Property("Surname") - .IsRequired(); - - b.HasKey("Id"); - - b.ToTable("People"); - }); - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142601_BirthDate.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142601_BirthDate.cs deleted file mode 100644 index c94f6c0..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142601_BirthDate.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace SimplePatch.Examples.Core.DAL.Migrations -{ - public partial class BirthDate : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "BirthDate", - table: "People", - nullable: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "BirthDate", - table: "People"); - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142632_GlobalId.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142632_GlobalId.cs deleted file mode 100644 index 6991a85..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142632_GlobalId.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace SimplePatch.Examples.Core.DAL.Migrations -{ - public partial class GlobalId : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "GlobalId", - table: "People", - nullable: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "GlobalId", - table: "People"); - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/SimplePatch.Examples.Core.DAL.csproj b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/SimplePatch.Examples.Core.DAL.csproj deleted file mode 100644 index cff45f6..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/SimplePatch.Examples.Core.DAL.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netcoreapp1.1 - - - - - - - - - diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Controllers/ValuesController.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Controllers/ValuesController.cs deleted file mode 100644 index c1f6cbe..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Controllers/ValuesController.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using SimplePatch.Examples.Core.DAL; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace SimplePatch.Examples.Core.WebAPI.Controllers -{ - [Route("api/[controller]")] - public class ValuesController : Controller - { - AppDbContext appDbContext; - - public ValuesController(AppDbContext context) - { - appDbContext = context; - } - - // GET api/values - [HttpGet] - public async Task> Get() - { - return await appDbContext.People.ToListAsync(); - } - - // GET api/values/5 - [HttpGet("{id}")] - public async Task Get(int id) - { - return await appDbContext.People.FindAsync(id); - } - - // POST api/values - [HttpPost] - public async Task Post([FromBody]Person value) - { - await appDbContext.People.AddAsync(value); - await appDbContext.SaveChangesAsync(); - return Ok(value); - } - - // PATCH api/values/5 - [HttpPatch("{id}")] - public async Task PatchAsync(int id, [FromBody]Delta person) - { - // Determines the entity to be updated according to the id parameter - var personToPatch = await appDbContext.People.FindAsync(id); - if (personToPatch == null) return BadRequest("Person not found"); - - // Apply the specified changes to the original entity - person.Patch(personToPatch); - - // Mark the entity as modified - appDbContext.Entry(personToPatch).State = EntityState.Modified; - - // Now the personToPatch variable is updated - - // Save the changes - await appDbContext.SaveChangesAsync(); - - return Ok(personToPatch); - } - - // DELETE api/values/5 - [HttpDelete("{id}")] - public async Task Delete(int id) - { - var personToDelete = await appDbContext.People.FindAsync(id); - if (personToDelete == null) return BadRequest("Person not found"); - appDbContext.People.Remove(personToDelete); - await appDbContext.SaveChangesAsync(); - return Ok(); - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Program.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Program.cs deleted file mode 100644 index 380e917..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; - -namespace SimplePatch.Examples.Core.WebAPI -{ - public class Program - { - public static void Main(string[] args) - { - var host = new WebHostBuilder() - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseStartup() - .UseApplicationInsights() - .Build(); - - host.Run(); - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/SimplePatch.Examples.Core.WebAPI.csproj b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/SimplePatch.Examples.Core.WebAPI.csproj deleted file mode 100644 index d287cb7..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/SimplePatch.Examples.Core.WebAPI.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - netcoreapp1.1 - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Startup.cs b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Startup.cs deleted file mode 100644 index ccf32a9..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/Startup.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using SimplePatch.Examples.Core.DAL; - -namespace SimplePatch.Examples.Core.WebAPI -{ - public class Startup - { - public Startup(IHostingEnvironment env) - { - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) - .AddEnvironmentVariables(); - Configuration = builder.Build(); - - DeltaConfig.Init(x => x.ExcludeProperties(y => y.Id)); - } - - public IConfigurationRoot Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - // Add framework services. - services.AddMvc(); - - services.AddEntityFrameworkSqlServer().AddDbContext(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - loggerFactory.AddConsole(Configuration.GetSection("Logging")); - loggerFactory.AddDebug(); - - app.UseMvc(); - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/appsettings.json b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/appsettings.json deleted file mode 100644 index 5fff67b..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/appsettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - } -} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.sln b/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.sln deleted file mode 100644 index 4d4385c..0000000 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.10 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePatch.Examples.Core.WebAPI", "SimplePatch.Examples.Core.WebAPI\SimplePatch.Examples.Core.WebAPI.csproj", "{1237A459-4EC5-40A5-90C8-E274AB40FBA2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePatch.Examples.Core.DAL", "SimplePatch.Examples.Core.DAL\SimplePatch.Examples.Core.DAL.csproj", "{057C1127-53B8-4E91-AF37-27EAF293A295}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1237A459-4EC5-40A5-90C8-E274AB40FBA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1237A459-4EC5-40A5-90C8-E274AB40FBA2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1237A459-4EC5-40A5-90C8-E274AB40FBA2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1237A459-4EC5-40A5-90C8-E274AB40FBA2}.Release|Any CPU.Build.0 = Release|Any CPU - {057C1127-53B8-4E91-AF37-27EAF293A295}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {057C1127-53B8-4E91-AF37-27EAF293A295}.Debug|Any CPU.Build.0 = Debug|Any CPU - {057C1127-53B8-4E91-AF37-27EAF293A295}.Release|Any CPU.ActiveCfg = Release|Any CPU - {057C1127-53B8-4E91-AF37-27EAF293A295}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {E4EC4A78-AD36-4530-A2F7-724AD921C36E} - EndGlobalSection -EndGlobal diff --git a/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI.sln b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI.sln new file mode 100644 index 0000000..4cd8af1 --- /dev/null +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2035 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePatch.Examples.Core2.WebAPI", "SimplePatch.Examples.Core2.WebAPI\SimplePatch.Examples.Core2.WebAPI.csproj", "{86BAB8D1-3673-4E4B-9562-0D97EC7F3676}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {86BAB8D1-3673-4E4B-9562-0D97EC7F3676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86BAB8D1-3673-4E4B-9562-0D97EC7F3676}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86BAB8D1-3673-4E4B-9562-0D97EC7F3676}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86BAB8D1-3673-4E4B-9562-0D97EC7F3676}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FB1857DD-145C-4552-AAF7-D5A26F4A1112} + EndGlobalSection +EndGlobal diff --git a/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Controllers/PersonsController.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Controllers/PersonsController.cs new file mode 100644 index 0000000..d7662bb --- /dev/null +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Controllers/PersonsController.cs @@ -0,0 +1,80 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using SimplePatch.Examples.Core2.WebAPI.Domain; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SimplePatch.Examples.Core2.WebAPI.Controllers +{ + [Route("[controller]")] + public class PersonsController : Controller + { + private readonly AppDbContext dbContext; + + public PersonsController(AppDbContext dbContext) + { + this.dbContext = dbContext; + } + + // GET /persons + [HttpGet] + public async Task> Get() + { + return await dbContext.People.ToListAsync(); + } + + // GET /persons/5 + [HttpGet("{id}")] + public async Task Get(int id) + { + var person = await dbContext.People.FindAsync(id); + if (person == null) return NotFound(); + return Ok(person); + } + + // POST /persons + [HttpPost] + public async Task Post([FromBody]Person value) + { + await dbContext.People.AddAsync(value); + await dbContext.SaveChangesAsync(); + return Ok(value); + } + + // PATCH /persons/5 + [HttpPatch("{id}")] + public async Task Patch(int id, [FromBody]Delta person) + { + // Determines the entity to be updated according to the id parameter + var personToPatch = await dbContext.People.FindAsync(id); + if (personToPatch == null) return BadRequest("Person not found"); + + if (person != null) + { + // Apply the specified changes to the original entity + person.Patch(personToPatch); + + // Now the personToPatch variable is updated + + // Mark the entity as modified + dbContext.Entry(personToPatch).State = EntityState.Modified; + + // Save the changes + await dbContext.SaveChangesAsync(); + } + + return Ok(personToPatch); + } + + // DELETE /persons/5 + [HttpDelete("{id}")] + public async Task Delete(int id) + { + var personToDelete = await dbContext.People.FindAsync(id); + if (personToDelete == null) return BadRequest("Person not found"); + dbContext.People.Remove(personToDelete); + await dbContext.SaveChangesAsync(); + return Ok(); + } + } +} diff --git a/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Domain/AppDbContext.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Domain/AppDbContext.cs new file mode 100644 index 0000000..e354fee --- /dev/null +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Domain/AppDbContext.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using System.Threading.Tasks; + +namespace SimplePatch.Examples.Core2.WebAPI.Domain +{ + public class AppDbContext : DbContext + { + public DbSet People { get; set; } + + public AppDbContext() : base() { } + public AppDbContext(DbContextOptions options) : base(options) { } + + public static async Task SeedData(AppDbContext context) + { + await context.Database.EnsureCreatedAsync(); + if (!(await context.People.AnyAsync())) + { + context.People.Add(new Person() { Name = "John", Surname = "Doe", Age = 30 }); + context.People.Add(new Person() { Name = "Alyce", Surname = "Adams", Age = 25 }); + await context.SaveChangesAsync(); + } + } + } +} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Person.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Domain/Person.cs similarity index 91% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Person.cs rename to examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Domain/Person.cs index e9eb9f4..ec8862d 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Person.cs +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Domain/Person.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace SimplePatch.Examples.FullNET.DAL +namespace SimplePatch.Examples.Core2.WebAPI.Domain { public class Person { diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142632_GlobalId.Designer.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/20180624155739_Init.Designer.cs similarity index 65% rename from examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142632_GlobalId.Designer.cs rename to examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/20180624155739_Init.Designer.cs index d933aaa..fcdbf96 100644 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914142632_GlobalId.Designer.cs +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/20180624155739_Init.Designer.cs @@ -1,23 +1,27 @@ -using System; +// using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; -using SimplePatch.Examples.Core.DAL; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using SimplePatch.Examples.Core2.WebAPI.Domain; +using System; -namespace SimplePatch.Examples.Core.DAL.Migrations +namespace SimplePatch.Examples.Core2.WebAPI.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20170914142632_GlobalId")] - partial class GlobalId + [Migration("20180624155739_Init")] + partial class Init { protected override void BuildTargetModel(ModelBuilder modelBuilder) { +#pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "1.1.2") + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("SimplePatch.Examples.Core.DAL.Person", b => + modelBuilder.Entity("SimplePatch.Examples.Core2.WebAPI.Domain.Person", b => { b.Property("Id") .ValueGeneratedOnAdd(); @@ -38,6 +42,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("People"); }); +#pragma warning restore 612, 618 } } } diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914140401_Init.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/20180624155739_Init.cs similarity index 80% rename from examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914140401_Init.cs rename to examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/20180624155739_Init.cs index 6b9ee74..45060c1 100644 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/20170914140401_Init.cs +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/20180624155739_Init.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Metadata; +using System; -namespace SimplePatch.Examples.Core.DAL.Migrations +namespace SimplePatch.Examples.Core2.WebAPI.Migrations { public partial class Init : Migration { @@ -16,6 +15,8 @@ protected override void Up(MigrationBuilder migrationBuilder) Id = table.Column(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Age = table.Column(nullable: false), + BirthDate = table.Column(nullable: true), + GlobalId = table.Column(nullable: true), Name = table.Column(nullable: false), Surname = table.Column(nullable: false) }, diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/AppDbContextModelSnapshot.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/AppDbContextModelSnapshot.cs similarity index 73% rename from examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/AppDbContextModelSnapshot.cs rename to examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/AppDbContextModelSnapshot.cs index f982fd9..6e74234 100644 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Migrations/AppDbContextModelSnapshot.cs +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Migrations/AppDbContextModelSnapshot.cs @@ -1,22 +1,23 @@ -using System; +// using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using SimplePatch.Examples.Core.DAL; +using SimplePatch.Examples.Core2.WebAPI.Domain; +using System; -namespace SimplePatch.Examples.Core.DAL.Migrations +namespace SimplePatch.Examples.Core2.WebAPI.Migrations { [DbContext(typeof(AppDbContext))] partial class AppDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { +#pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "1.1.2") + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("SimplePatch.Examples.Core.DAL.Person", b => + modelBuilder.Entity("SimplePatch.Examples.Core2.WebAPI.Domain.Person", b => { b.Property("Id") .ValueGeneratedOnAdd(); @@ -37,6 +38,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("People"); }); +#pragma warning restore 612, 618 } } } diff --git a/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Program.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Program.cs new file mode 100644 index 0000000..8396e44 --- /dev/null +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Program.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using SimplePatch.Examples.Core2.WebAPI.Domain; + +namespace SimplePatch.Examples.Core2.WebAPI +{ + public class Program + { + public static void Main(string[] args) + { + var host = BuildWebHost(args); + + using (var scope = host.Services.CreateScope()) + { + var services = scope.ServiceProvider; + var context = services.GetRequiredService(); + AppDbContext.SeedData(context).Wait(); + } + + host.Run(); + } + + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup() + .Build(); + } +} diff --git a/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI.csproj b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI.csproj new file mode 100644 index 0000000..225a90d --- /dev/null +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.0 + + + + + + + + + + + + + + + + diff --git a/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Startup.cs b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Startup.cs new file mode 100644 index 0000000..80e7efa --- /dev/null +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/Startup.cs @@ -0,0 +1,69 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using SimplePatch.Examples.Core2.WebAPI.Domain; +using SimplePatch.Mapping; +using System; +using System.Globalization; + +namespace SimplePatch.Examples.Core2.WebAPI +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + + DeltaConfig.Init(cfg => + { + cfg.AddEntity(); + + cfg.AddMapping((propertyType, newValue) => + { + var result = new MapResult(); + + if (propertyType != typeof(DateTime?) || newValue.GetType() != typeof(string)) + { + return result.SkipMap(); + } + + if (DateTime.TryParseExact((string)newValue, "dd/MM/yyyy", new CultureInfo("it-IT"), DateTimeStyles.None, out var date)) + { + result.Value = date; + } + else + { + result.Value = null; + } + + return result; + }); + }); + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + + services + .AddEntityFrameworkSqlServer() + .AddDbContext(opt => opt.UseSqlServer(Configuration.GetConnectionString("AppDbContext"))); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvc(); + } + } +} diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/appsettings.Development.json b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/appsettings.Development.json similarity index 100% rename from examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.WebAPI/appsettings.Development.json rename to examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/appsettings.Development.json diff --git a/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/appsettings.json b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/appsettings.json new file mode 100644 index 0000000..528ad7f --- /dev/null +++ b/examples/SimplePatch.Examples.Core2.WebAPI/SimplePatch.Examples.Core2.WebAPI/appsettings.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning" + } + } + }, + "ConnectionStrings": { + "AppDbContext": "Server=(localdb)\\mssqllocaldb;Database=SimplePatch_Examples_Core2_WebAPI;Trusted_Connection=True;" + } +} diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.sln b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.sln new file mode 100644 index 0000000..cad88b4 --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2035 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePatch.Examples.FullNET.WebAPI", "SimplePatch.Examples.FullNET.WebAPI\SimplePatch.Examples.FullNET.WebAPI.csproj", "{9541501A-49CC-4C84-AF74-26F128D4A506}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9541501A-49CC-4C84-AF74-26F128D4A506}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9541501A-49CC-4C84-AF74-26F128D4A506}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9541501A-49CC-4C84-AF74-26F128D4A506}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9541501A-49CC-4C84-AF74-26F128D4A506}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FD39A14B-3355-4B4D-ACBD-CD16008423A4} + EndGlobalSection +EndGlobal diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/App_Start/WebApiConfig.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/App_Start/WebApiConfig.cs similarity index 89% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/App_Start/WebApiConfig.cs rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/App_Start/WebApiConfig.cs index 43fb8f2..a2b0b6d 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/App_Start/WebApiConfig.cs +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/App_Start/WebApiConfig.cs @@ -13,7 +13,7 @@ public static void Register(HttpConfiguration config) config.Routes.MapHttpRoute( name: "DefaultApi", - routeTemplate: "api/{controller}/{id}", + routeTemplate: "{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/ApplicationInsights.config b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/ApplicationInsights.config new file mode 100644 index 0000000..75e12df --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/ApplicationInsights.config @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Controllers/PersonsController.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Controllers/PersonsController.cs new file mode 100644 index 0000000..bced782 --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Controllers/PersonsController.cs @@ -0,0 +1,78 @@ +using SimplePatch.Examples.FullNET.WebAPI.Domain; +using System.Collections.Generic; +using System.Data.Entity; +using System.Threading.Tasks; +using System.Web.Http; + +namespace SimplePatch.Examples.FullNET.WebAPI.Controllers +{ + [RoutePrefix("persons")] + public class PersonsController : ApiController + { + private readonly AppDbContext dbContext = new AppDbContext(); + + // GET api/persons + [HttpGet] + public async Task> Get() + { + return await dbContext.People.ToListAsync(); + } + + // GET api/persons/5 + [HttpGet(), Route("{id}")] + public async Task Get(int id) + { + var person = await dbContext.People.FindAsync(id); + + if (person == null) return NotFound(); + + return Ok(person); + } + + // POST api/persons + [HttpPost] + public async Task Post([FromBody]Person value) + { + dbContext.People.Add(value); + await dbContext.SaveChangesAsync(); + return Ok(value); + } + + // PATCH api/persons/5 + [HttpPatch, Route("{id}")] + public async Task Patch(int id, [FromBody]Delta person) + { + // Determines the entity to be updated according to the id parameter + + var personToPatch = await dbContext.People.FindAsync(id); + if (personToPatch == null) return BadRequest("Person not found"); + + if (person != null) + { + // Apply the specified changes to the original entity + person.Patch(personToPatch); + + // Now the personToPatch variable is updated + + // Mark the entity as modified + dbContext.Entry(personToPatch).State = EntityState.Modified; + + // Save the changes + await dbContext.SaveChangesAsync(); + } + + return Ok(personToPatch); + } + + // DELETE api/persons/5 + [HttpDelete(), Route("{id}")] + public async Task Delete(int id) + { + var personToDelete = await dbContext.People.FindAsync(id); + if (personToDelete == null) return BadRequest("Person not found"); + dbContext.People.Remove(personToDelete); + await dbContext.SaveChangesAsync(); + return Ok(); + } + } +} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/AppDbContext.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Domain/AppDbContext.cs similarity index 58% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/AppDbContext.cs rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Domain/AppDbContext.cs index 2e92e1a..ad5b7a2 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/AppDbContext.cs +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Domain/AppDbContext.cs @@ -1,9 +1,11 @@ using System.Data.Entity; -namespace SimplePatch.Examples.FullNET.DAL +namespace SimplePatch.Examples.FullNET.WebAPI.Domain { public class AppDbContext : DbContext { public DbSet People { get; set; } + + public AppDbContext() : base() { } } -} +} \ No newline at end of file diff --git a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Person.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Domain/Person.cs similarity index 90% rename from examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Person.cs rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Domain/Person.cs index 4dd6e2f..175bb08 100644 --- a/examples/SimplePatch.Examples.Core/SimplePatch.Examples.Core.DAL/Person.cs +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Domain/Person.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace SimplePatch.Examples.Core.DAL +namespace SimplePatch.Examples.FullNET.WebAPI.Domain { public class Person { diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Global.asax b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Global.asax similarity index 100% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Global.asax rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Global.asax diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Global.asax.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Global.asax.cs new file mode 100644 index 0000000..80b758b --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Global.asax.cs @@ -0,0 +1,18 @@ +using SimplePatch.Examples.FullNET.WebAPI.Domain; +using System.Web.Http; + +namespace SimplePatch.Examples.FullNET.WebAPI +{ + public class WebApiApplication : System.Web.HttpApplication + { + protected void Application_Start() + { + GlobalConfiguration.Configure(WebApiConfig.Register); + + DeltaConfig.Init(cfg => { + cfg.AddEntity() + .Property(x => x.Id).Exclude(); + }); + } + } +} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.Designer.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.Designer.cs similarity index 78% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.Designer.cs rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.Designer.cs index 1e1e618..74ffaba 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.Designer.cs +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.Designer.cs @@ -1,19 +1,19 @@ // -namespace SimplePatch.Examples.FullNET.DAL.Migrations +namespace SimplePatch.Examples.FullNET.WebAPI.Migrations { using System.CodeDom.Compiler; using System.Data.Entity.Migrations; using System.Data.Entity.Migrations.Infrastructure; using System.Resources; - [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] + [GeneratedCode("EntityFramework.Migrations", "6.2.0-61023")] public sealed partial class Init : IMigrationMetadata { private readonly ResourceManager Resources = new ResourceManager(typeof(Init)); string IMigrationMetadata.Id { - get { return "201708250935300_Init"; } + get { return "201806241631571_Init"; } } string IMigrationMetadata.Source diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.cs similarity index 81% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.cs rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.cs index c4660c1..2975450 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.cs +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.cs @@ -1,8 +1,7 @@ -namespace SimplePatch.Examples.FullNET.DAL.Migrations +namespace SimplePatch.Examples.FullNET.WebAPI.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class Init : DbMigration { public override void Up() @@ -15,6 +14,8 @@ public override void Up() Name = c.String(nullable: false), Surname = c.String(nullable: false), Age = c.Int(nullable: false), + BirthDate = c.DateTime(), + GlobalId = c.Guid(), }) .PrimaryKey(t => t.Id); diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.resx b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.resx similarity index 79% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.resx rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.resx index d9c7626..033845d 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.resx +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/201806241631571_Init.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - H4sIAAAAAAAEAM1X227jNhB9L7D/IPBpC2TFXF7aQN6F15ciaJwEK+++09LYJpYiVZIK7G/rQz+pv9Ch7pZ8S7JoiwCBRc2cuXDOzOjvP/8KPm0S4T2DNlzJAbnyL4kHMlIxl6sByezywy/k08d3PwWTONl43yq5GyeHmtIMyNra9JZSE60hYcZPeKSVUUvrRyqhLFb0+vLyV3p1RQEhCGJ5XvAlk5YnkD/g40jJCFKbMTFTMQhTnuObMEf1HlgCJmURDEjIk1TAE7PR2p9smHsw/jQT4mEy98fDe+INBWfoVwhiSTwmpbLMote3Xw2EViu5ClM8YGK+TQHllkwYKKO5bcTPDezy2gVGG8UKKsqMVckLAa9uykzRrvqr8k3qTGIuJ5hzu3VR5/kckCe8TgfcNXU7EtqJnU62X0BceKcEL+rawRJzfxfeKBM20zCQkFnNxIX3lC0Ej36H7Vx9BzmQqN32HyPAdzsHePSkVQrabr/AsozqLiYe3dWjXcVaraVTRHwn7c018R7QOFsIqMujlZ3QKg2/gQTNLMQYswUtHQbkCe5Z79hy/ytrWI9INOLN2OYe5MquBwR/Em/KNxBXJ6UHXyVHXqKS1Rns8fC41TDT8j8xPFzBqdweB/jMtV2PMdUVjPs950lHMaBNfferHjuMZRyvrPIqTccLdwgbu4cA2CpKDpgy7F0XC9AQbM0khRVPvMaFov/4FcX2eVr71LQ7WvS7qi/SA40xmLE0xftrNcryxAuLLjn6EL68YSQFBo3Mnr5Re1tbQhqwFXTeomn0dMq1sXhPbMHcBY/ipCe2cwMHsluZ2klytwM0Oa/E3e+y6s/rX13MJp9TDDFBXufRQu1Y3Tt7ivnMYoLpPR1mpESWyENd6ph20TPa+sXJ+Qg1/9sg9eH5ODmd2xj5wfn6LTa3UVrHfayAdm6jWwK0VwOd/t+tqGNs7IrU1mtWdtgXlEw4vbv0qFGIEA+T9MxjR4twaywkvhPwwz/ESHCMtxGYMcmXYGwxIgkuDNedhef/s3xQY2Jx1gbyr0957nJ6co6/cFC1B7t8ZjpaM/0+YZuf3zqs3wbWGsB52G8dvzH+tvn4bZCKzeD147g/M07N22KsHpy3BbHQ2YVCvwsnqyH9ylncZ3lA298xwRgMXzUQ7qtGQuTo04BWMndyqapEY1htjyqRzj3MwDJMPRtqy5cssvg6AmPyLe4bExmKTJIFxHfyMbNpZofGQLIQO+toQI/bzxeOXZ+Dx9Q9mR8RArrJXfU8ys8ZF3Ht97Rfh4cgXKGUlEWvcItFuNW2RnpQ8kygMn1jSEE6ws8BlwMEM48yZM/wGt9wabyHFYu2VbM+DHL6InbTHow5W2mWmBKj0Xff5tR9nH/8B2ZKLznODwAA + H4sIAAAAAAAEAM1Y247bNhB9L5B/EPTUAhtxLy/tQk7g+LIwGu8akZM+09LYJkKRCkkt7G/LQz4pv9Ch7pZ83S3awoAhUTOHM8OZo2P//P7Df7+JufMMSjMpeu6Nd+06IEIZMbHqualZvv3dff/uzS/+KIo3zpfS7s7aoafQPXdtTHJPiA7XEFPtxSxUUsul8UIZExpJcnt9/Qe5uSGAEC5iOY7/KRWGxZDd4O1AihASk1I+lRFwXazjkyBDdR5pDDqhIfTcgMUJhxk14dobbai90d445fxxNPf+gkV/NvGGMqZMuE6fM4oRBsCXrkOFkIYajP/+s4bAKClWQYILlM+3CaDdknINRV73tfm5KV7f2hRJ7VhChak2GNFlgDd3Rc1I2/1FlXermmJVR1h9s7VZZ5XtuTM8WAvc3up+wJU1u6TsXg525ZzhclW1FHae/Vw5g5SbVEFPQGoU5VfOLF1wFv4J27n8CqInEKCZDKaDz3YWcGmmZALKbD/BskhxErkO2fUjbcfKreGTpz8R5u7WdR5xc7rgUPVKo1SBkQoeQICiBiJM24ASFgOyand2b+1lv8vdsDlx/lxnSjcfQazMuufipeuM2QaicqWI4LNgOK7oZFQKeyI8vmuQKvGfbNxfwanaHgf4wJRZD7HUJYy9nrP4pOMDlwvK66N9SFmrNXxSD0h3bJCsDLY5qDKTJBku7CJszJ4JQq4phkgXpdqNLgcNwFSjKHFQXKcOIScwr5zRfZFWMdXMSXLqLCmWHOBYf0qTBM+8wbnFihPkhDt4G1zOOHGOQUK9h3iqaKudcHToClpPcWuMdMyUNni2dEFtUwyiuGO2cwIHqltutVPkNmvUNS/N7XUxKZcSYBu9ruwYk42RFbK8oQqxouGOY/YipJyqPfw0kDyNxSGOO+adM07TP185H6FijyZItXg+TkYGTYxs4Xz/Bhc0URrL52PV9NCEqle7SD5pnWu7rUinr1rvoXaXHpvwtkm1ezXprYn2i+k6La0645abuA6W6JlFdtSCrTYQe9bAC77xAWeYb20wpYItQZv8Ve2iirltqbD/jyIiWkf8LFn0r6sNZmt6Uk9c+MJsCgzxTFW4purXmG5+e61oeB1YQwhkab9WBkR4bTIZUCPlCuUyWZAK9i0FllV7yUAdx7tMMnTfa6c0Qf7qP6gJ8kHF5BcS48+DLIXEC/VClzV80vzZ5g9Bs1UNYX/ECQjtONagpc1ELGVZb0yrGVFp0jqOKRiKR0n7CstPQ4OPQ9A6U6dfKE/RZBQvIJqIp9QkqelrDfGC78hsnxzfPxNFuzH7T4m90/9EChgms934JD6kjEdV3ONuXx+CsI1SUABGheoc4VbbCulRijOBivINIQFhW3oOKGAQTD+JgD7DS2JDYfsRVjTcluR/GOT0QeyW3R8yulI01gVG7W//iiD2v4h3fwPTIeNHvRAAAA== dbo diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/Configuration.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/Configuration.cs new file mode 100644 index 0000000..fa3d9e4 --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Migrations/Configuration.cs @@ -0,0 +1,28 @@ +namespace SimplePatch.Examples.FullNET.WebAPI.Migrations +{ + using SimplePatch.Examples.FullNET.WebAPI.Domain; + using System.Data.Entity.Migrations; + using System.Linq; + + internal sealed class Configuration : DbMigrationsConfiguration + { + public Configuration() + { + AutomaticMigrationsEnabled = false; + } + + protected override void Seed(AppDbContext context) + { + // This method will be called after migrating to the latest version. + + // You can use the DbSet.AddOrUpdate() helper extension method + // to avoid creating duplicate seed data. + if (!(context.People.Any())) + { + context.People.Add(new Person() { Name = "John", Surname = "Doe", Age = 30 }); + context.People.Add(new Person() { Name = "Alyce", Surname = "Adams", Age = 25 }); + context.SaveChanges(); + } + } + } +} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Properties/AssemblyInfo.cs b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Properties/AssemblyInfo.cs similarity index 67% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Properties/AssemblyInfo.cs rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Properties/AssemblyInfo.cs index 5230acd..bf28ecb 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Properties/AssemblyInfo.cs +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Properties/AssemblyInfo.cs @@ -2,7 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// Le informazioni generali relative a un assembly sono controllate dal seguente +// Le informazioni generali relative a un assembly sono controllate dal seguente // set di attributi. Modificare i valori di questi attributi per modificare le informazioni // associate a un assembly. [assembly: AssemblyTitle("SimplePatch.Examples.FullNET.WebAPI")] @@ -10,26 +10,26 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SimplePatch.Examples.FullNET.WebAPI")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// Se si imposta ComVisible su false, i tipi in questo assembly non saranno visibili -// ai componenti COM. Se è necessario accedere a un tipo in questo assembly da +// Se si imposta il valore di ComVisible su falso, i tipi nell'assembly non sono più visibili +// dai componenti COM. Se è necessario accedere al tipo nell'assembly da // COM, impostare su true l'attributo ComVisible per tale tipo. [assembly: ComVisible(false)] -// Se il progetto viene esposto a COM, il GUID seguente verrà utilizzato per creare l'ID della libreria dei tipi -[assembly: Guid("50fa66a1-f8a3-4301-b842-ab5f48d9be6b")] +// Se il progetto viene esposto a COM, il GUID seguente verrà utilizzato per creare l'ID di typelib +[assembly: Guid("a12e2144-e630-4749-8f1f-ee08d6030e61")] // Le informazioni sulla versione di un assembly sono costituite dai quattro valori seguenti: // // Versione principale -// Versione secondaria +// Versione secondaria // Numero build // Revisione // -// È possibile specificare tutti i valori oppure impostare valori predefiniti per i numeri relativi alla revisione e alla build +// È possibile specificare tutti i valori o lasciare i valori predefiniti per Revisione e Numeri build // utilizzando l'asterisco (*) come illustrato di seguito: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.csproj b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.csproj similarity index 65% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.csproj rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.csproj index 5dd3164..765aed0 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.csproj +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI.csproj @@ -1,6 +1,7 @@ - - - + + + + Debug @@ -8,15 +9,16 @@ 2.0 - {50FA66A1-F8A3-4301-B842-AB5F48D9BE6B} + {9541501A-49CC-4C84-AF74-26F128D4A506} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties SimplePatch.Examples.FullNET.WebAPI SimplePatch.Examples.FullNET.WebAPI v4.6.1 + false true - + false @@ -45,80 +47,88 @@ - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll - - ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll - - ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll - - ..\packages\SimplePatch.1.2.1\lib\net451\SimplePatch.dll + + ..\packages\SimplePatch.3.0.0\lib\net451\SimplePatch.dll + + + + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.6\lib\net45\System.Net.Http.Formatting.dll - - - - - + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.6\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.6\lib\net45\System.Web.Http.WebHost.dll + - + + - - - - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + + - - ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll + - - - - - - + + + Global.asax + + + 201806241631571_Init.cs + + - - + + + + Designer + + Web.config - - + + Web.config - + - + - - {7109d4aa-c7c6-4c60-bb81-9ca79417d3f6} - SimplePatch.Examples.FullNET.DAL - + + + + + 201806241631571_Init.cs + 10.0 @@ -127,15 +137,18 @@ + + + True True - 64816 + 51004 / - http://localhost:64816/ + http://localhost:51004/ False False @@ -149,14 +162,13 @@ Questo progetto fa riferimento a uno o più pacchetti NuGet che non sono presenti in questo computer. Usare lo strumento di ripristino dei pacchetti NuGet per scaricarli. Per altre informazioni, vedere http://go.microsoft.com/fwlink/?LinkID=322105. Il file mancante è {0}. - - + + - + --> \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.Debug.config b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.Debug.config new file mode 100644 index 0000000..d56adf3 --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.Release.config b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.Release.config new file mode 100644 index 0000000..19f5296 --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.config b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.config similarity index 59% rename from examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.config rename to examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.config index 78815e7..8f964e0 100644 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.config +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/Web.config @@ -1,6 +1,6 @@  @@ -8,12 +8,23 @@
- + + + + + + + + + + + + @@ -24,20 +35,24 @@ - - + + - - + + - - + + - - + + + + + + @@ -53,8 +68,8 @@ - - + + \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/favicon.ico b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/favicon.ico new file mode 100644 index 0000000..a3a7999 Binary files /dev/null and b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/favicon.ico differ diff --git a/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/packages.config b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/packages.config new file mode 100644 index 0000000..d594752 --- /dev/null +++ b/examples/SimplePatch.Examples.FullNET.WebAPI/SimplePatch.Examples.FullNET.WebAPI/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/App.config b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/App.config deleted file mode 100644 index 7e1d79c..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/App.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - -
- - - - - - - - - - - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.resx b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.resx deleted file mode 100644 index 0e7dc9a..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201708250935300_Init.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - H4sIAAAAAAAEAM1X227bOBB9X6D/IPBpF0jFXF52A6mF15dFsHESVGnfaWlsE6VILUkF9rf1oZ+0v7BD3S3ZsZMW2yJAYI1mzswczoX698vX4P0mFd4TaMOVDMmFf048kLFKuFyFJLfLt7+T9+/e/BJMk3Tjfar1rpweWkoTkrW12TWlJl5Dyoyf8lgro5bWj1VKWaLo5fn5H/TiggJCEMTyvOBDLi1PoXjAx7GSMWQ2Z2KuEhCmkuObqED17lgKJmMxhCTiaSbggdl47U83zD0Yf5YLcTd99CejW+KNBGcYVwRiSTwmpbLMYtTXHw1EViu5ijIUMPG4zQD1lkwYqLK5btVPTez80iVGW8MaKs6NVekLAS+uKqZo3/xVfJOGSeRyipzbrcu64DMkD3icDrjv6nostFM7TrZfQpx5xxTPmtrBEnN/Z944FzbXEErIrWbizHvIF4LHf8P2UX0GGUq07saPGeC7HQGKHrTKQNvtB1hWWd0kxKO7drRv2Jh1bMqMb6S9uiTeHTpnCwFNeXTYiazS8BdI0MxCgjlb0NJhQEHwwHvPl/tfe8N6xEYj3pxtbkGu7Dok+JN4M76BpJZUEXyUHPsSjazOYU+Ez3uNci1/iOPRCo5x2wUIaFumw+LFQWEZR+Zr8CybLJwQNnZPHWPHV6Vsquh3Qy1BI7BNQygsXOK1IZRjxK87ZV+kTUzt1KLl2KrHGz0w34I5yzI8hs68qyReVA678dvo5X2flhg0Nnvav4m28YTVzFbQe4uuMdIZ18ZOmGUL5s5pnKQDtZ0TOMBu7WqH5H4jt5zX6u53VbynjaE+ZsvnDFNMsT2LbKEJrBmBA8Ni9TDB9J5BMVYiT+WhYfOcddn6XftScjpC08ZdkEZ4Ok7RlV2MQjC0D2iPwf6x0cG59UZvvwqe66C+SuO96aRexwRV9R6/NgzKuVQhHhLzxBNXytHWWEh9p+BH/4ix4JhvqzBnki/B2HI7EdzVl727xs+z96kxiThp+f/vC5Y7To+u0Bcume5OlU9Mx2umf03Z5rdv3ZPfBtbZfUXa33HzDcfzsdVWbrCDq63sh5AkC4Uxl1HW+/CVa2/YnAHt3vyDCRi+aiHcd4CE2FV9C1rr3MilqunFtLoR1So99udgWYLsjLTlSxZbfB2DMcW95xMTOapM0wUkN/I+t1luR8ZAuhA7F7iAPu+/2O27MQf3mXsy3yMFDJNjCnAv/8y5SJq4Z3vK5wCEK5Sq0zAqvPch3GrbIN0peSJQRd8EMpCuTx8B9zCCmXsZsSd4TWx4P7uFFYu39Yw9DHL8IHZpDyacrTRLTYXR2ruvWeo+Z9/9BzgOqdgADwAA - - - dbo - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.Designer.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.Designer.cs deleted file mode 100644 index 0d5d21b..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -namespace SimplePatch.Examples.FullNET.DAL.Migrations -{ - using System.CodeDom.Compiler; - using System.Data.Entity.Migrations; - using System.Data.Entity.Migrations.Infrastructure; - using System.Resources; - - [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] - public sealed partial class BirthDate : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(BirthDate)); - - string IMigrationMetadata.Id - { - get { return "201709141340179_BirthDate"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.cs deleted file mode 100644 index 42c2cce..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340179_BirthDate.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace SimplePatch.Examples.FullNET.DAL.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class BirthDate : DbMigration - { - public override void Up() - { - AddColumn("dbo.People", "BirthDate", c => c.DateTime()); - } - - public override void Down() - { - DropColumn("dbo.People", "BirthDate"); - } - } -} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.Designer.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.Designer.cs deleted file mode 100644 index daa661d..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -namespace SimplePatch.Examples.FullNET.DAL.Migrations -{ - using System.CodeDom.Compiler; - using System.Data.Entity.Migrations; - using System.Data.Entity.Migrations.Infrastructure; - using System.Resources; - - [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] - public sealed partial class GlobalId : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(GlobalId)); - - string IMigrationMetadata.Id - { - get { return "201709141340313_GlobalId"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.cs deleted file mode 100644 index 285a4a3..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace SimplePatch.Examples.FullNET.DAL.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class GlobalId : DbMigration - { - public override void Up() - { - AddColumn("dbo.People", "GlobalId", c => c.Guid()); - } - - public override void Down() - { - DropColumn("dbo.People", "GlobalId"); - } - } -} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.resx b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.resx deleted file mode 100644 index 0878515..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/201709141340313_GlobalId.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - H4sIAAAAAAAEAM1Y227jNhB9L9B/EPjUAlkxl5c2kHfhtZ0gaJwEK+++09LYJkqRWpIK7G/rQz+pv9Ch7pZ8TYq2CBBYo5kzF84cjv3XH38Gn9aJ8F5BG67kgFz5l8QDGamYy+WAZHbx4Rfy6eOPPwSTOFl73yq9G6eHltIMyMra9JZSE60gYcZPeKSVUQvrRyqhLFb0+vLyV3p1RQEhCGJ5XvAlk5YnkD/g40jJCFKbMTFVMQhTyvFNmKN6TywBk7IIBiTkSSrghdlo5U/WzD0Y/y4T4mky88fDR+INBWcYVwhiQTwmpbLMYtS3Xw2EViu5DFMUMDHbpIB6CyYMlNncNuqnJnZ57RKjjWEFFWXGquRMwKubslK0a/6mepO6kljLCdbcblzWeT0H5AWP0wF3Xd2OhHZqx4vtFxAX3jHFi7p3sMXc34U3yoTNNAwkZFYzceG9ZHPBo99gM1O/gxxItG7Hjxnguy0Bil60SkHbzRdYlFk9xMSj23a0a1ibtWyKjB+kvbkm3hM6Z3MBdXu0qhNapeEeJGhmIcacLWjpMCAvcM97x5f7X3nDfsRBI96UrR9BLu1qQPAj8e74GuJKUkbwVXKcSzSyOoMdER72GmZa/ieOh0s4VtvDAJ+5tqsxlrqCcZ9nPDlqeC/UnInmaO8z3mmNgDYz0Z8UZCXLOB5zlUmajudOCGu7Y2iQXsq5MWWptqMrQEOw9fQpnBLiNSEUnOVXY7kr0jqmhiJpwZEVl9I9ZBpMWZrimbfItZR4YcGsow/h+SSTFBg0Mju4po629oSjw5bQeYuuMdI7ro3Fs2Vz5ppiFCc9ta0T2FPdytVWkbus0dS8Unefy0k5jfO6mE097zDFBLkgzxbqwGq+7Rnm9xwTTO9gpZESWSL3Mdsh64Jn2vaF5HSEmjPaILXwdJycAtoYueB0+xYDtFFa4tOxGlJoQzXSPlJAO+fabSba66bO7dPtzUNz3VWpvdfz3ZnjoJyp45tTb8gKFeJhiV557AYs3BgLie8U/PC7GAmO+TYKUyb5AowtLmiC68p1Z936/6w+1JhYnLT//Os7Bnc1PbpFnHlNttcK+cp0tGL6p4Stf37vqvA+sNb1n6f93ss/xs82v/wbpGIvOW8ZyCT/ngHPq73goA/jnbco9G+zY5tAceHv3QSKQcXk5wrjL4Ks1oc3bgl91gho+1tZMAbDlw2E+44mIXLj2IBWOg9yoap6Y1rtiCqVznFMwTI8SjbUWH4WWXwdgTH5TvqNiQxVJskc4gf5nNk0s0NjIJmLreU6oIf956vQdszBc+qezD+RAobJXTc+y88ZF3Ed912/r/dBuEYpKQCjwp0c4ZabGulJyROByvKNIQXpWnoGuLYgmHmWIXuFt8SG6+wjLFm0qch/P8jxg9guezDmbKlZYkqMxt790kDdTw0f/wbFiCc/nBAAAA== - - - dbo - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/Configuration.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/Configuration.cs deleted file mode 100644 index faacad3..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Migrations/Configuration.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace SimplePatch.Examples.FullNET.DAL.Migrations -{ - using System.Data.Entity.Migrations; - - internal sealed class Configuration : DbMigrationsConfiguration - { - public Configuration() - { - AutomaticMigrationsEnabled = false; - } - - protected override void Seed(AppDbContext context) - { - context.People.AddOrUpdate( - new Person() { Id = 1, Name = "Name 1", Surname = "Surname 1", Age = 21 }, - new Person() { Id = 2, Name = "Name 2", Surname = "Surname 2", Age = 22 }, - new Person() { Id = 3, Name = "Name 3", Surname = "Surname 3", Age = 23 }, - new Person() { Id = 4, Name = "Name 4", Surname = "Surname 4", Age = 24 }, - new Person() { Id = 5, Name = "Name 5", Surname = "Surname 5", Age = 25 }, - new Person() { Id = 6, Name = "Name 6", Surname = "Surname 6", Age = 26 }, - new Person() { Id = 7, Name = "Name 7", Surname = "Surname 7", Age = 27 }, - new Person() { Id = 8, Name = "Name 8", Surname = "Surname 8", Age = 28 }, - new Person() { Id = 9, Name = "Name 9", Surname = "Surname 9", Age = 29 } - ); - } - } -} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Properties/AssemblyInfo.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Properties/AssemblyInfo.cs deleted file mode 100644 index e5ae2dd..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Le informazioni generali relative a un assembly sono controllate dal seguente -// set di attributi. Modificare i valori di questi attributi per modificare le informazioni -// associate a un assembly. -[assembly: AssemblyTitle("SimplePatch.Examples.FullNET.DAL")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SimplePatch.Examples.FullNET.DAL")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Se si imposta ComVisible su false, i tipi in questo assembly non saranno visibili -// ai componenti COM. Se è necessario accedere a un tipo in questo assembly da -// COM, impostare su true l'attributo ComVisible per tale tipo. -[assembly: ComVisible(false)] - -// Se il progetto viene esposto a COM, il GUID seguente verrà utilizzato come ID della libreria dei tipi -[assembly: Guid("7109d4aa-c7c6-4c60-bb81-9ca79417d3f6")] - -// Le informazioni sulla versione di un assembly sono costituite dai seguenti quattro valori: -// -// Versione principale -// Versione secondaria -// Numero di build -// Revisione -// -// È possibile specificare tutti i valori oppure impostare valori predefiniti per i numeri relativi alla revisione e alla build -// usando l'asterisco '*' come illustrato di seguito: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/SimplePatch.Examples.FullNET.DAL.csproj b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/SimplePatch.Examples.FullNET.DAL.csproj deleted file mode 100644 index 6f9a78f..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/SimplePatch.Examples.FullNET.DAL.csproj +++ /dev/null @@ -1,83 +0,0 @@ - - - - - Debug - AnyCPU - {7109D4AA-C7C6-4C60-BB81-9CA79417D3F6} - Library - Properties - SimplePatch.Examples.FullNET.DAL - SimplePatch.Examples.FullNET.DAL - v4.6.1 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll - - - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - - - - - - - - - - - - - - - - 201708250935300_Init.cs - - - - 201709141340179_BirthDate.cs - - - - 201709141340313_GlobalId.cs - - - - - - - - - - - - 201708250935300_Init.cs - - - 201709141340179_BirthDate.cs - - - 201709141340313_GlobalId.cs - - - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/packages.config b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/packages.config deleted file mode 100644 index 373cff5..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.DAL/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Controllers/ValuesController.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Controllers/ValuesController.cs deleted file mode 100644 index 0d2edab..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Controllers/ValuesController.cs +++ /dev/null @@ -1,74 +0,0 @@ -using SimplePatch.Examples.FullNET.DAL; -using System.Collections.Generic; -using System.Data.Entity; -using System.Threading.Tasks; -using System.Web.Http; - -namespace SimplePatch.Examples.FullNET.WebAPI.Controllers -{ - public class ValuesController : ApiController - { - AppDbContext appDbContext; - - public ValuesController() - { - appDbContext = new AppDbContext(); - } - - // GET api/values - [HttpGet] - public async Task> Get() - { - return await appDbContext.People.ToListAsync(); - } - - // GET api/values/5 - [HttpGet] - public async Task Get(int id) - { - return await appDbContext.People.FindAsync(id); - } - - // POST api/values - [HttpPost] - public async Task Post([FromBody]Person value) - { - appDbContext.People.Add(value); - await appDbContext.SaveChangesAsync(); - return Ok(value); - } - - // PATCH api/values/5 - [HttpPatch] - public async Task PatchAsync(int id, [FromBody]Delta person) - { - // Determines the entity to be updated according to the id parameter - var personToPatch = await appDbContext.People.FindAsync(id); - if (personToPatch == null) return BadRequest("Person not found"); - - // Apply the specified changes to the original entity - person.Patch(personToPatch); - - // Mark the entity as modified - appDbContext.Entry(personToPatch).State = EntityState.Modified; - - // Now the personToPatch variable is updated - - // Save the changes - await appDbContext.SaveChangesAsync(); - - return Ok(personToPatch); - } - - // DELETE api/values/5 - [HttpDelete] - public async Task Delete(int id) - { - var personToDelete = await appDbContext.People.FindAsync(id); - if (personToDelete == null) return BadRequest("Person not found"); - appDbContext.People.Remove(personToDelete); - await appDbContext.SaveChangesAsync(); - return Ok(); - } - } -} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Global.asax.cs b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Global.asax.cs deleted file mode 100644 index e55442e..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Global.asax.cs +++ /dev/null @@ -1,16 +0,0 @@ -using SimplePatch.Examples.FullNET.DAL; -using System.Web; -using System.Web.Http; - -namespace SimplePatch.Examples.FullNET.WebAPI -{ - public class WebApiApplication : HttpApplication - { - protected void Application_Start() - { - GlobalConfiguration.Configure(WebApiConfig.Register); - - DeltaConfig.Init(x => x.ExcludeProperties(y => y.Id)); - } - } -} diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.Debug.config b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.Debug.config deleted file mode 100644 index 5364362..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.Debug.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.Release.config b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.Release.config deleted file mode 100644 index 40150c7..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/Web.Release.config +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/packages.config b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/packages.config deleted file mode 100644 index 0c12064..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.WebAPI/packages.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.sln b/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.sln deleted file mode 100644 index a550617..0000000 --- a/examples/SimplePatch.Examples.FullNET/SimplePatch.Examples.FullNET.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.10 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePatch.Examples.FullNET.DAL", "SimplePatch.Examples.FullNET.DAL\SimplePatch.Examples.FullNET.DAL.csproj", "{7109D4AA-C7C6-4C60-BB81-9CA79417D3F6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePatch.Examples.FullNET.WebAPI", "SimplePatch.Examples.FullNET.WebAPI\SimplePatch.Examples.FullNET.WebAPI.csproj", "{50FA66A1-F8A3-4301-B842-AB5F48D9BE6B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7109D4AA-C7C6-4C60-BB81-9CA79417D3F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7109D4AA-C7C6-4C60-BB81-9CA79417D3F6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7109D4AA-C7C6-4C60-BB81-9CA79417D3F6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7109D4AA-C7C6-4C60-BB81-9CA79417D3F6}.Release|Any CPU.Build.0 = Release|Any CPU - {50FA66A1-F8A3-4301-B842-AB5F48D9BE6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50FA66A1-F8A3-4301-B842-AB5F48D9BE6B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50FA66A1-F8A3-4301-B842-AB5F48D9BE6B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50FA66A1-F8A3-4301-B842-AB5F48D9BE6B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {DDF4AA0F-49AA-47B1-854B-AC55B0E38EA9} - EndGlobalSection -EndGlobal diff --git a/simplepatch.png b/simplepatch.png index d345fef..1757128 100644 Binary files a/simplepatch.png and b/simplepatch.png differ diff --git a/simplepatch.svg b/simplepatch.svg new file mode 100644 index 0000000..c58aaca --- /dev/null +++ b/simplepatch.svg @@ -0,0 +1,45 @@ + + + + + simplepatch + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/SimplePatch.Tests/ConfigurationTests/Globals.cs b/src/SimplePatch.Tests/ConfigurationTests/Globals.cs new file mode 100644 index 0000000..08529bb --- /dev/null +++ b/src/SimplePatch.Tests/ConfigurationTests/Globals.cs @@ -0,0 +1,77 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SimplePatch.Mapping; +using static SimplePatch.Tests.DeltaUtils; + +namespace SimplePatch.Tests.ConfigurationTests +{ + [TestClass, TestCategory(TestCategories.Configuration)] + public class Globals : TestBase + { + [TestCleanup] + public void TestCleanup() + { + DeltaConfig.Clean(); + } + + [TestMethod] + public void IgnoreLetterCase() + { + DeltaConfig.Init(cfg => + { + cfg + .IgnoreLetterCase() + .AddEntity(); + }); + + GetDelta("AgE", 23).Patch(John); + Assert.AreEqual(23, John.Age); + } + + [TestMethod] + public void MappingFunction() + { + DeltaConfig.Init(cfg => + { + cfg + + /* When the target property type is int and the input is string, + then the assigned value will be the length of the input string*/ + .AddMapping((propType, newValue) => + { + var result = new MapResult(); + + if (propType != typeof(int)) return result.SkipMap(); + if (newValue.GetType() != typeof(string)) return result.SkipMap(); + + result.Value = newValue.ToString().Length; + + return result; + }) + + /* When the target property is double and the input is string, + then the assigned value will be the length of the string + 0.5*/ + .AddMapping((propType, newValue) => + { + var result = new MapResult(); + + if (propType != typeof(double)) return result.SkipMap(); + + if (newValue.GetType() != typeof(string)) return result.SkipMap(); + + result.Value = newValue.ToString().Length + 0.5; + + return result; + }) + .AddEntity(); + }); + + // First mapping function will be executed here, Age type is int + GetDelta(x => x.Age, "abc").Patch(John); + Assert.AreEqual("abc".Length, John.Age); + + // Second mapping function will be executed here, Height type is double + GetDelta(x => x.Height, "abcdef").Patch(John); + Assert.AreEqual("abcdef".Length + 0.5, John.Height); + } + } +} diff --git a/src/SimplePatch.Tests/ConfigurationTests/Properties.cs b/src/SimplePatch.Tests/ConfigurationTests/Properties.cs new file mode 100644 index 0000000..3cf0121 --- /dev/null +++ b/src/SimplePatch.Tests/ConfigurationTests/Properties.cs @@ -0,0 +1,127 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SimplePatch.Mapping; +using System; +using System.Linq; +using static SimplePatch.Tests.DeltaUtils; + +namespace SimplePatch.Tests.ConfigurationTests +{ + [TestClass, TestCategory(TestCategories.Configuration)] + public class Properties : TestBase + { + [TestCleanup] + public void TestCleanup() + { + DeltaConfig.Clean(); + } + + [TestMethod] + public void Exclude() + { + DeltaConfig.Init(cfg => + { + cfg + .AddEntity() + .Property(x => x.Age).Exclude(); + }); + + var initialAge = John.Age; + + GetDelta(x => x.Age, 23).Patch(John); + + Assert.AreEqual(initialAge, John.Age); + } + + [TestMethod] + public void IgnoreNullValue() + { + DeltaConfig.Init(cfg => + { + cfg + .AddEntity() + .Property(x => x.Name).IgnoreNull(); + }); + + var initialName = John.Name; + + GetDelta(x => x.Name, null).Patch(John); + + Assert.AreEqual(initialName, John.Name); + } + + [TestMethod] + public void MappingFunction() + { + DeltaConfig.Init(cfg => + { + cfg + + /* When the target property type is string and the input is string, then the assigned value will be the reversed input string */ + .AddMapping((propType, newValue) => + { + var result = new MapResult(); + + if (propType == typeof(string) && newValue.GetType() == typeof(string)) + { + result.Value = string.Join("", newValue.ToString().ToCharArray().Reverse()); + } + else + { + result.Skip = true; + } + + return result; + }) + .AddEntity() + .Property(x => x.Name) + /* If the input value is string, then the assigned value will be the same string. Overriding global mapping function.*/ + .AddMapping((propType, newValue) => + { + if (newValue.GetType() != typeof(string)) return new MapResult().SkipMap(); + return new MapResult() { Value = newValue.ToString() }; + }) + /* If the input value is int, then the assigned value will be "number:{number}" */ + .AddMapping((propType, newValue) => + { + var result = new MapResult(); + + if (newValue.GetType() != typeof(int)) return result.SkipMap(); + + result.Value = "number:" + newValue.ToString(); + + return result; + }) + + /* If the input value is DateTime, then the assigned value will be "datetime:{datetime}". + * This behavior could be accomplished using only the previous mapping function. They are separeted + * functions to test mapping functions order execution*/ + .AddMapping((propType, newValue) => + { + var result = new MapResult(); + + if (newValue.GetType() != typeof(DateTime)) return result.SkipMap(); + + result.Value = "datetime:" + ((DateTime)newValue).ToString("s"); + + return result; + }); + }); + + // Global mapping function executed here + GetDelta(x => x.Surname, "Rossi").Patch(John); + Assert.AreEqual("issoR", John.Surname); + + // First property mapping function executed here + GetDelta(x => x.Name, "Mario").Patch(John); + Assert.AreEqual("Mario", John.Name); + + // Second property mapping function executed here + GetDelta(x => x.Name, 15).Patch(John); + Assert.AreEqual("number:15", John.Name); + + // Third property mapping function executed here + GetDelta(x => x.Name, John.BirthDate).Patch(John); + Assert.AreEqual("datetime:1990-02-01T20:15:10", John.Name); + } + } +} diff --git a/src/SimplePatch.Tests/DeltaUtils.cs b/src/SimplePatch.Tests/DeltaUtils.cs new file mode 100644 index 0000000..8f20c38 --- /dev/null +++ b/src/SimplePatch.Tests/DeltaUtils.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq.Expressions; + +namespace SimplePatch.Tests +{ + internal class DeltaUtils + { + internal static Delta GetDelta(Expression> property, T propValue) + { + var delta = new Delta(); + delta.Add(property, propValue); + return delta; + } + + internal static Delta GetDelta(string propertyName, T propValue) + { + var delta = new Delta(); + delta.Add(propertyName, propValue); + return delta; + } + + } +} diff --git a/src/SimplePatch.Tests/Person.cs b/src/SimplePatch.Tests/Person.cs new file mode 100644 index 0000000..48083a9 --- /dev/null +++ b/src/SimplePatch.Tests/Person.cs @@ -0,0 +1,14 @@ +using System; + +namespace SimplePatch.Tests +{ + internal class Person + { + public string Name { get; set; } + public string Surname { get; set; } + public int Age { get; set; } + public double Height { get; set; } + public Guid Guid { get; set; } + public DateTime BirthDate { get; set; } + } +} diff --git a/src/SimplePatch.Tests/Properties/AssemblyInfo.cs b/src/SimplePatch.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b868778 --- /dev/null +++ b/src/SimplePatch.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("SimplePatch.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SimplePatch.Tests")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("fb7e9dba-9b99-4dbf-9f14-882832f5ac83")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/SimplePatch.Tests/PropertiesTypesTests.cs b/src/SimplePatch.Tests/PropertiesTypesTests.cs new file mode 100644 index 0000000..23f32ff --- /dev/null +++ b/src/SimplePatch.Tests/PropertiesTypesTests.cs @@ -0,0 +1,95 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using static SimplePatch.Tests.DeltaUtils; + +namespace SimplePatch.Tests +{ + [TestClass, TestCategory(TestCategories.PropertiesTypes)] + public class PropertiesTypesTests : TestBase + { + [ClassInitialize] + public static void ClassInit(TestContext context) + { + DeltaConfig.Init(cfg => cfg.AddEntity()); + } + + [ClassCleanup] + public static void ClassCleanup() + { + DeltaConfig.Clean(); + } + + [TestMethod] + public void IntProp() + { + GetDelta(x => x.Age, 23).Patch(John); + Assert.AreEqual(23, John.Age); + } + + [TestMethod] + public void StringProp() + { + GetDelta(x => x.Name, "John Marco").Patch(John); + Assert.AreEqual("John Marco", John.Name); + } + + [TestMethod] + public void DoubleProp() + { + GetDelta(x => x.Height, 1.65).Patch(John); + Assert.AreEqual(1.65, John.Height); + } + + [TestMethod] + public void DateTimeProp() + { + var date = DateTime.UtcNow; + GetDelta(x => x.BirthDate, date).Patch(John); + Assert.AreEqual(date, John.BirthDate); + } + + [TestMethod] + public void GuidProp() + { + var guid = Guid.NewGuid(); + GetDelta(x => x.Guid, guid).Patch(John); + Assert.AreEqual(guid, John.Guid); + } + + + + #region From string + + [TestMethod] + public void IntPropFromString() + { + GetDelta(x => x.Age, "28").Patch(John); + Assert.AreEqual(28, John.Age); + } + + [TestMethod] + public void DoublePropFromString() + { + GetDelta(x => x.Height, "28,5").Patch(John); + Assert.AreEqual(28.5, John.Height); + } + + [TestMethod] + public void DateTimePropFromString() + { + var date = DateTime.UtcNow; + GetDelta(x => x.BirthDate, date.ToString("s")).Patch(John); + Assert.AreEqual(date.ToString("s"), John.BirthDate.ToString("s")); + } + + [TestMethod] + public void GuidPropFromString() + { + var guid = Guid.NewGuid(); + GetDelta(x => x.Guid, guid.ToString()).Patch(John); + Assert.AreEqual(guid, John.Guid); + } + + #endregion + } +} diff --git a/src/SimplePatch.Tests/SimplePatch.Tests.csproj b/src/SimplePatch.Tests/SimplePatch.Tests.csproj new file mode 100644 index 0000000..c593fbd --- /dev/null +++ b/src/SimplePatch.Tests/SimplePatch.Tests.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {FB7E9DBA-9B99-4DBF-9F14-882832F5AC83} + Library + Properties + SimplePatch.Tests + SimplePatch.Tests + v4.7 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + + + + + + + + {71d49858-8706-4e19-ab53-022320b263cd} + SimplePatch + + + + + + + Questo progetto fa riferimento a uno o più pacchetti NuGet che non sono presenti in questo computer. Usare lo strumento di ripristino dei pacchetti NuGet per scaricarli. Per altre informazioni, vedere http://go.microsoft.com/fwlink/?LinkID=322105. Il file mancante è {0}. + + + + + + \ No newline at end of file diff --git a/src/SimplePatch.Tests/TestBase.cs b/src/SimplePatch.Tests/TestBase.cs new file mode 100644 index 0000000..d1b76c3 --- /dev/null +++ b/src/SimplePatch.Tests/TestBase.cs @@ -0,0 +1,24 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace SimplePatch.Tests +{ + [TestClass] + public class TestBase + { + internal Person John; + + [TestInitialize] + public void TestInit() + { + John = new Person() + { + Name = "John", + Surname = "Doe", + Age = 22, + Height = 1.7, + BirthDate = new DateTime(1990, 2, 1, 20, 15, 10) + }; + } + } +} diff --git a/src/SimplePatch.Tests/TestCategories.cs b/src/SimplePatch.Tests/TestCategories.cs new file mode 100644 index 0000000..ad45ed0 --- /dev/null +++ b/src/SimplePatch.Tests/TestCategories.cs @@ -0,0 +1,8 @@ +namespace SimplePatch.Tests +{ + internal class TestCategories + { + public const string PropertiesTypes = "Properties Types"; + public const string Configuration = "Configuration"; + } +} diff --git a/src/SimplePatch.Tests/packages.config b/src/SimplePatch.Tests/packages.config new file mode 100644 index 0000000..aa9eae9 --- /dev/null +++ b/src/SimplePatch.Tests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/SimplePatch.sln b/src/SimplePatch.sln index caa447f..20ab7f8 100644 --- a/src/SimplePatch.sln +++ b/src/SimplePatch.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimplePatch", "SimplePatch\SimplePatch.csproj", "{71D49858-8706-4E19-AB53-022320B263CD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePatch.Tests", "SimplePatch.Tests\SimplePatch.Tests.csproj", "{FB7E9DBA-9B99-4DBF-9F14-882832F5AC83}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {71D49858-8706-4E19-AB53-022320B263CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {71D49858-8706-4E19-AB53-022320B263CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {71D49858-8706-4E19-AB53-022320B263CD}.Release|Any CPU.Build.0 = Release|Any CPU + {FB7E9DBA-9B99-4DBF-9F14-882832F5AC83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB7E9DBA-9B99-4DBF-9F14-882832F5AC83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB7E9DBA-9B99-4DBF-9F14-882832F5AC83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB7E9DBA-9B99-4DBF-9F14-882832F5AC83}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/SimplePatch/Delta.cs b/src/SimplePatch/Delta.cs index ad6fa4e..ceacf5c 100644 --- a/src/SimplePatch/Delta.cs +++ b/src/SimplePatch/Delta.cs @@ -1,8 +1,10 @@ -using System; +using SimplePatch.Helpers; +using SimplePatch.Mapping; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Reflection; +using System.Linq.Expressions; namespace SimplePatch { @@ -13,10 +15,7 @@ namespace SimplePatch /// private Dictionary dict = new Dictionary(); - /// - /// The full name of the type . - /// - private string typeFullName; + private List entityProperties; public object this[string key] { @@ -40,9 +39,9 @@ public object this[string key] public Delta() : base() { - typeFullName = typeof(TEntity).FullName; + entityProperties = DeltaCache.GetEntityProperties(); - DeltaCache.entityProperties.TryAdd(typeFullName, TypeHelper.GetEntityProperties()); + if (entityProperties == null) throw new Exception($"Entity {typeof(TEntity).Name} ({typeof(TEntity).FullName}) must be declared in DeltaConfig.Init(cfg => cfg.AddEntity()) method."); } /// @@ -122,6 +121,17 @@ public void Add(string key, object value) } } + /// + /// Adds the specified key and value to the dictionary only if the specified key is a property name of . + /// + /// Element key to add. + /// Value of element to be added. The element will not be added if null or equal to . See + public void Add(Expression> property, object value) + { + var propertyName = ExpressionHelper.GetPropertyName(property); + Add(propertyName, value); + } + /// /// Returns the properties that have been specified (compared to properties) as an enumeration of property names. /// @@ -140,7 +150,7 @@ public IEnumerable GetSpecifiedPropertyNames() /// The property names. public IEnumerable GetNotSpecifiedPropertyNames() { - return DeltaCache.entityProperties[typeFullName].Select(x => x.Name).Where(x => !dict.ContainsKey(x)); + return entityProperties.Select(x => x.Name).Where(x => !dict.ContainsKey(x)); } #region Private methods @@ -152,7 +162,7 @@ public IEnumerable GetNotSpecifiedPropertyNames() /// True if exposes a property with the specified name, otherwise False. private bool IsPropertyAllowed(string propertyName) { - return !string.IsNullOrEmpty(propertyName) && DeltaCache.entityProperties[typeFullName].Any(x => x.Name == propertyName); + return !string.IsNullOrEmpty(propertyName) && entityProperties.Any(x => x.Name == propertyName); } /// @@ -164,12 +174,10 @@ private string GetCorrectCasePropertyName(string propertyName) { if (string.IsNullOrEmpty(propertyName)) return null; - var properties = DeltaCache.entityProperties[typeFullName]; - - var propertyNameLowerCase = propertyName.ToLower(); - foreach (var property in properties) + var propertyNameUpperCase = propertyName.ToUpper(); + foreach (var property in entityProperties) { - if (property.Name.ToLower() == propertyNameLowerCase) return property.Name; + if (string.Equals(property.Name, propertyNameUpperCase, StringComparison.OrdinalIgnoreCase)) return property.Name; } return null; @@ -182,67 +190,135 @@ private string GetCorrectCasePropertyName(string propertyName) /// The modified entity. private TEntity SetPropertiesValue(TEntity entity) { - //If the cache contains the property list for the specified type, set the properties value - if (DeltaCache.entityProperties.TryGetValue(typeFullName, out var properties)) + foreach (var prop in entityProperties) { - foreach (var prop in properties) + var propertyInfo = prop.PropertyInfo; + if (ContainsKey(propertyInfo.Name) && !prop.Excluded) { - var propertyInfo = prop.PropertyInfo; - if (ContainsKey(propertyInfo.Name) && !IsExcludedProperty(typeFullName, propertyInfo.Name)) - { - var truePropertyType = TypeHelper.GetTrueType(propertyInfo.PropertyType); - var newPropertyValue = this[propertyInfo.Name]; + var truePropertyType = TypeHelper.GetTrueType(propertyInfo.PropertyType); + var newPropertyValue = this[propertyInfo.Name]; + //Check for null value before getting type of new value + if (newPropertyValue == null) + { + if (prop.IgnoreNullValue) continue; - //Check for null value before getting type of new value - if (newPropertyValue == null) + //Check if destination property allows null value + if (TypeHelper.IsNullable(propertyInfo.PropertyType)) { - if (prop.IgnoreNullValue) continue; + var valueFromMappingsForNull = GetValueFromAllMappings(typeof(TEntity), prop, newPropertyValue); - //Check if destination property allows null value - if (TypeHelper.IsNullable(propertyInfo.PropertyType)) + if (!valueFromMappingsForNull.Skip) { - propertyInfo.SetValue(entity, null, null); - continue; + propertyInfo.SetValue(entity, valueFromMappingsForNull.Value); } else { - throw new Exception($"Null value not allowed for '{propertyInfo.Name}' property of '{typeFullName}'"); + propertyInfo.SetValue(entity, null); } + continue; } + else + { + throw new Exception($"Null value not allowed for '{propertyInfo.Name}' property of '{typeof(TEntity).FullName}'"); + } + } + + var valueFromMappings = GetValueFromAllMappings(typeof(TEntity), prop, newPropertyValue); + if (!valueFromMappings.Skip) + { + propertyInfo.SetValue(entity, valueFromMappings.Value); + } + // If no mapping function assigned a value to the property, use the default mapping + else + { var newPropertyValueType = newPropertyValue.GetType(); - //Guid from string + // Guid from string if (truePropertyType == typeof(Guid) && newPropertyValueType == typeof(string)) { newPropertyValue = new Guid((string)newPropertyValue); - propertyInfo.SetValue(entity, newPropertyValue, null); + propertyInfo.SetValue(entity, newPropertyValue); } else { - propertyInfo.SetValue(entity, Convert.ChangeType(newPropertyValue, truePropertyType), null); + propertyInfo.SetValue(entity, Convert.ChangeType(newPropertyValue, truePropertyType)); } } } + } + + return entity; + } - return entity; + /// + /// Obtain the from or . + /// if returns a with = false, then it will be return. + /// Otherwise the result of will be returned. + /// + /// Type of the entity + /// Informations about the property + /// New value which should be processed before assigning it to the processed property + /// + private MapResult GetValueFromAllMappings(Type entityType, DeltaPropInfo deltaPropInfo, object newPropertyValue) + { + var valueFromPropertyMappings = GetValueFromPropertyMappings(deltaPropInfo, newPropertyValue); + if (!valueFromPropertyMappings.Skip) return valueFromPropertyMappings; + + return GetValueFromGlobalMappings(deltaPropInfo.PropertyInfo.PropertyType, newPropertyValue); + } + + /// + /// Obtains the first from global mapping functions which handles the specified and . + /// + /// Type of the property to be processed + /// New value which should be processed before assigning it to the processed property + /// + private MapResult GetValueFromGlobalMappings(Type propertyType, object newPropertyValue) + { + var mappings = DeltaConfig.GlobalMappings; + + if (mappings != null) + { + foreach (var mapping in mappings) + { + var mapResult = mapping(propertyType, newPropertyValue); + + if (mapResult.Skip) continue; + + return mapResult; + } } - throw new Exception("Entity properties not added to cache. Problems with Delta constructor?"); + return new MapResult() { Skip = true }; } /// - /// Specifies whether the change must be disabled for the property with specified name belonging to the specified entity. + /// Obtains the first from the mapping functions linked to the specified property. /// - /// The entity's full name that exposes the property. - /// The name of the property. - /// True if property is excluded from changes, otherwise False. - private bool IsExcludedProperty(string typeFullName, string propertyName) + /// Type of the entity + /// Type of the property + /// Name of the property + /// New value which should be processed before assigning it to the processed property + /// + private MapResult GetValueFromPropertyMappings(DeltaPropInfo deltaPropInfo, object newPropertyValue) { - if (!DeltaCache.excludedProperties.ContainsKey(typeFullName)) return false; - if (DeltaCache.excludedProperties[typeFullName].Contains(propertyName)) return true; - return false; + var mappings = deltaPropInfo.MapFunctions; + + if (mappings != null) + { + foreach (var mapping in mappings) + { + var mapResult = mapping(deltaPropInfo.PropertyInfo.PropertyType, newPropertyValue); + + if (mapResult.Skip) continue; + + return mapResult; + } + } + + return new MapResult().SkipMap(); } #endregion diff --git a/src/SimplePatch/DeltaCache.cs b/src/SimplePatch/DeltaCache.cs index 1ab4935..e92ab73 100644 --- a/src/SimplePatch/DeltaCache.cs +++ b/src/SimplePatch/DeltaCache.cs @@ -1,12 +1,62 @@ -using System.Collections.Concurrent; +using SimplePatch.Helpers; +using SimplePatch.Mapping; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.Reflection; +using System.Linq; namespace SimplePatch { internal static class DeltaCache { - public static ConcurrentDictionary> entityProperties = new ConcurrentDictionary>(); - public static ConcurrentDictionary excludedProperties = new ConcurrentDictionary(); + private static ConcurrentDictionary> EntityProperties = new ConcurrentDictionary>(); + + internal static string GetEntityKey() where TEntity : class, new() + { + return typeof(TEntity).FullName; + } + + internal static List GetEntityProperties() where TEntity : class, new() + { + var key = GetEntityKey(); + if (!EntityProperties.ContainsKey(key)) return null; + return EntityProperties[key]; + } + + internal static void AddEntity() where TEntity : class, new() + { + // Adding entity properties to cache + EntityProperties.TryAdd(GetEntityKey(), TypeHelper.GetEntityProperties().ToList()); + } + + internal class PropertyEditor where TEntity : class, new() + { + private readonly DeltaPropInfo deltaInfo; + + internal PropertyEditor(string propertyName) + { + deltaInfo = EntityProperties[GetEntityKey()].Find(x => x.Name == propertyName); + } + + internal void Exclude() + { + deltaInfo.Excluded = true; + } + + internal void IgnoreNullValue() + { + deltaInfo.IgnoreNullValue = true; + } + + internal void AddMapping(MapDelegate mapFunction) + { + if (deltaInfo.MapFunctions == null) deltaInfo.MapFunctions = new List>(); + deltaInfo.MapFunctions.Add((propertyType, newValue) => (MapResult)mapFunction(propertyType, newValue)); + } + } + + internal static void Clear() + { + EntityProperties.Clear(); + } } } \ No newline at end of file diff --git a/src/SimplePatch/DeltaConfig.cs b/src/SimplePatch/DeltaConfig.cs index d029ea2..b450d8f 100644 --- a/src/SimplePatch/DeltaConfig.cs +++ b/src/SimplePatch/DeltaConfig.cs @@ -1,8 +1,8 @@ -using System; +using SimplePatch.Helpers; +using SimplePatch.Mapping; +using System; using System.Collections.Generic; using System.Linq.Expressions; -using System.Reflection; -using System.Linq; namespace SimplePatch { @@ -10,56 +10,89 @@ public class DeltaConfig { internal static bool IgnoreLetterCase = false; + /// + /// List of the global mapping function + /// + internal static List> GlobalMappings; + public static void Init(Action config) { config(new Config()); } + internal static void Clean() + { + IgnoreLetterCase = false; + GlobalMappings?.Clear(); + DeltaCache.Clear(); + } + public sealed class Config { - /// - /// Sets the properties to exclude when calling . - /// - /// Class in which the property is contained. - /// Properties to exclude when calling - /// - public Config ExcludeProperties(params Expression>[] properties) + public sealed class EntityConfig where T : class, new() { - var type = typeof(T); - - var propList = new List(); - foreach (var item in properties) + public PropertyConfig Property(Expression> property) { - var propertyInfo = GetMemberExpression(item).Member as PropertyInfo; - propList.Add(propertyInfo.Name); + return new PropertyConfig(property); } - - DeltaCache.excludedProperties.TryAdd(type.FullName, propList.ToArray()); - - return this; } - /// - /// Specifies properties for whose null value will be ignored. - /// - /// Class in which the property is contained. - /// Properties for whose null value will be ignored. - /// - public Config IgnoreNullValue(params Expression>[] properties) + public sealed class PropertyConfig where TEntity : class, new() { - var type = typeof(T); + private readonly string propertyName; + private readonly DeltaCache.PropertyEditor deltaCachePropertyEditor; - var propList = TypeHelper.GetEntityProperties().ToList(); - foreach (var prop in properties) + public PropertyConfig(Expression> property) { - var propertyInfo = GetMemberExpression(prop).Member as PropertyInfo; + propertyName = ExpressionHelper.GetPropertyName(property); + deltaCachePropertyEditor = new DeltaCache.PropertyEditor(propertyName); + } - propList.First(x => x.Name == propertyInfo.Name).IgnoreNullValue = true; + /// + /// Adds a mapping function for the specified property of the specified entity. + /// + /// Type of the property + /// Expression which indicates the property + /// Mapping function used to evaluate the value to be assigned to the property + /// + public PropertyConfig AddMapping(MapDelegate mapFunction) + { + deltaCachePropertyEditor.AddMapping(mapFunction); + return this; } - DeltaCache.entityProperties.TryAdd(type.FullName, propList); + /// + /// Ignore null value for the specified property + /// + /// + public PropertyConfig IgnoreNull() + { + deltaCachePropertyEditor.IgnoreNullValue(); + return this; + } - return this; + /// + /// Marks the specified property as excluded when calling . + /// + /// + /// + /// + public PropertyConfig Exclude() + { + deltaCachePropertyEditor.Exclude(); + return this; + } + } + + /// + /// Allows to add settings for the specified property + /// + /// Type of the property for which add settings + /// + public EntityConfig AddEntity() where T : class, new() + { + DeltaCache.AddEntity(); + return new EntityConfig(); } /// @@ -73,11 +106,17 @@ public Config IgnoreLetterCase(bool enabled = true) return this; } - private static MemberExpression GetMemberExpression(Expression> exp) + /// + /// Adds a global mapping function which will be executed for every property of the processed entities. + /// The result of the will be used to evaluate the value to be assigned to the processed property. + /// + /// The mapping function + /// + public Config AddMapping(MapDelegate mappingFunc) { - var member = exp.Body as MemberExpression; - var unary = exp.Body as UnaryExpression; - return member ?? (unary != null ? unary.Operand as MemberExpression : null); + if (GlobalMappings == null) GlobalMappings = new List>(); + GlobalMappings.Add(mappingFunc); + return this; } } } diff --git a/src/SimplePatch/DeltaInfo.cs b/src/SimplePatch/DeltaInfo.cs deleted file mode 100644 index 260d63e..0000000 --- a/src/SimplePatch/DeltaInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; - -namespace SimplePatch -{ - internal class DeltaInfo - { - public bool IgnoreNullValue { get; set; } = false; - public PropertyInfo PropertyInfo { get; set; } - - public string Name { get => PropertyInfo.Name; } - - public DeltaInfo(PropertyInfo propertyInfo) - { - PropertyInfo = propertyInfo; - } - } -} diff --git a/src/SimplePatch/DeltaPropInfo.cs b/src/SimplePatch/DeltaPropInfo.cs new file mode 100644 index 0000000..17436ce --- /dev/null +++ b/src/SimplePatch/DeltaPropInfo.cs @@ -0,0 +1,23 @@ +using SimplePatch.Mapping; +using System.Collections.Generic; +using System.Reflection; + +namespace SimplePatch +{ + internal class DeltaPropInfo + { + internal bool IgnoreNullValue { get; set; } + internal PropertyInfo PropertyInfo { get; set; } + + internal string Name { get => PropertyInfo.Name; } + + internal List> MapFunctions { get; set; } + + internal bool Excluded { get; set; } + + internal DeltaPropInfo(PropertyInfo propertyInfo) + { + PropertyInfo = propertyInfo; + } + } +} diff --git a/src/SimplePatch/Helpers/ExpressionHelper.cs b/src/SimplePatch/Helpers/ExpressionHelper.cs new file mode 100644 index 0000000..5bd9022 --- /dev/null +++ b/src/SimplePatch/Helpers/ExpressionHelper.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace SimplePatch.Helpers +{ + internal class ExpressionHelper + { + /// + /// Gets the name of the property specified in the expression + /// + /// Type of the entity containing the property + /// Type of the property + /// Expression indicating the property + /// The name of the specified property + internal static string GetPropertyName(Expression> exp) + { + var member = exp.Body as MemberExpression; + var unary = exp.Body as UnaryExpression; + return ((member ?? (unary != null ? unary.Operand as MemberExpression : null)).Member as PropertyInfo).Name; + } + } +} diff --git a/src/SimplePatch/TypeHelper.cs b/src/SimplePatch/Helpers/TypeHelper.cs similarity index 65% rename from src/SimplePatch/TypeHelper.cs rename to src/SimplePatch/Helpers/TypeHelper.cs index 44d5ad4..4924863 100644 --- a/src/SimplePatch/TypeHelper.cs +++ b/src/SimplePatch/Helpers/TypeHelper.cs @@ -3,21 +3,27 @@ using System.Linq; using System.Reflection; -namespace SimplePatch +namespace SimplePatch.Helpers { internal class TypeHelper { - public static IEnumerable GetEntityProperties() + /// + /// Obtains the list of properties belonging to the specified identity as + /// + /// Entity for which obtain the properties + /// List of properties belonging to the specified identity as + internal static IEnumerable GetEntityProperties() { - return typeof(TEntity).GetTypeInfo().DeclaredProperties.Where(x => x.GetMethod.IsPublic && x.SetMethod.IsPublic && x.CanRead && x.CanWrite).Select(x => new DeltaInfo(x)); + return typeof(TEntity).GetTypeInfo().DeclaredProperties.Where(x => x.GetMethod.IsPublic && x.SetMethod.IsPublic && x.CanRead && x.CanWrite).Select(x => new DeltaPropInfo(x)); } + /// /// Returns the type specified by the parameter or type below if is . /// /// The type to be verified. /// The type specified by the parameter or type below if is . - public static Type GetTrueType(Type type) + internal static Type GetTrueType(Type type) { return Nullable.GetUnderlyingType(type) ?? type; } @@ -27,7 +33,7 @@ public static Type GetTrueType(Type type) /// /// Type to check. /// True if the specified type accepts null values, otherwise false. - public static bool IsNullable(Type type) + internal static bool IsNullable(Type type) { if (!type.GetTypeInfo().IsValueType) return true; // ref-type if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable diff --git a/src/SimplePatch/Mapping/MapDelegate.cs b/src/SimplePatch/Mapping/MapDelegate.cs new file mode 100644 index 0000000..5c79ce9 --- /dev/null +++ b/src/SimplePatch/Mapping/MapDelegate.cs @@ -0,0 +1,12 @@ +using System; + +namespace SimplePatch.Mapping +{ + /// + /// A delegate for a mapping function which processes the new value of the entities' properties. + /// + /// The type of the property to which assign the value. + /// The new value which can be assigned to the property. + /// + public delegate MapResult MapDelegate(Type propertyType, object newValue); +} diff --git a/src/SimplePatch/Mapping/MapResult.cs b/src/SimplePatch/Mapping/MapResult.cs new file mode 100644 index 0000000..cf23ad2 --- /dev/null +++ b/src/SimplePatch/Mapping/MapResult.cs @@ -0,0 +1,41 @@ +namespace SimplePatch.Mapping +{ + /// + /// The result of a function execution. + /// + public class MapResult + { + /// + /// Whatever to skip the current mapping function and execute the default one. + /// + public bool Skip { get; set; } = false; + + /// + /// The value to assign to the property when is false. + /// + public T Value { get; set; } + + /// + /// Assigns false to and return the current instance. + /// Use it as a shortcut of + /// + /// instance.Skip = false; + /// return instance; + /// + /// + /// + public MapResult SkipMap() + { + Skip = true; + return this; + } + + public static explicit operator MapResult(MapResult v) + { + return new MapResult() { + Skip = v.Skip, + Value = v.Value + }; + } + } +} diff --git a/src/SimplePatch/Properties/AssemblyInfo.cs b/src/SimplePatch/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..eaed834 --- /dev/null +++ b/src/SimplePatch/Properties/AssemblyInfo.cs @@ -0,0 +1,9 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("SimplePatch.Tests")] +namespace SimplePatch +{ + class AssemblyInfo + { + } +} diff --git a/src/SimplePatch/SimplePatch.csproj b/src/SimplePatch/SimplePatch.csproj index c7658b1..2385fd8 100644 --- a/src/SimplePatch/SimplePatch.csproj +++ b/src/SimplePatch/SimplePatch.csproj @@ -2,22 +2,26 @@ net451;netstandard1.4 - 1.2.4 + 3.0.0 Omar Muscatello Omar Muscatello A simple library for partial entity changes in ASP.NET and ASP.NET Core. - Copyright (c) Omar Muscatello 2017 + Copyright (c) Omar Muscatello 2018 https://github.com/OmarMuscatello/SimplePatch/blob/master/LICENSE - Fix exception when null assigned to a nullable value type. + * Break change: All entities types which is used in Delta<T> or DeltaCollection<T> must be declared in DeltaConfig.Init(). +* Break change: excluded property and 'ignore null value' option must be declared in DeltaConfig.Init(). +* Added global and properties mapping functions. +* Added support to add property using expressions in Delta<T>.Add method. +* Added unit tests. https://github.com/OmarMuscatello/SimplePatch https://github.com/OmarMuscatello/SimplePatch http://raw.github.com/OmarMuscatello/SimplePatch/master/simplepatch-icon.png - web-api patch entity-framework update asp-net asp-net-core + patch http-patch partial-entity-changes web-api asp-net-web-api entity-framework entity-framework-core asp-net asp-net-core true - 1.2.4.0 - 1.2.4.0 + 3.0.0.0 + 3.0.0.0