├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── AspNetCore_OData.postman_collection.json ├── README.md ├── aspnet-core-web-api-using-odata.sln ├── docker-compose.yml ├── postman.JPG └── src └── OData.WebApi ├── Controllers ├── BaseController.cs ├── OData │ ├── ODataBaseController.cs │ ├── ProductCategoryODataController.cs │ └── ProductODataController.cs ├── ProductCategoryController.cs └── ProductController.cs ├── Data ├── Entities │ ├── Base │ │ └── BaseEntity.cs │ ├── Product.cs │ └── ProductCategory.cs ├── Migrations │ ├── 20191030195859_init.Designer.cs │ ├── 20191030195859_init.cs │ └── ProductDbContextModelSnapshot.cs ├── ProductDbContext.cs ├── Repositories │ ├── IRepository.cs │ └── Repository.cs └── TypeConfigurations │ ├── ProductCategoryTypeConfiguration.cs │ └── ProductTypeConfiguration.cs ├── Dto ├── ProductCategoryDto.cs └── ProductDto.cs ├── OData.WebApi.csproj ├── Program.cs ├── Properties └── launchSettings.json ├── Startup.cs └── appsettings.json /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # Logs 255 | Logs* 256 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | "program": "${workspaceFolder}/src/OData.WebApi/bin/Debug/netcoreapp2.2/OData.WebApi.dll", 13 | "args": [], 14 | "cwd": "${workspaceFolder}/src/OData.WebApi", 15 | "stopAtEntry": false, 16 | "serverReadyAction": { 17 | "action": "openExternally", 18 | "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" 19 | }, 20 | "env": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "sourceFileMap": { 24 | "/Views": "${workspaceFolder}/Views" 25 | } 26 | }, 27 | { 28 | "name": ".NET Core Attach", 29 | "type": "coreclr", 30 | "request": "attach", 31 | "processId": "${command:pickProcess}" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/src/OData.WebApi/OData.WebApi.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/src/OData.WebApi/OData.WebApi.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/src/OData.WebApi/OData.WebApi.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /AspNetCore_OData.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "9a64ed14-4419-4fe2-a636-e341bd37e44c", 4 | "name": "Dotnet_Conf_OData", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Product", 10 | "item": [ 11 | { 12 | "name": "Get", 13 | "item": [ 14 | { 15 | "name": "GetAll", 16 | "request": { 17 | "method": "GET", 18 | "header": [], 19 | "url": { 20 | "raw": "localhost:5000/odata/products", 21 | "host": [ 22 | "localhost" 23 | ], 24 | "port": "5000", 25 | "path": [ 26 | "odata", 27 | "products" 28 | ] 29 | } 30 | }, 31 | "response": [] 32 | }, 33 | { 34 | "name": "GetById", 35 | "request": { 36 | "method": "GET", 37 | "header": [ 38 | { 39 | "key": "Content-Type", 40 | "value": "application/json", 41 | "type": "text" 42 | } 43 | ], 44 | "url": { 45 | "raw": "localhost:5000/odata/products(6d42ac79-2b65-460d-9991-22b86ad66fb9)", 46 | "host": [ 47 | "localhost" 48 | ], 49 | "port": "5000", 50 | "path": [ 51 | "odata", 52 | "products(6d42ac79-2b65-460d-9991-22b86ad66fb9)" 53 | ] 54 | } 55 | }, 56 | "response": [] 57 | }, 58 | { 59 | "name": "Filter", 60 | "request": { 61 | "method": "GET", 62 | "header": [], 63 | "url": { 64 | "raw": "localhost:5000/odata/products?$filter=price gt 10000", 65 | "host": [ 66 | "localhost" 67 | ], 68 | "port": "5000", 69 | "path": [ 70 | "odata", 71 | "products" 72 | ], 73 | "query": [ 74 | { 75 | "key": "$filter", 76 | "value": "price gt 10000" 77 | } 78 | ] 79 | } 80 | }, 81 | "response": [] 82 | }, 83 | { 84 | "name": "SkipTake", 85 | "request": { 86 | "method": "GET", 87 | "header": [], 88 | "url": { 89 | "raw": "localhost:5000/odata/products?$count=true&$top=5&$skip=1", 90 | "host": [ 91 | "localhost" 92 | ], 93 | "port": "5000", 94 | "path": [ 95 | "odata", 96 | "products" 97 | ], 98 | "query": [ 99 | { 100 | "key": "$count", 101 | "value": "true" 102 | }, 103 | { 104 | "key": "$top", 105 | "value": "5" 106 | }, 107 | { 108 | "key": "$skip", 109 | "value": "1" 110 | } 111 | ] 112 | } 113 | }, 114 | "response": [] 115 | }, 116 | { 117 | "name": "OrderByAsc", 118 | "request": { 119 | "method": "GET", 120 | "header": [], 121 | "url": { 122 | "raw": "localhost:5000/odata/products?$orderby=price", 123 | "host": [ 124 | "localhost" 125 | ], 126 | "port": "5000", 127 | "path": [ 128 | "odata", 129 | "products" 130 | ], 131 | "query": [ 132 | { 133 | "key": "$orderby", 134 | "value": "price" 135 | } 136 | ] 137 | } 138 | }, 139 | "response": [] 140 | }, 141 | { 142 | "name": "OrderByDesc", 143 | "request": { 144 | "method": "GET", 145 | "header": [], 146 | "url": { 147 | "raw": "localhost:5000/odata/products?$orderby=price desc", 148 | "host": [ 149 | "localhost" 150 | ], 151 | "port": "5000", 152 | "path": [ 153 | "odata", 154 | "products" 155 | ], 156 | "query": [ 157 | { 158 | "key": "$orderby", 159 | "value": "price desc" 160 | } 161 | ] 162 | } 163 | }, 164 | "response": [] 165 | }, 166 | { 167 | "name": "FilterProductNameContains", 168 | "request": { 169 | "method": "GET", 170 | "header": [], 171 | "url": { 172 | "raw": "localhost:5000/odata/products?$filter=contains(productname, 'LG')", 173 | "host": [ 174 | "localhost" 175 | ], 176 | "port": "5000", 177 | "path": [ 178 | "odata", 179 | "products" 180 | ], 181 | "query": [ 182 | { 183 | "key": "$filter", 184 | "value": "contains(productname, 'LG')" 185 | } 186 | ] 187 | } 188 | }, 189 | "response": [] 190 | }, 191 | { 192 | "name": "FilterBySubProperty", 193 | "request": { 194 | "method": "GET", 195 | "header": [], 196 | "url": { 197 | "raw": "localhost:5000/odata/products?$expand=category($filter=categoryName eq 'TV')", 198 | "host": [ 199 | "localhost" 200 | ], 201 | "port": "5000", 202 | "path": [ 203 | "odata", 204 | "products" 205 | ], 206 | "query": [ 207 | { 208 | "key": "$expand", 209 | "value": "category($filter=categoryName eq 'TV')" 210 | } 211 | ] 212 | } 213 | }, 214 | "response": [] 215 | }, 216 | { 217 | "name": "OnlyCount", 218 | "request": { 219 | "method": "GET", 220 | "header": [], 221 | "url": { 222 | "raw": "localhost:5000/odata/products?$top=0&$count=true", 223 | "host": [ 224 | "localhost" 225 | ], 226 | "port": "5000", 227 | "path": [ 228 | "odata", 229 | "products" 230 | ], 231 | "query": [ 232 | { 233 | "key": "$top", 234 | "value": "0" 235 | }, 236 | { 237 | "key": "$count", 238 | "value": "true" 239 | } 240 | ] 241 | } 242 | }, 243 | "response": [] 244 | }, 245 | { 246 | "name": "SelectFields", 247 | "request": { 248 | "method": "GET", 249 | "header": [], 250 | "url": { 251 | "raw": "localhost:5000/odata/products?$select=id,productname", 252 | "host": [ 253 | "localhost" 254 | ], 255 | "port": "5000", 256 | "path": [ 257 | "odata", 258 | "products" 259 | ], 260 | "query": [ 261 | { 262 | "key": "$select", 263 | "value": "id,productname" 264 | } 265 | ] 266 | } 267 | }, 268 | "response": [] 269 | }, 270 | { 271 | "name": "Expand", 272 | "request": { 273 | "method": "GET", 274 | "header": [], 275 | "url": { 276 | "raw": "localhost:5000/odata/products?$expand=category", 277 | "host": [ 278 | "localhost" 279 | ], 280 | "port": "5000", 281 | "path": [ 282 | "odata", 283 | "products" 284 | ], 285 | "query": [ 286 | { 287 | "key": "$expand", 288 | "value": "category" 289 | } 290 | ] 291 | } 292 | }, 293 | "response": [] 294 | }, 295 | { 296 | "name": "ExpandAndSelect", 297 | "request": { 298 | "method": "GET", 299 | "header": [], 300 | "url": { 301 | "raw": "localhost:5000/odata/products?$expand=category($select=categoryName)&$select=id,productName", 302 | "host": [ 303 | "localhost" 304 | ], 305 | "port": "5000", 306 | "path": [ 307 | "odata", 308 | "products" 309 | ], 310 | "query": [ 311 | { 312 | "key": "$expand", 313 | "value": "category($select=categoryName)" 314 | }, 315 | { 316 | "key": "$select", 317 | "value": "id,productName" 318 | } 319 | ] 320 | } 321 | }, 322 | "response": [] 323 | } 324 | ], 325 | "_postman_isSubFolder": true 326 | }, 327 | { 328 | "name": "Post", 329 | "request": { 330 | "method": "POST", 331 | "header": [ 332 | { 333 | "key": "Content-Type", 334 | "value": "application/json", 335 | "type": "text" 336 | } 337 | ], 338 | "body": { 339 | "mode": "raw", 340 | "raw": "{\n \"productName\": \"Sony 65-Inch\",\n \"categoryId\": \"1236a458-0802-4340-bdd4-05859c9aaaad\",\n \"description\": \"Sony 65-Inch 4K Ultra HD Smart LED TV\",\n \"price\": 10000,\n \"weight\": 70\n}" 341 | }, 342 | "url": { 343 | "raw": "localhost:5000/odata/products", 344 | "host": [ 345 | "localhost" 346 | ], 347 | "port": "5000", 348 | "path": [ 349 | "odata", 350 | "products" 351 | ] 352 | } 353 | }, 354 | "response": [] 355 | }, 356 | { 357 | "name": "Delete", 358 | "request": { 359 | "method": "DELETE", 360 | "header": [], 361 | "url": { 362 | "raw": "localhost:5000/odata/products/0100b063-f8db-4ba5-936c-b3d623cebde5", 363 | "host": [ 364 | "localhost" 365 | ], 366 | "port": "5000", 367 | "path": [ 368 | "odata", 369 | "products", 370 | "0100b063-f8db-4ba5-936c-b3d623cebde5" 371 | ] 372 | } 373 | }, 374 | "response": [] 375 | }, 376 | { 377 | "name": "Put", 378 | "request": { 379 | "method": "PUT", 380 | "header": [ 381 | { 382 | "key": "Content-Type", 383 | "value": "application/json", 384 | "type": "text" 385 | } 386 | ], 387 | "body": { 388 | "mode": "raw", 389 | "raw": " {\n \"id\": \"010b34c3-5d91-4f15-a926-a4edbc3a9770\",\n \"name\": \"Lenovo Thinkpad - YENİİİİİİİİİİ2222\",\n \"categoryId\": \"a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0\",\n \"description\": \"Lenovo Thinkpad Laptop\",\n \"price\": 2800,\n \"weight\": 1.7\n }" 390 | }, 391 | "url": { 392 | "raw": "localhost:5000/odata/products", 393 | "host": [ 394 | "localhost" 395 | ], 396 | "port": "5000", 397 | "path": [ 398 | "odata", 399 | "products" 400 | ] 401 | } 402 | }, 403 | "response": [] 404 | } 405 | ] 406 | }, 407 | { 408 | "name": "Product_Category", 409 | "item": [ 410 | { 411 | "name": "Get", 412 | "item": [ 413 | { 414 | "name": "GetAll", 415 | "request": { 416 | "method": "GET", 417 | "header": [], 418 | "url": { 419 | "raw": "localhost:5000/odata/products", 420 | "host": [ 421 | "localhost" 422 | ], 423 | "port": "5000", 424 | "path": [ 425 | "odata", 426 | "products" 427 | ] 428 | } 429 | }, 430 | "response": [] 431 | }, 432 | { 433 | "name": "GetById", 434 | "request": { 435 | "method": "GET", 436 | "header": [ 437 | { 438 | "key": "Content-Type", 439 | "value": "application/json", 440 | "type": "text" 441 | } 442 | ], 443 | "url": { 444 | "raw": "localhost:5000/odata/product_categories(1236a458-0802-4340-bdd4-05859c9aaaad)", 445 | "host": [ 446 | "localhost" 447 | ], 448 | "port": "5000", 449 | "path": [ 450 | "odata", 451 | "product_categories(1236a458-0802-4340-bdd4-05859c9aaaad)" 452 | ] 453 | } 454 | }, 455 | "response": [] 456 | }, 457 | { 458 | "name": "OnlyCount", 459 | "request": { 460 | "method": "GET", 461 | "header": [], 462 | "url": { 463 | "raw": "localhost:5000/odata/product_categories?$top=0&$count=true", 464 | "host": [ 465 | "localhost" 466 | ], 467 | "port": "5000", 468 | "path": [ 469 | "odata", 470 | "product_categories" 471 | ], 472 | "query": [ 473 | { 474 | "key": "$top", 475 | "value": "0" 476 | }, 477 | { 478 | "key": "$count", 479 | "value": "true" 480 | } 481 | ] 482 | } 483 | }, 484 | "response": [] 485 | } 486 | ], 487 | "_postman_isSubFolder": true 488 | }, 489 | { 490 | "name": "Post", 491 | "request": { 492 | "method": "POST", 493 | "header": [ 494 | { 495 | "key": "Content-Type", 496 | "value": "application/json", 497 | "type": "text" 498 | } 499 | ], 500 | "body": { 501 | "mode": "raw", 502 | "raw": "{\n \"categoryName\":\"My Category\"\n}" 503 | }, 504 | "url": { 505 | "raw": "localhost:5000/odata/product_categories", 506 | "host": [ 507 | "localhost" 508 | ], 509 | "port": "5000", 510 | "path": [ 511 | "odata", 512 | "product_categories" 513 | ] 514 | } 515 | }, 516 | "response": [] 517 | }, 518 | { 519 | "name": "Delete", 520 | "request": { 521 | "method": "DELETE", 522 | "header": [], 523 | "url": { 524 | "raw": "localhost:5000/odata/product_categories/833507f4-958c-469e-8615-5993df3b53fe", 525 | "host": [ 526 | "localhost" 527 | ], 528 | "port": "5000", 529 | "path": [ 530 | "odata", 531 | "product_categories", 532 | "833507f4-958c-469e-8615-5993df3b53fe" 533 | ] 534 | } 535 | }, 536 | "response": [] 537 | }, 538 | { 539 | "name": "Put", 540 | "request": { 541 | "method": "PUT", 542 | "header": [ 543 | { 544 | "key": "Content-Type", 545 | "type": "text", 546 | "value": "application/json" 547 | } 548 | ], 549 | "body": { 550 | "mode": "raw", 551 | "raw": " {\n \"id\": \"9eebe71d-d58c-462c-882f-5241b860211e\",\n \"name\": \"My category update\"\n }" 552 | }, 553 | "url": { 554 | "raw": "localhost:5000/odata/product_categories", 555 | "host": [ 556 | "localhost" 557 | ], 558 | "port": "5000", 559 | "path": [ 560 | "odata", 561 | "product_categories" 562 | ] 563 | } 564 | }, 565 | "response": [] 566 | } 567 | ] 568 | } 569 | ], 570 | "event": [ 571 | { 572 | "listen": "prerequest", 573 | "script": { 574 | "id": "b9ccdc18-8a58-4f39-915e-abb42055dca8", 575 | "type": "text/javascript", 576 | "exec": [ 577 | "" 578 | ] 579 | } 580 | }, 581 | { 582 | "listen": "test", 583 | "script": { 584 | "id": "006bf3f6-b431-4b5a-8959-8a889a2e354c", 585 | "type": "text/javascript", 586 | "exec": [ 587 | "" 588 | ] 589 | } 590 | } 591 | ], 592 | "protocolProfileBehavior": {} 593 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Presentation : https://speakerdeck.com/suadev/add-odata-support-to-asp-dot-net-core-web-api 2 | 3 | This repo aims to show that how easily OData can be added to your existing/new Asp.Net Core Web Api to perform all CRUD operations. 4 | 5 | This is a simple api that consist of two entities called Product and ProductCategory. 6 | 7 | localhost:5000/api/products -> classic rest endpoint 8 | 9 | localhost:5000/odata/products -> odata rest endpoint 10 | 11 | You can check following sample OData queries and results. 12 | 13 | if you like Postman, just import AspNetCore_OData.postman_collection.json file and you will see more sample query in Get folders. 14 | 15 | 16 | 17 | 18 | #### Get All Categories - http://localhost:5000/odata/product_categories?$count=true 19 | 20 | ```javascript 21 | { 22 | "@odata.context": "http://localhost:5000/odata/$metadata#product_categories", 23 | "@odata.count": 3, 24 | "value": [ 25 | { 26 | "id": "1236a458-0802-4340-bdd4-05859c9aaaad", 27 | "categoryName": "Headphones" 28 | }, 29 | { 30 | "id": "8b726886-e719-413c-a125-939ee5af387d", 31 | "categoryName": "TV" 32 | }, 33 | { 34 | "id": "a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0", 35 | "categoryName": "Computers" 36 | } 37 | ] 38 | } 39 | ``` 40 |
41 | 42 | #### Get All Products - http://localhost:5000/odata/products?$count=true 43 | 44 | ```javascript 45 | { 46 | "@odata.context": "http://localhost:5000/odata/$metadata#products", 47 | "@odata.count": 3, 48 | "value": [ 49 | { 50 | "id": "6d42ac79-2b65-460d-9991-22b86ad66fb9", 51 | "productName": "JBL Tune", 52 | "categoryId": "1236a458-0802-4340-bdd4-05859c9aaaad", 53 | "description": "JBL Tune 500BT On-Ear", 54 | "price": 15, 55 | "weight": 0.3 56 | }, 57 | { 58 | "id": "78a4a7db-073f-4ad9-a157-2a9da0ceae38", 59 | "productName": "HP Zbook", 60 | "categoryId": "a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0", 61 | "description": "HP Zbook Laptop", 62 | "price": 2000, 63 | "weight": 3 64 | }, 65 | { 66 | "id": "aec7ce71-bfa6-4b0f-8aef-78816a31c9fa", 67 | "productName": "LG 32-Inch", 68 | "categoryId": "8b726886-e719-413c-a125-939ee5af387d", 69 | "description": "LG 32-Inch 720p LED TV", 70 | "price": 12000, 71 | "weight": 60 72 | } 73 | ] 74 | } 75 | ``` 76 |
77 | 78 | #### Get Product by ID - http://localhost:5000/odata/products(010b34c3-5d91-4f15-a926-a4edbc3a9770) 79 | 80 | ```javascript 81 | { 82 | "@odata.context": "http://localhost:5000/odata/$metadata#products/$entity", 83 | "id": "6d42ac79-2b65-460d-9991-22b86ad66fb9", 84 | "productName": "JBL Tune", 85 | "categoryId": "1236a458-0802-4340-bdd4-05859c9aaaad", 86 | "description": "JBL Tune 500BT On-Ear", 87 | "price": 15, 88 | "weight": 0.3 89 | } 90 | ``` 91 |
92 | 93 | #### Filter by Price (price greater than 10000) - http://localhost:5000/odata/products?$filter=price%20gt%2010000 94 | 95 | ```javascript 96 | { 97 | "@odata.context": "http://localhost:5000/odata/$metadata#products", 98 | "value": [ 99 | { 100 | "id": "aec7ce71-bfa6-4b0f-8aef-78816a31c9fa", 101 | "productName": "LG 32-Inch", 102 | "categoryId": "8b726886-e719-413c-a125-939ee5af387d", 103 | "description": "LG 32-Inch 720p LED TV", 104 | "price": 12000, 105 | "weight": 60 106 | } 107 | ] 108 | } 109 | ``` 110 |
111 | 112 | #### Order by Price - http://localhost:5000/odata/products?$orderby=price 113 | 114 | ```javascript 115 | { 116 | "@odata.context": "http://localhost:5000/odata/$metadata#products", 117 | "value": [ 118 | { 119 | "id": "6d42ac79-2b65-460d-9991-22b86ad66fb9", 120 | "productName": "JBL Tune", 121 | "categoryId": "1236a458-0802-4340-bdd4-05859c9aaaad", 122 | "description": "JBL Tune 500BT On-Ear", 123 | "price": 15, 124 | "weight": 0.3 125 | }, 126 | { 127 | "id": "78a4a7db-073f-4ad9-a157-2a9da0ceae38", 128 | "productName": "HP Zbook", 129 | "categoryId": "a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0", 130 | "description": "HP Zbook Laptop", 131 | "price": 2000, 132 | "weight": 3 133 | }, 134 | { 135 | "id": "aec7ce71-bfa6-4b0f-8aef-78816a31c9fa", 136 | "productName": "LG 32-Inch", 137 | "categoryId": "8b726886-e719-413c-a125-939ee5af387d", 138 | "description": "LG 32-Inch 720p LED TV", 139 | "price": 12000, 140 | "weight": 60 141 | } 142 | ] 143 | } 144 | ``` 145 |
146 | 147 | #### Skip & Take - http://localhost:5000/odata/products?$count=true&$top=2&$skip=1 148 | 149 | ```javascript 150 | { 151 | "@odata.context": "http://localhost:5000/odata/$metadata#products", 152 | "@odata.count": 3, 153 | "value": [ 154 | { 155 | "id": "78a4a7db-073f-4ad9-a157-2a9da0ceae38", 156 | "productName": "HP Zbook", 157 | "categoryId": "a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0", 158 | "description": "HP Zbook Laptop", 159 | "price": 2000, 160 | "weight": 3 161 | }, 162 | { 163 | "id": "aec7ce71-bfa6-4b0f-8aef-78816a31c9fa", 164 | "productName": "LG 32-Inch", 165 | "categoryId": "8b726886-e719-413c-a125-939ee5af387d", 166 | "description": "LG 32-Inch 720p LED TV", 167 | "price": 12000, 168 | "weight": 60 169 | } 170 | ] 171 | } 172 | ``` 173 |
174 | 175 | #### Get Product Count - http://localhost:5000/odata/products?$top=0&$count=true 176 | 177 | ```javascript 178 | { 179 | "@odata.context": "http://localhost:5000/odata/$metadata#products", 180 | "@odata.count": 3, 181 | "value": [] 182 | } 183 | ``` 184 |
185 | 186 | #### Select Specific Fields - http://localhost:5000/odata/products?$select=id,productname 187 | 188 | ```javascript 189 | { 190 | "@odata.context": "http://localhost:5000/odata/$metadata#products(id,productName)", 191 | "value": [ 192 | { 193 | "id": "6d42ac79-2b65-460d-9991-22b86ad66fb9", 194 | "productName": "JBL Tune" 195 | }, 196 | { 197 | "id": "78a4a7db-073f-4ad9-a157-2a9da0ceae38", 198 | "productName": "HP Zbook" 199 | }, 200 | { 201 | "id": "aec7ce71-bfa6-4b0f-8aef-78816a31c9fa", 202 | "productName": "LG 32-Inch" 203 | } 204 | ] 205 | } 206 | ``` 207 |
208 | 209 | #### Expand with CategoryGroup - http://localhost:5000/odata/products?$expand=category 210 | 211 | ```javascript 212 | { 213 | "@odata.context": "http://localhost:5000/odata/$metadata#products", 214 | "value": [ 215 | { 216 | "id": "6d42ac79-2b65-460d-9991-22b86ad66fb9", 217 | "productName": "JBL Tune", 218 | "categoryId": "1236a458-0802-4340-bdd4-05859c9aaaad", 219 | "description": "JBL Tune 500BT On-Ear", 220 | "price": 15, 221 | "weight": 0.3, 222 | "category": { 223 | "id": "1236a458-0802-4340-bdd4-05859c9aaaad", 224 | "categoryName": "Headphones" 225 | } 226 | }, 227 | { 228 | "id": "78a4a7db-073f-4ad9-a157-2a9da0ceae38", 229 | "productName": "HP Zbook", 230 | "categoryId": "a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0", 231 | "description": "HP Zbook Laptop", 232 | "price": 2000, 233 | "weight": 3, 234 | "category": { 235 | "id": "a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0", 236 | "categoryName": "Computers" 237 | } 238 | }, 239 | { 240 | "id": "aec7ce71-bfa6-4b0f-8aef-78816a31c9fa", 241 | "productName": "LG 32-Inch", 242 | "categoryId": "8b726886-e719-413c-a125-939ee5af387d", 243 | "description": "LG 32-Inch 720p LED TV", 244 | "price": 12000, 245 | "weight": 60, 246 | "category": { 247 | "id": "8b726886-e719-413c-a125-939ee5af387d", 248 | "categoryName": "TV" 249 | } 250 | } 251 | ] 252 | } 253 | ``` 254 |
255 | 256 | #### Expand with CategoryGroup and Select - http://localhost:5000/odata/products?$expand=category($select=categoryName)&$select=id,productName 257 | 258 | ```javascript 259 | { 260 | "@odata.context": "http://localhost:5000/odata/$metadata#products(id,productName,category(categoryName))", 261 | "value": [ 262 | { 263 | "id": "6d42ac79-2b65-460d-9991-22b86ad66fb9", 264 | "productName": "JBL Tune", 265 | "category": { 266 | "categoryName": "Headphones" 267 | } 268 | }, 269 | { 270 | "id": "78a4a7db-073f-4ad9-a157-2a9da0ceae38", 271 | "productName": "HP Zbook", 272 | "category": { 273 | "categoryName": "Computers" 274 | } 275 | }, 276 | { 277 | "id": "aec7ce71-bfa6-4b0f-8aef-78816a31c9fa", 278 | "productName": "LG 32-Inch", 279 | "category": { 280 | "categoryName": "TV" 281 | } 282 | } 283 | ] 284 | } 285 | ``` 286 |
287 | 288 | ### Running Locally 289 | 290 | - docker-comppose up (for postgres instance) 291 | - dotnet run 292 | 293 |
294 | 295 | ### Migration 296 | 297 | - dotnet ef migrations add "init" -c ProductDbContext -p src/OData.WebApi --output-dir Data/Migrations 298 | -------------------------------------------------------------------------------- /aspnet-core-web-api-using-odata.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{13F32673-540E-4553-B723-6823F01DA296}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData.WebApi", "src\OData.WebApi\OData.WebApi.csproj", "{63C6CBC9-3E0E-4593-A801-A9023E10F664}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Debug|x64.Build.0 = Debug|Any CPU 27 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Debug|x86.Build.0 = Debug|Any CPU 29 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Release|x64.ActiveCfg = Release|Any CPU 32 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Release|x64.Build.0 = Release|Any CPU 33 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Release|x86.ActiveCfg = Release|Any CPU 34 | {63C6CBC9-3E0E-4593-A801-A9023E10F664}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {63C6CBC9-3E0E-4593-A801-A9023E10F664} = {13F32673-540E-4553-B723-6823F01DA296} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | postgres: 4 | image: postgres:10.6-alpine 5 | container_name: odata_postgres 6 | ports: 7 | - "5436:5432" 8 | environment: 9 | - POSTGRES_DB=OData 10 | - POSTGRES_USER=postgres 11 | - POSTGRES_PASSWORD=postgres -------------------------------------------------------------------------------- /postman.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suadev/aspnet-core-web-api-using-odata/df83d62d6ee939e626e04cf0b56d0a271731c454/postman.JPG -------------------------------------------------------------------------------- /src/OData.WebApi/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Mapster; 5 | using Microsoft.AspNet.OData; 6 | using Microsoft.AspNetCore.Mvc; 7 | using OData.WebApi.Data.Entities; 8 | using OData.WebApi.Data.Repositories; 9 | 10 | namespace Pms.Api.Controllers 11 | { 12 | [ApiController] 13 | public class BaseController : ControllerBase 14 | where TEntity : BaseEntity 15 | { 16 | private readonly IRepository _repository; 17 | public BaseController(IRepository repository) 18 | { 19 | _repository = repository; 20 | } 21 | public IActionResult Get() 22 | { 23 | return Ok(_repository.Query().ProjectToType()); 24 | } 25 | 26 | [HttpGet("({id})")] 27 | public virtual SingleResult Get([FromODataUri] Guid id) 28 | { 29 | var entity = _repository.Query().Where(q => q.Id.Equals(id)).ProjectToType(); 30 | return SingleResult.Create(entity); 31 | } 32 | 33 | [HttpPost] 34 | public virtual async Task Post([FromBody] TEntity entity) 35 | { 36 | _repository.Add(entity); 37 | await _repository.SaveChangesAsync(); 38 | return Ok(); 39 | } 40 | 41 | [HttpPut] 42 | public virtual async Task Put([FromBody] TEntity entity) 43 | { 44 | if (await _repository.IsExistAsync(entity.Id) == false) 45 | { 46 | return NotFound(); 47 | } 48 | 49 | _repository.Update(entity); 50 | await _repository.SaveChangesAsync(); 51 | return Ok(); 52 | } 53 | 54 | [HttpDelete("{id}")] 55 | public virtual async Task Delete(Guid id) 56 | { 57 | if (await _repository.IsExistAsync(id) == false) 58 | { 59 | return NotFound(); 60 | } 61 | 62 | _repository.Delete(id); 63 | await _repository.SaveChangesAsync(); 64 | return Ok(); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Controllers/OData/ODataBaseController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNet.OData; 5 | using Microsoft.AspNet.OData.Routing; 6 | using Microsoft.AspNetCore.Mvc; 7 | using OData.WebApi.Data.Entities; 8 | using OData.WebApi.Data.Repositories; 9 | 10 | namespace Pms.Api.Controllers 11 | { 12 | public class ODataBaseController : ODataController 13 | where TEntity : BaseEntity 14 | { 15 | private readonly IRepository _repository; 16 | public ODataBaseController(IRepository repository) 17 | { 18 | _repository = repository; 19 | } 20 | 21 | [ODataRoute] 22 | [EnableQuery(PageSize = 2)] 23 | public IActionResult Get() 24 | { 25 | return Ok(_repository.Query()); 26 | } 27 | 28 | [ODataRoute("({id})")] 29 | [EnableQuery] 30 | public virtual SingleResult Get([FromODataUri] Guid id) 31 | { 32 | var entity = _repository.Query().Where(q => q.Id.Equals(id)); 33 | return SingleResult.Create(entity); 34 | } 35 | 36 | [ODataRoute()] 37 | public virtual async Task Post([FromBody] TEntity entity) 38 | { 39 | _repository.Add(entity); 40 | await _repository.SaveChangesAsync(); 41 | return Ok(); 42 | } 43 | 44 | [ODataRoute] 45 | [HttpPut] 46 | public virtual async Task Put([FromBody] TEntity entity) 47 | { 48 | if (await _repository.IsExistAsync(entity.Id) == false) 49 | { 50 | return NotFound(); 51 | } 52 | 53 | _repository.Update(entity); 54 | await _repository.SaveChangesAsync(); 55 | return Ok(); 56 | } 57 | 58 | [ODataRoute("{id}")] 59 | [HttpDelete()] 60 | public virtual async Task Delete(Guid id) 61 | { 62 | if (await _repository.IsExistAsync(id) == false) 63 | { 64 | return NotFound(); 65 | } 66 | 67 | _repository.Delete(id); 68 | await _repository.SaveChangesAsync(); 69 | return Ok(); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Controllers/OData/ProductCategoryODataController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.OData.Routing; 2 | using OData.WebApi.Data.Entities; 3 | using OData.WebApi.Data.Repositories; 4 | 5 | namespace Pms.Api.Controllers.OData 6 | { 7 | [ODataRoutePrefix("product_categories")] 8 | public class ProductCategoryODataController : ODataBaseController 9 | { 10 | public ProductCategoryODataController(IRepository productCategoryRepository) 11 | : base(productCategoryRepository) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Controllers/OData/ProductODataController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.OData.Routing; 2 | using OData.WebApi.Data.Entities; 3 | using OData.WebApi.Data.Repositories; 4 | 5 | namespace Pms.Api.Controllers.OData 6 | { 7 | [ODataRoutePrefix("products")] 8 | public class ProductODataController : ODataBaseController 9 | { 10 | public ProductODataController(IRepository productRepository) 11 | : base(productRepository) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Controllers/ProductCategoryController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using OData.WebApi.Data.Entities; 3 | using OData.WebApi.Data.Repositories; 4 | using OData.WebApi.Dto; 5 | 6 | namespace Pms.Api.Controllers.OData 7 | { 8 | [Route("api/product_categories")] 9 | public class ProductCategoryController : BaseController 10 | { 11 | public ProductCategoryController(IRepository productRepository) 12 | : base(productRepository) 13 | { 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Controllers/ProductController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using OData.WebApi.Data.Entities; 3 | using OData.WebApi.Data.Repositories; 4 | using OData.WebApi.Dto; 5 | 6 | namespace Pms.Api.Controllers.OData 7 | { 8 | [Route("api/products")] 9 | public class ProductController : BaseController 10 | { 11 | public ProductController(IRepository productRepository) 12 | : base(productRepository) 13 | { 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Entities/Base/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OData.WebApi.Data.Entities 4 | { 5 | public class BaseEntity 6 | { 7 | public Guid Id { get; set; } 8 | public DateTime CreatedDate { get; set; } 9 | public string CreatedBy { get; set; } 10 | public DateTime? UpdatedDate { get; set; } 11 | public string UpdatedBy { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Entities/Product.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OData.WebApi.Data.Entities 4 | { 5 | public class Product : BaseEntity 6 | { 7 | public string ProductName { get; set; } 8 | public Guid CategoryId { get; set; } 9 | public ProductCategory Category { get; set; } 10 | public string Description { get; set; } 11 | public decimal Price { get; set; } 12 | public double Weight { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Entities/ProductCategory.cs: -------------------------------------------------------------------------------- 1 | namespace OData.WebApi.Data.Entities 2 | { 3 | public class ProductCategory : BaseEntity 4 | { 5 | public string CategoryName { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Migrations/20191030195859_init.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; 8 | using OData.WebApi.Data; 9 | 10 | namespace OData.WebApi.Data.Migrations 11 | { 12 | [DbContext(typeof(ProductDbContext))] 13 | [Migration("20191030195859_init")] 14 | partial class init 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) 21 | .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") 22 | .HasAnnotation("Relational:MaxIdentifierLength", 63); 23 | 24 | modelBuilder.Entity("OData.WebApi.Data.Entities.Product", b => 25 | { 26 | b.Property("Id") 27 | .ValueGeneratedOnAdd(); 28 | 29 | b.Property("CategoryId"); 30 | 31 | b.Property("CreatedBy") 32 | .ValueGeneratedOnAdd() 33 | .HasDefaultValue("skose"); 34 | 35 | b.Property("CreatedDate") 36 | .ValueGeneratedOnAdd() 37 | .HasDefaultValue(new DateTime(2019, 10, 30, 22, 58, 59, 249, DateTimeKind.Local).AddTicks(675)); 38 | 39 | b.Property("Description"); 40 | 41 | b.Property("Price"); 42 | 43 | b.Property("ProductName"); 44 | 45 | b.Property("UpdatedBy"); 46 | 47 | b.Property("UpdatedDate"); 48 | 49 | b.Property("Weight"); 50 | 51 | b.HasKey("Id"); 52 | 53 | b.HasIndex("CategoryId"); 54 | 55 | b.ToTable("Product"); 56 | 57 | b.HasData( 58 | new 59 | { 60 | Id = new Guid("bc4987fe-1226-419c-b140-cf6327542b32"), 61 | CategoryId = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 62 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 63 | Description = "HP Zbook Laptop", 64 | Price = 2000m, 65 | ProductName = "HP Zbook", 66 | Weight = 3.0 67 | }, 68 | new 69 | { 70 | Id = new Guid("0ffd7b32-0df0-40c0-9905-6ad65ae11433"), 71 | CategoryId = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 72 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 73 | Description = "MacBook Laptop", 74 | Price = 3000m, 75 | ProductName = "MacBook Pro", 76 | Weight = 2.1000000000000001 77 | }, 78 | new 79 | { 80 | Id = new Guid("baaeab63-7bab-467c-a06f-274eedea528a"), 81 | CategoryId = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 82 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 83 | Description = "Lenovo Thinkpad Laptop", 84 | Price = 2800m, 85 | ProductName = "Lenovo Thinkpad", 86 | Weight = 1.7 87 | }, 88 | new 89 | { 90 | Id = new Guid("2f2aed89-f417-4225-822a-113baf3041f5"), 91 | CategoryId = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 92 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 93 | Description = "LG 32-Inch 720p LED TV", 94 | Price = 12000m, 95 | ProductName = "LG 32-Inch", 96 | Weight = 60.0 97 | }, 98 | new 99 | { 100 | Id = new Guid("570b67c6-7a24-4041-8582-0ceba4debf0b"), 101 | CategoryId = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 102 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 103 | Description = "Sony 65-Inch 4K Ultra HD Smart LED TV", 104 | Price = 10000m, 105 | ProductName = "Sony 65-Inch", 106 | Weight = 70.0 107 | }, 108 | new 109 | { 110 | Id = new Guid("5d54be88-12fa-4ccf-b394-c8d909111449"), 111 | CategoryId = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 112 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 113 | Description = "Samsung 32-Inch 1080p Smart LED TV", 114 | Price = 15000m, 115 | ProductName = "Samsung 32-Inch", 116 | Weight = 50.0 117 | }, 118 | new 119 | { 120 | Id = new Guid("746e00e0-49dc-42a8-b935-96697f772478"), 121 | CategoryId = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 122 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 123 | Description = "JBL Tune 500BT On-Ear", 124 | Price = 15m, 125 | ProductName = "JBL Tune", 126 | Weight = 0.29999999999999999 127 | }, 128 | new 129 | { 130 | Id = new Guid("218a34f5-52a0-4106-9c98-ba3acdf3db6f"), 131 | CategoryId = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 132 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 133 | Description = "Panasonic ErgoFit In-Ear", 134 | Price = 29m, 135 | ProductName = "Panasonic ErgoFit", 136 | Weight = 0.40000000000000002 137 | }, 138 | new 139 | { 140 | Id = new Guid("390ab7d0-3d23-456b-854c-0e5b153b08fa"), 141 | CategoryId = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 142 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 143 | Description = "Sennheiser CX Wireless In-Ear", 144 | Price = 44m, 145 | ProductName = "Sennheiser CX", 146 | Weight = 0.40000000000000002 147 | }); 148 | }); 149 | 150 | modelBuilder.Entity("OData.WebApi.Data.Entities.ProductCategory", b => 151 | { 152 | b.Property("Id") 153 | .ValueGeneratedOnAdd(); 154 | 155 | b.Property("CategoryName"); 156 | 157 | b.Property("CreatedBy") 158 | .ValueGeneratedOnAdd() 159 | .HasDefaultValue("skose"); 160 | 161 | b.Property("CreatedDate") 162 | .ValueGeneratedOnAdd() 163 | .HasDefaultValue(new DateTime(2019, 10, 30, 22, 58, 59, 253, DateTimeKind.Local).AddTicks(537)); 164 | 165 | b.Property("UpdatedBy"); 166 | 167 | b.Property("UpdatedDate"); 168 | 169 | b.HasKey("Id"); 170 | 171 | b.ToTable("ProductCategory"); 172 | 173 | b.HasData( 174 | new 175 | { 176 | Id = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 177 | CategoryName = "TV", 178 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) 179 | }, 180 | new 181 | { 182 | Id = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 183 | CategoryName = "Headphones", 184 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) 185 | }, 186 | new 187 | { 188 | Id = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 189 | CategoryName = "Computers", 190 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) 191 | }); 192 | }); 193 | 194 | modelBuilder.Entity("OData.WebApi.Data.Entities.Product", b => 195 | { 196 | b.HasOne("OData.WebApi.Data.Entities.ProductCategory", "Category") 197 | .WithMany() 198 | .HasForeignKey("CategoryId") 199 | .OnDelete(DeleteBehavior.Cascade); 200 | }); 201 | #pragma warning restore 612, 618 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Migrations/20191030195859_init.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace OData.WebApi.Data.Migrations 5 | { 6 | public partial class init : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "ProductCategory", 12 | columns: table => new 13 | { 14 | Id = table.Column(nullable: false), 15 | CreatedDate = table.Column(nullable: false, defaultValue: new DateTime(2019, 10, 30, 22, 58, 59, 253, DateTimeKind.Local).AddTicks(537)), 16 | CreatedBy = table.Column(nullable: true, defaultValue: "skose"), 17 | UpdatedDate = table.Column(nullable: true), 18 | UpdatedBy = table.Column(nullable: true), 19 | CategoryName = table.Column(nullable: true) 20 | }, 21 | constraints: table => 22 | { 23 | table.PrimaryKey("PK_ProductCategory", x => x.Id); 24 | }); 25 | 26 | migrationBuilder.CreateTable( 27 | name: "Product", 28 | columns: table => new 29 | { 30 | Id = table.Column(nullable: false), 31 | CreatedDate = table.Column(nullable: false, defaultValue: new DateTime(2019, 10, 30, 22, 58, 59, 249, DateTimeKind.Local).AddTicks(675)), 32 | CreatedBy = table.Column(nullable: true, defaultValue: "skose"), 33 | UpdatedDate = table.Column(nullable: true), 34 | UpdatedBy = table.Column(nullable: true), 35 | ProductName = table.Column(nullable: true), 36 | CategoryId = table.Column(nullable: false), 37 | Description = table.Column(nullable: true), 38 | Price = table.Column(nullable: false), 39 | Weight = table.Column(nullable: false) 40 | }, 41 | constraints: table => 42 | { 43 | table.PrimaryKey("PK_Product", x => x.Id); 44 | table.ForeignKey( 45 | name: "FK_Product_ProductCategory_CategoryId", 46 | column: x => x.CategoryId, 47 | principalTable: "ProductCategory", 48 | principalColumn: "Id", 49 | onDelete: ReferentialAction.Cascade); 50 | }); 51 | 52 | migrationBuilder.InsertData( 53 | table: "ProductCategory", 54 | columns: new[] { "Id", "CategoryName", "UpdatedBy", "UpdatedDate" }, 55 | values: new object[] { new Guid("8b726886-e719-413c-a125-939ee5af387d"), "TV", null, null }); 56 | 57 | migrationBuilder.InsertData( 58 | table: "ProductCategory", 59 | columns: new[] { "Id", "CategoryName", "UpdatedBy", "UpdatedDate" }, 60 | values: new object[] { new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), "Headphones", null, null }); 61 | 62 | migrationBuilder.InsertData( 63 | table: "ProductCategory", 64 | columns: new[] { "Id", "CategoryName", "UpdatedBy", "UpdatedDate" }, 65 | values: new object[] { new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), "Computers", null, null }); 66 | 67 | migrationBuilder.InsertData( 68 | table: "Product", 69 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 70 | values: new object[] { new Guid("2f2aed89-f417-4225-822a-113baf3041f5"), new Guid("8b726886-e719-413c-a125-939ee5af387d"), "LG 32-Inch 720p LED TV", 12000m, "LG 32-Inch", null, null, 60.0 }); 71 | 72 | migrationBuilder.InsertData( 73 | table: "Product", 74 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 75 | values: new object[] { new Guid("570b67c6-7a24-4041-8582-0ceba4debf0b"), new Guid("8b726886-e719-413c-a125-939ee5af387d"), "Sony 65-Inch 4K Ultra HD Smart LED TV", 10000m, "Sony 65-Inch", null, null, 70.0 }); 76 | 77 | migrationBuilder.InsertData( 78 | table: "Product", 79 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 80 | values: new object[] { new Guid("5d54be88-12fa-4ccf-b394-c8d909111449"), new Guid("8b726886-e719-413c-a125-939ee5af387d"), "Samsung 32-Inch 1080p Smart LED TV", 15000m, "Samsung 32-Inch", null, null, 50.0 }); 81 | 82 | migrationBuilder.InsertData( 83 | table: "Product", 84 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 85 | values: new object[] { new Guid("746e00e0-49dc-42a8-b935-96697f772478"), new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), "JBL Tune 500BT On-Ear", 15m, "JBL Tune", null, null, 0.29999999999999999 }); 86 | 87 | migrationBuilder.InsertData( 88 | table: "Product", 89 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 90 | values: new object[] { new Guid("218a34f5-52a0-4106-9c98-ba3acdf3db6f"), new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), "Panasonic ErgoFit In-Ear", 29m, "Panasonic ErgoFit", null, null, 0.40000000000000002 }); 91 | 92 | migrationBuilder.InsertData( 93 | table: "Product", 94 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 95 | values: new object[] { new Guid("390ab7d0-3d23-456b-854c-0e5b153b08fa"), new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), "Sennheiser CX Wireless In-Ear", 44m, "Sennheiser CX", null, null, 0.40000000000000002 }); 96 | 97 | migrationBuilder.InsertData( 98 | table: "Product", 99 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 100 | values: new object[] { new Guid("bc4987fe-1226-419c-b140-cf6327542b32"), new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), "HP Zbook Laptop", 2000m, "HP Zbook", null, null, 3.0 }); 101 | 102 | migrationBuilder.InsertData( 103 | table: "Product", 104 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 105 | values: new object[] { new Guid("0ffd7b32-0df0-40c0-9905-6ad65ae11433"), new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), "MacBook Laptop", 3000m, "MacBook Pro", null, null, 2.1000000000000001 }); 106 | 107 | migrationBuilder.InsertData( 108 | table: "Product", 109 | columns: new[] { "Id", "CategoryId", "Description", "Price", "ProductName", "UpdatedBy", "UpdatedDate", "Weight" }, 110 | values: new object[] { new Guid("baaeab63-7bab-467c-a06f-274eedea528a"), new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), "Lenovo Thinkpad Laptop", 2800m, "Lenovo Thinkpad", null, null, 1.7 }); 111 | 112 | migrationBuilder.CreateIndex( 113 | name: "IX_Product_CategoryId", 114 | table: "Product", 115 | column: "CategoryId"); 116 | } 117 | 118 | protected override void Down(MigrationBuilder migrationBuilder) 119 | { 120 | migrationBuilder.DropTable( 121 | name: "Product"); 122 | 123 | migrationBuilder.DropTable( 124 | name: "ProductCategory"); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Migrations/ProductDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 6 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; 7 | using OData.WebApi.Data; 8 | 9 | namespace OData.WebApi.Data.Migrations 10 | { 11 | [DbContext(typeof(ProductDbContext))] 12 | partial class ProductDbContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) 19 | .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") 20 | .HasAnnotation("Relational:MaxIdentifierLength", 63); 21 | 22 | modelBuilder.Entity("OData.WebApi.Data.Entities.Product", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd(); 26 | 27 | b.Property("CategoryId"); 28 | 29 | b.Property("CreatedBy") 30 | .ValueGeneratedOnAdd() 31 | .HasDefaultValue("skose"); 32 | 33 | b.Property("CreatedDate") 34 | .ValueGeneratedOnAdd() 35 | .HasDefaultValue(new DateTime(2019, 10, 30, 22, 58, 59, 249, DateTimeKind.Local).AddTicks(675)); 36 | 37 | b.Property("Description"); 38 | 39 | b.Property("Price"); 40 | 41 | b.Property("ProductName"); 42 | 43 | b.Property("UpdatedBy"); 44 | 45 | b.Property("UpdatedDate"); 46 | 47 | b.Property("Weight"); 48 | 49 | b.HasKey("Id"); 50 | 51 | b.HasIndex("CategoryId"); 52 | 53 | b.ToTable("Product"); 54 | 55 | b.HasData( 56 | new 57 | { 58 | Id = new Guid("bc4987fe-1226-419c-b140-cf6327542b32"), 59 | CategoryId = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 60 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 61 | Description = "HP Zbook Laptop", 62 | Price = 2000m, 63 | ProductName = "HP Zbook", 64 | Weight = 3.0 65 | }, 66 | new 67 | { 68 | Id = new Guid("0ffd7b32-0df0-40c0-9905-6ad65ae11433"), 69 | CategoryId = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 70 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 71 | Description = "MacBook Laptop", 72 | Price = 3000m, 73 | ProductName = "MacBook Pro", 74 | Weight = 2.1000000000000001 75 | }, 76 | new 77 | { 78 | Id = new Guid("baaeab63-7bab-467c-a06f-274eedea528a"), 79 | CategoryId = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 80 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 81 | Description = "Lenovo Thinkpad Laptop", 82 | Price = 2800m, 83 | ProductName = "Lenovo Thinkpad", 84 | Weight = 1.7 85 | }, 86 | new 87 | { 88 | Id = new Guid("2f2aed89-f417-4225-822a-113baf3041f5"), 89 | CategoryId = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 90 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 91 | Description = "LG 32-Inch 720p LED TV", 92 | Price = 12000m, 93 | ProductName = "LG 32-Inch", 94 | Weight = 60.0 95 | }, 96 | new 97 | { 98 | Id = new Guid("570b67c6-7a24-4041-8582-0ceba4debf0b"), 99 | CategoryId = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 100 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 101 | Description = "Sony 65-Inch 4K Ultra HD Smart LED TV", 102 | Price = 10000m, 103 | ProductName = "Sony 65-Inch", 104 | Weight = 70.0 105 | }, 106 | new 107 | { 108 | Id = new Guid("5d54be88-12fa-4ccf-b394-c8d909111449"), 109 | CategoryId = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 110 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 111 | Description = "Samsung 32-Inch 1080p Smart LED TV", 112 | Price = 15000m, 113 | ProductName = "Samsung 32-Inch", 114 | Weight = 50.0 115 | }, 116 | new 117 | { 118 | Id = new Guid("746e00e0-49dc-42a8-b935-96697f772478"), 119 | CategoryId = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 120 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 121 | Description = "JBL Tune 500BT On-Ear", 122 | Price = 15m, 123 | ProductName = "JBL Tune", 124 | Weight = 0.29999999999999999 125 | }, 126 | new 127 | { 128 | Id = new Guid("218a34f5-52a0-4106-9c98-ba3acdf3db6f"), 129 | CategoryId = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 130 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 131 | Description = "Panasonic ErgoFit In-Ear", 132 | Price = 29m, 133 | ProductName = "Panasonic ErgoFit", 134 | Weight = 0.40000000000000002 135 | }, 136 | new 137 | { 138 | Id = new Guid("390ab7d0-3d23-456b-854c-0e5b153b08fa"), 139 | CategoryId = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 140 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), 141 | Description = "Sennheiser CX Wireless In-Ear", 142 | Price = 44m, 143 | ProductName = "Sennheiser CX", 144 | Weight = 0.40000000000000002 145 | }); 146 | }); 147 | 148 | modelBuilder.Entity("OData.WebApi.Data.Entities.ProductCategory", b => 149 | { 150 | b.Property("Id") 151 | .ValueGeneratedOnAdd(); 152 | 153 | b.Property("CategoryName"); 154 | 155 | b.Property("CreatedBy") 156 | .ValueGeneratedOnAdd() 157 | .HasDefaultValue("skose"); 158 | 159 | b.Property("CreatedDate") 160 | .ValueGeneratedOnAdd() 161 | .HasDefaultValue(new DateTime(2019, 10, 30, 22, 58, 59, 253, DateTimeKind.Local).AddTicks(537)); 162 | 163 | b.Property("UpdatedBy"); 164 | 165 | b.Property("UpdatedDate"); 166 | 167 | b.HasKey("Id"); 168 | 169 | b.ToTable("ProductCategory"); 170 | 171 | b.HasData( 172 | new 173 | { 174 | Id = new Guid("8b726886-e719-413c-a125-939ee5af387d"), 175 | CategoryName = "TV", 176 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) 177 | }, 178 | new 179 | { 180 | Id = new Guid("1236a458-0802-4340-bdd4-05859c9aaaad"), 181 | CategoryName = "Headphones", 182 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) 183 | }, 184 | new 185 | { 186 | Id = new Guid("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 187 | CategoryName = "Computers", 188 | CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) 189 | }); 190 | }); 191 | 192 | modelBuilder.Entity("OData.WebApi.Data.Entities.Product", b => 193 | { 194 | b.HasOne("OData.WebApi.Data.Entities.ProductCategory", "Category") 195 | .WithMany() 196 | .HasForeignKey("CategoryId") 197 | .OnDelete(DeleteBehavior.Cascade); 198 | }); 199 | #pragma warning restore 612, 618 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/OData.WebApi/Data/ProductDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using OData.WebApi.Data.TypeConfigurations; 3 | 4 | namespace OData.WebApi.Data 5 | { 6 | public class ProductDbContext : DbContext 7 | { 8 | public ProductDbContext(DbContextOptions dbOptions) 9 | : base(dbOptions) 10 | { 11 | 12 | } 13 | 14 | protected override void OnModelCreating(ModelBuilder builder) 15 | { 16 | builder.ApplyConfiguration(new ProductTypeConfiguration()); 17 | builder.ApplyConfiguration(new ProductCategoryTypeConfiguration()); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Repositories/IRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using OData.WebApi.Data.Entities; 6 | 7 | namespace OData.WebApi.Data.Repositories 8 | { 9 | public interface IRepository 10 | where TEntity : BaseEntity 11 | { 12 | Task IsExistAsync(Guid id); 13 | void Add(TEntity obj); 14 | void Update(TEntity obj); 15 | void Delete(object id); 16 | Task SaveChangesAsync(); 17 | IQueryable Query(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/Repositories/Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using OData.WebApi.Data.Entities; 7 | 8 | namespace OData.WebApi.Data.Repositories 9 | { 10 | public class Repository : IRepository 11 | where TEntity : BaseEntity 12 | { 13 | private ProductDbContext _context; 14 | private DbSet _dbSet; 15 | public Repository(ProductDbContext context) 16 | { 17 | _context = context; 18 | _dbSet = _context.Set(); 19 | } 20 | 21 | public async Task IsExistAsync(Guid id) 22 | { 23 | return await _dbSet.AsNoTracking().FirstOrDefaultAsync(q => q.Id == id) != null; 24 | } 25 | public void Add(TEntity entity) 26 | { 27 | entity.CreatedDate = DateTime.Now; 28 | entity.CreatedBy = "admin"; 29 | _dbSet.Add(entity); 30 | } 31 | public void Update(TEntity entity) 32 | { 33 | entity.UpdatedDate = DateTime.Now; 34 | entity.UpdatedBy = "admin"; 35 | _dbSet.Attach(entity); 36 | _context.Entry(entity).State = EntityState.Modified; 37 | } 38 | public void Delete(object id) 39 | { 40 | TEntity existing = _dbSet.Find(id); 41 | _dbSet.Remove(existing); 42 | } 43 | public Task SaveChangesAsync() 44 | { 45 | return _context.SaveChangesAsync(); 46 | } 47 | 48 | public IQueryable Query() 49 | { 50 | return _dbSet; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/TypeConfigurations/ProductCategoryTypeConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 4 | using OData.WebApi.Data.Entities; 5 | 6 | namespace OData.WebApi.Data.TypeConfigurations 7 | { 8 | public class ProductCategoryTypeConfiguration : IEntityTypeConfiguration 9 | { 10 | public void Configure(EntityTypeBuilder builder) 11 | { 12 | builder.Property(s => s.CreatedDate).HasDefaultValue(DateTime.Now); 13 | builder.Property(s => s.CreatedBy).HasDefaultValue("skose"); 14 | 15 | builder.HasData( 16 | new ProductCategory 17 | { 18 | Id = Guid.Parse("8b726886-e719-413c-a125-939ee5af387d"), 19 | CategoryName = "TV" 20 | }, 21 | new ProductCategory 22 | { 23 | Id = Guid.Parse("1236a458-0802-4340-bdd4-05859c9aaaad"), 24 | CategoryName = "Headphones" 25 | }, 26 | new ProductCategory 27 | { 28 | Id = Guid.Parse("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 29 | CategoryName = "Computers" 30 | }); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Data/TypeConfigurations/ProductTypeConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 4 | using OData.WebApi.Data.Entities; 5 | 6 | namespace OData.WebApi.Data.TypeConfigurations 7 | { 8 | public class ProductTypeConfiguration : IEntityTypeConfiguration 9 | { 10 | public void Configure(EntityTypeBuilder builder) 11 | { 12 | builder.Property(s => s.CreatedDate).HasDefaultValue(DateTime.Now); 13 | builder.Property(s => s.CreatedBy).HasDefaultValue("skose"); 14 | 15 | builder.HasData( 16 | 17 | //Computer Products 18 | new Product 19 | { 20 | Id = Guid.NewGuid(), 21 | CategoryId = Guid.Parse("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 22 | ProductName = "HP Zbook", 23 | Description = "HP Zbook Laptop", 24 | Weight = 3, 25 | Price = 2000 26 | }, 27 | new Product 28 | { 29 | Id = Guid.NewGuid(), 30 | CategoryId = Guid.Parse("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 31 | ProductName = "MacBook Pro", 32 | Description = "MacBook Laptop", 33 | Weight = 2.1, 34 | Price = 3000 35 | }, 36 | new Product 37 | { 38 | Id = Guid.NewGuid(), 39 | CategoryId = Guid.Parse("a65bc1ae-c1c7-4c20-8b3b-4b48490d3fb0"), 40 | ProductName = "Lenovo Thinkpad", 41 | Description = "Lenovo Thinkpad Laptop", 42 | Weight = 1.7, 43 | Price = 2800 44 | }, 45 | 46 | //TV Products 47 | new Product 48 | { 49 | Id = Guid.NewGuid(), 50 | CategoryId = Guid.Parse("8b726886-e719-413c-a125-939ee5af387d"), 51 | ProductName = "LG 32-Inch", 52 | Description = "LG 32-Inch 720p LED TV", 53 | Weight = 60, 54 | Price = 12000 55 | }, 56 | new Product 57 | { 58 | Id = Guid.NewGuid(), 59 | CategoryId = Guid.Parse("8b726886-e719-413c-a125-939ee5af387d"), 60 | ProductName = "Sony 65-Inch", 61 | Description = "Sony 65-Inch 4K Ultra HD Smart LED TV", 62 | Weight = 70, 63 | Price = 10000 64 | }, 65 | new Product 66 | { 67 | Id = Guid.NewGuid(), 68 | CategoryId = Guid.Parse("8b726886-e719-413c-a125-939ee5af387d"), 69 | ProductName = "Samsung 32-Inch", 70 | Description = "Samsung 32-Inch 1080p Smart LED TV", 71 | Weight = 50, 72 | Price = 15000 73 | }, 74 | 75 | //Headphone Products 76 | new Product 77 | { 78 | Id = Guid.NewGuid(), 79 | CategoryId = Guid.Parse("1236a458-0802-4340-bdd4-05859c9aaaad"), 80 | ProductName = "JBL Tune", 81 | Description = "JBL Tune 500BT On-Ear", 82 | Weight = 0.3, 83 | Price = 15 84 | }, 85 | new Product 86 | { 87 | Id = Guid.NewGuid(), 88 | CategoryId = Guid.Parse("1236a458-0802-4340-bdd4-05859c9aaaad"), 89 | ProductName = "Panasonic ErgoFit", 90 | Description = "Panasonic ErgoFit In-Ear", 91 | Weight = 0.4, 92 | Price = 29 93 | }, 94 | new Product 95 | { 96 | Id = Guid.NewGuid(), 97 | CategoryId = Guid.Parse("1236a458-0802-4340-bdd4-05859c9aaaad"), 98 | ProductName = "Sennheiser CX", 99 | Description = "Sennheiser CX Wireless In-Ear", 100 | Weight = 0.4, 101 | Price = 44 102 | }); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Dto/ProductCategoryDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OData.WebApi.Dto 4 | { 5 | public class ProductCategoryDto 6 | { 7 | public Guid Id { get; set; } 8 | public string CategoryName { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Dto/ProductDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OData.WebApi.Data.Entities; 3 | 4 | namespace OData.WebApi.Dto 5 | { 6 | public class ProductDto 7 | { 8 | public Guid Id { get; set; } 9 | public string ProductName { get; set; } 10 | public Guid CategoryId { get; set; } 11 | public string Description { get; set; } 12 | public decimal Price { get; set; } 13 | public double Weight { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/OData.WebApi/OData.WebApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | InProcess 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/OData.WebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace OData.WebApi 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateWebHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 14 | WebHost.CreateDefaultBuilder(args) 15 | .UseStartup(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/OData.WebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:15577", 8 | "sslPort": 44330 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "api/values", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "OData.WebApi": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "api/values", 24 | "applicationUrl": "http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/OData.WebApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.AspNet.OData.Builder; 3 | using Microsoft.AspNet.OData.Extensions; 4 | using Microsoft.AspNet.OData.Formatter; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.EntityFrameworkCore; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Net.Http.Headers; 12 | using Microsoft.OData.Edm; 13 | using OData.WebApi.Data; 14 | using OData.WebApi.Data.Entities; 15 | using OData.WebApi.Data.Repositories; 16 | using OData.WebApi.Dto; 17 | 18 | namespace OData.WebApi 19 | { 20 | public class Startup 21 | { 22 | public Startup(IConfiguration configuration) 23 | { 24 | Configuration = configuration; 25 | } 26 | 27 | public IConfiguration Configuration { get; } 28 | 29 | public void ConfigureServices(IServiceCollection services) 30 | { 31 | services.AddDbContext( 32 | options => options.UseNpgsql(Configuration.GetConnectionString("ProductConStr"))); 33 | services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); 34 | 35 | services 36 | .AddMvc(options => options.EnableEndpointRouting = false) 37 | .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 38 | services.AddOData(); 39 | } 40 | 41 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 42 | { 43 | using (var serviceScope = app.ApplicationServices.GetRequiredService().CreateScope()) 44 | { 45 | using (var context = serviceScope.ServiceProvider.GetService()) 46 | { 47 | context.Database.Migrate(); 48 | } 49 | } 50 | 51 | app.UseMvc( 52 | routeBuilder => 53 | { 54 | // routeBuilder.EnableDependencyInjection(); //Enables the non-OData route container. 55 | // routeBuilder.Filter().Count().Expand().OrderBy().Select(); 56 | routeBuilder.MapODataServiceRoute("ODataRoute", "odata", GetEdmModel()); 57 | }); 58 | } 59 | 60 | private static IEdmModel GetEdmModel() 61 | { 62 | var builder = new ODataConventionModelBuilder(); 63 | builder.EnableLowerCamelCase(); 64 | 65 | builder.EntitySet("products") 66 | .EntityType.Filter().Count().Expand().OrderBy().Page().Select(); 67 | 68 | builder.EntitySet("product_categories") 69 | .EntityType.Filter().Count().Expand().OrderBy().Page().Select(); 70 | 71 | return builder.GetEdmModel(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/OData.WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "ProductConStr": "Host=localhost;Port=5436;Username=postgres;Password=postgres;Database=OData;" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Debug" 8 | } 9 | }, 10 | "AllowedHosts": "*" 11 | } --------------------------------------------------------------------------------