Compare commits

..

238 Commits

Author SHA1 Message Date
Steve Sanderson
678e230021 Example of using asp-prerender-data to get server-supplied data to both server and client JS 2017-05-22 10:25:08 +01:00
Steve Sanderson
ad645cbfe9 Remove build reference cycle caused by samples 2017-05-19 15:35:24 +01:00
Steve Sanderson
d5483f3b64 Add workaround for 'publish' tests failing since 2.0 update 2017-05-19 15:16:49 +01:00
Steve Sanderson
a4512873f9 Update templates' SpaServices dependency to 2.0.0-* 2017-05-19 14:57:13 +01:00
Steve Sanderson
9e59ed4bc8 Put back <IsPackable>false</IsPackable> on template csproj files 2017-05-19 12:12:52 +01:00
Steve Sanderson
b091932e10 Temporarily remove templates from .sln to avoid dependency cycle 2017-05-19 12:00:22 +01:00
Steve Sanderson
42b88c15f2 Make all templates as consistent as possible with stock MVC Core 2.0 one 2017-05-19 11:36:17 +01:00
Steve Sanderson
3ee16a5ba5 Update templates to .NET Core 2.0 (but not yet own package refs until they are built in CI) 2017-05-19 09:28:33 +01:00
Steve Sanderson
33cc640942 Remove obsolete 1.x APIs 2017-05-18 14:17:29 +01:00
Steve Sanderson
baae62132b Remove projects and packages obsoleted in v2 2017-05-18 13:52:58 +01:00
Steve Sanderson
58fa07d4dc Build fixes following .NET Core 2.0 update 2017-05-18 13:45:13 +01:00
Steve Sanderson
deaaa43e02 Fix SDK download URL for AppVeyor 2017-05-18 13:45:13 +01:00
Steve Sanderson
58fd9150b9 Update AppVeyor build to use .NET Core 2.0 Preview 1 2017-05-18 13:45:13 +01:00
Steve Sanderson
244deca019 Update libraries and samples to .NET Core 2.0 2017-05-18 13:45:13 +01:00
Steve Sanderson
e081d5e091 Make build.sh consistent with other ASP.NET repos 2017-05-18 13:44:15 +01:00
Steve Sanderson
85e59efa21 Rename Angular2Spa to AngularSpa, plus rephrase "Angular 2" as "Angular" in many places 2017-05-18 13:40:58 +01:00
Steve Sanderson
a74b7aea6b Bump template package versions to 0.9.3 for release 2017-05-18 12:10:15 +01:00
Mads Kristensen
178a05cc33 Added missing Framework symbol 2017-05-17 22:25:37 +01:00
Steve Sanderson
33b275a7d3 Update ReactReduxSpa to latest versions of all dependencies, and pin to exact versions 2017-05-17 22:21:37 +01:00
Steve Sanderson
c9d235d425 Update ReactSpa to latest versions of dependencies, and pin to exact versions. Simplify some instructions. 2017-05-17 22:06:32 +01:00
Steve Sanderson
48b923fcd5 Update ReactReduxSpa to React Hot Loader 3, and remove Babel dependency 2017-05-17 21:51:13 +01:00
Steve Sanderson
eeaf4e6590 Update ReactSpa to React Hot Loader 3. Remove babel dependency. 2017-05-17 21:37:47 +01:00
Steve Sanderson
ef9dbfe44b Update aspnet-webpack-react to v2.0.0, now supporting Webpack 2+ and React Hot Loader 3+ only 2017-05-17 21:10:29 +01:00
Steve Sanderson
e658ee6375 Simplifications in ReactSpa and ReactReduxSpa 2017-05-17 14:46:37 +01:00
Keven van Zuijlen
785e7d48a2 Implemented react-router v4 to ReactRedux template 2017-05-17 14:46:37 +01:00
Keven van Zuijlen
c791ceee49 Updatged package.json and webpack vendor config 2017-05-17 14:46:37 +01:00
Keven van Zuijlen
d2c56d19d0 Implemented react-router-dom v4 2017-05-17 14:46:37 +01:00
Keven van Zuijlen
7a11cf97fd Updated react-router to react-router-dom 4.0.0 2017-05-17 14:46:37 +01:00
Steve Sanderson
84aec29cf2 Bump template package versions to 0.9.2 for publish 2017-05-17 14:27:11 +01:00
Steve Sanderson
5086d0cc1c Fix test script 2017-05-17 12:25:23 +01:00
Steve Sanderson
144ad6a561 Further attempt to make Djna.nosys=true work on AppVeyor 2017-05-17 12:04:43 +01:00
Steve Sanderson
5ec31b3672 Try another way to get AppVeyor to pass through -Djna.nosys=true 2017-05-17 11:43:33 +01:00
Steve Sanderson
82dca64b8a When generating test project, do include test files 2017-05-17 11:37:33 +01:00
Steve Sanderson
45ec148dec Update AppVeyor to use .NET Core 1.0.4 SDK 2017-05-17 11:22:59 +01:00
Steve Sanderson
4e847f4545 Reference icon in vs-2017.3.host.json files 2017-05-17 11:10:29 +01:00
Steve Sanderson
ca9c6387d0 Add template icons 2017-05-17 11:10:29 +01:00
Mads Kristensen
d7a3289164 Outputs vs-2017.3.host.json file
This should now write out the correct file for VS to pick up. The icon is missing since I'm not totally sure about how we should inject that into the _.template.config_ folder. Also, is there a description of each template we can use?
2017-05-17 11:10:29 +01:00
Steve Sanderson
3c1a1ffaa4 Attempt to make selenium-standalone run on AppVeyor 2017-05-17 11:04:41 +01:00
Steve Sanderson
cd240907f8 Update tests to work with latest tooling 2017-05-17 10:43:20 +01:00
Steve Sanderson
3390d75528 Begin re-enabling tests in AppVeyor (still need to diagnose why they intermittently time out) 2017-05-17 10:18:26 +01:00
Steve Sanderson
5ade33b870 Update to Angular 4 (but leave directory name as Angular2Spa until finished) 2017-05-17 10:13:46 +01:00
Steve Sanderson
b5636ea871 Clarify contribution instructions. Fixes #934. 2017-05-16 10:56:53 +01:00
Steve Sanderson
1d57ca384f Remove vue-class-component dependency, because we're only referencing vue-property-decorator directly 2017-05-12 10:25:38 +01:00
ferrx
9d58c8836b Switched from av-ts to vue-property-decorator and vue-class-component 2017-05-12 10:20:06 +01:00
Ashley Grant
d016950e50 Add Aurelia and Vue to framework list
Doing this due to a Stack Overflow question where a dev thought this project doesn't support Aurelia simply b/c this Readme file didn't include it.  Went ahead and added Vue as well for completeness.
2017-05-11 15:06:50 +01:00
Stephan
f2260d572f Templates: update React's component's State and Property type 2017-05-05 17:37:47 +01:00
Steve Sanderson
214a908c92 Temporarily pin React to 15.4.x, because 15.5.0 has conflict with Babel (Babel injects code that tries to evaluate PropTypes) 2017-05-05 16:23:16 +01:00
Steve Sanderson
4abc39faa0 Bump generator-aspnetcore-spa to 0.9.1 for release 2017-05-04 19:53:49 +01:00
Kevin Logan
c9486eac0b Add blog link
I keep searching for this very useful MSDN blog link.
2017-05-04 19:54:07 +01:00
Steve Sanderson
6fa6765fa5 Update README.md 2017-05-04 19:52:30 +01:00
Rick Anderson
c570d79dcc removed cache priming 2017-05-04 19:52:08 +01:00
Rick Anderson
8f3e9308cc Update README.md 2017-05-04 19:52:08 +01:00
Rick Anderson
76c7db42b9 Update README.md 2017-05-04 19:52:08 +01:00
Steve Sanderson
5a5b309cdf Bump aspnet-prerendering to 2.0.5 for release 2017-05-04 18:20:38 +01:00
Steve Sanderson
d41f47f5bc Make statusCode optional on RenderToStringResult. Fixes #917 and #918 2017-05-04 18:20:25 +01:00
Steve Sanderson
e48ee287c2 Change .NET package versions since subsequent builds won't be RTM releases 2017-05-04 14:24:59 +01:00
Steve Sanderson
15ceec0b3f Have templates declare dependency on SpaServices 1.1.1 2017-05-04 14:15:16 +01:00
Steve Sanderson
decb13b33b Make versions consistent across all .NET projects 2017-05-04 14:07:00 +01:00
Steve Sanderson
f18e0be128 Fix VersionSuffix in build 2017-05-04 14:03:00 +01:00
Steve Sanderson
6300f6cdcd Have CI build 1.1.1 RTM versions of NodeServices/SpaServices for release 2017-05-04 13:37:56 +01:00
Steve Sanderson
3dca3c37ff Bump aspnet-webpack to 1.0.29 for publish 2017-05-04 13:33:25 +01:00
Steve Sanderson
1862e388e1 Bump aspnet-prerendering to 2.0.4 for publish 2017-05-04 13:21:35 +01:00
Steve Sanderson
cbaeb1c055 Fix ReactReduxSpa build issue. Fixes #859 and #878 2017-05-04 13:17:24 +01:00
Matt Perry
cdbbbcc473 Fix build break 2017-05-04 13:07:18 +01:00
Matt Perry
02b31d05a2 Stick closer to established style guides. Up buffer size to match websocket buffer size from original proxy code. 2017-05-04 13:07:18 +01:00
Matt Perry
54884459bd Allow proxy middleware to abort long running connections. 2017-05-04 13:07:18 +01:00
Steve Sanderson
018a3e65ff Put back "name" in AureliaSpa package.json because this is auto-replaced during template evaluation 2017-05-04 12:41:30 +01:00
Steve Sanderson
e3a8c13c22 Make Aurelia webpack config as consistent as possible with the other templates 2017-05-04 12:36:25 +01:00
Meirion Hughes
37df30929c Update Aurelia SPA template 2017-04-13 20:54:56 +01:00
Erick Galassi
ea429cccf4 Minimize CSS in production builds (all templates) 2017-04-11 14:51:31 +01:00
Pavlo Glazkov
c0205cfc4e Fix watchOptions in webpack.config.js being ignored by WebpackDevMiddleware (#806)
This enables Docker HMR workflow and the workaround mentioned in this comment: https://github.com/aspnet/JavaScriptServices/issues/806#issuecomment-290081291
2017-04-11 14:35:57 +01:00
Ben Adams
b28f85236f Console type needs exe 2017-04-11 14:31:28 +01:00
Pranav K
e1533c2203 Use 2.0.0 version of Internal.AspNetCore.Sdk 2017-04-07 12:03:14 -07:00
Steve Sanderson
5dc476182c Reference bootstrap JS in KnockoutSpa/ReactSpa/VueSpa. Fixes #846 2017-04-07 15:48:20 +01:00
Steve Sanderson
5cb1846dd2 Fix and reorganise WebpackDevMiddleware.ts following PR 2017-03-29 14:18:37 +01:00
Steve Sanderson
1f03b1e633 Tiny shortcut to reduce string checking in non-debug scenarios 2017-03-29 12:02:02 +01:00
Steve Sanderson
8544714cbb Revert IsWarning code path to avoid breaking change (e.g., if someone overrode OnErrorDataReceived to receive such lines) 2017-03-29 12:02:02 +01:00
Thomas Hermann
0c09c68b77 Support V8 Inspector Integration for Node.js
Addresses #796
2017-03-29 12:02:02 +01:00
Steve Sanderson
ee601bed50 Minor doc tweaks 2017-03-29 11:41:38 +01:00
Thomas Hermann
ce40973859 Add option to configure Webpack Hot Middleware client
Addresses #667
2017-03-29 11:35:57 +01:00
peterlazzarino
347524a116 Add status code response capabilities to PrerenderTagHelper and RenderToStringResult. Client can now send status codes back based on client routes. 2017-03-29 11:32:37 +01:00
Steve Sanderson
b9c387bf5f Remove obsolete references from gitignore files. Fixes #818 and #819. 2017-03-29 10:33:29 +01:00
Steve Sanderson
6ed6b8e3d2 Set AutoCompleteOnClose=false on JsonTextWriter. Fixes #822 2017-03-29 10:27:50 +01:00
Kiran Challa
dcf51f706c Upgraded Json.NET version to 10.0.1 2017-03-28 10:43:05 -07:00
Dan Harman
365b2c50cb Added babel-loader caching to improve subsequent build times. (#777) 2017-03-22 10:28:26 +00:00
Steve Sanderson
efcfc0b9a6 Bump template packages to 0.9.0 for release 2017-03-15 17:04:55 -07:00
Steve Sanderson
68e27d6091 In Yeoman generator, assert dotnet is >= 1.0.0, and set detected version in global.json 2017-03-15 17:04:20 -07:00
Steve Sanderson
08ea344fcf Remove project.json-specific content from all the SPA templates 2017-03-14 17:23:08 -07:00
Steve Sanderson
a6e0955581 In Yeoman/dotnetnew generators, remove support for producing project.json projects 2017-03-14 17:09:33 -07:00
Steve Sanderson
d8143e2603 Update Yeoman package README 2017-03-14 17:07:42 -07:00
Mark Thiessen
3077b8a8c0 Modifies babel config to allow for webpack tree shaking 2017-03-13 11:20:05 -07:00
Steve Sanderson
5866713a05 Remove style-loader from vendor bundles. Fixes #715. 2017-03-13 11:11:38 -07:00
Steve Sanderson
7aacf2151c Bump templates packages versions to 0.8.7 for release 2017-03-13 09:55:48 -07:00
Steve Sanderson
e4623bb186 Add Vue template to Yeoman generator 2017-03-13 09:32:02 -07:00
Steve Sanderson
c6a089880c Add VueSpa.csproj to .sln 2017-03-13 09:29:29 -07:00
Steve Sanderson
59e09816c7 Fix typos (etc) in description on homepage 2017-03-13 09:25:23 -07:00
Steve Sanderson
c3ad9e8c2f Remove Vue template server-side prerendering because of limitations 2017-03-13 09:25:23 -07:00
Steve Sanderson
119b274c19 Add server-side rendering (via bundleRenderer, as this is what the Vue docs recommend, and apparently the only way it does encapsulation) 2017-03-13 09:25:23 -07:00
Steve Sanderson
360688f78b Add Vue template 2017-03-13 09:25:23 -07:00
Maciej Lelito
0a1ac6a70a Added missing import in boot.ts in Aurelia template 2017-03-10 13:58:32 +00:00
Kyle Summers
8d27d9d583 Updates Aurelia project url (#741)
aurealia.io doesn't appear to support https currently
2017-03-10 13:36:37 +00:00
David Fowler
8bba142e8f Merge pull request #751 from aspnet/no-mono
No mono
2017-03-10 00:58:33 -08:00
David Fowler
94d47a3917 No mono 2017-03-10 00:15:39 -08:00
Nate McMaster
13fb00a71e Update KoreBuild to dev 2017-03-01 18:44:16 -08:00
Nate McMaster
684a77430c Add CI feed to NuGet.config 2017-03-01 09:10:37 -08:00
Nate McMaster
3058c050bb Unify dependency versions across all non-template projects 2017-03-01 09:28:11 +00:00
Nate McMaster
821ad85e41 Fixup MSBuild conversion (#721)
* Replace makefile.shade with repo.targets

* Remove web site projects as these are not yet supported by MSBuild for .NET Core

cref https://github.com/Microsoft/msbuild/issues/1767
2017-03-01 09:17:49 +00:00
Steve Sanderson
76ae9aa58f Fix build error in aspnet-prerendering caused by TypeScript 2.2.1 breaking change 2017-02-28 09:19:39 +00:00
Steve Sanderson
a79bc75671 Migrate to csproj (#703)
Migrate to csproj
2017-02-28 09:17:35 +00:00
Steve Sanderson
795aac241e Bump templates package versions to 0.8.6 for release 2017-02-23 10:23:34 +00:00
Steve Sanderson
a38db81b71 Templates that were waiting for awesome-typescript-loader 3.0.0 to ship no longer need to wait - it has shipped 2017-02-23 10:14:37 +00:00
Steve Sanderson
689f106181 Update templates to TypeScript 2.2.1 and fix whatwg-fetch related errors. Fixes #705 2017-02-23 10:07:03 +00:00
Steve Sanderson
627ea78e72 Replace image resizing sample with chartist sample 2017-02-22 13:27:18 +00:00
Steve Sanderson
3162946139 In ReactReduxSpa, configure store before matching routes. Fixes #663 2017-02-21 16:56:35 +00:00
Steve Sanderson
c9526dd4d2 Remove Dockerfiles for consistency with other templates. Also fixes #694 2017-02-21 16:18:25 +00:00
SteveSandersonMS
3c2fd50e8f Add fields to dotnet new NuSpec file 2017-02-13 16:38:56 +00:00
SteveSandersonMS
04111db230 Add "dotnet new" nupkg as AppVeyor artifact 2017-02-13 15:48:19 +00:00
SteveSandersonMS
5a593d657d Bump generator-aspnetcore-spa to 0.8.5 2017-02-13 15:44:49 +00:00
SteveSandersonMS
5a67ca35be Add 'primaryOutputs' to dotnet new template.json files (and reorder JSON properties alphabetically) 2017-02-13 15:28:12 +00:00
SteveSandersonMS
72dabb3664 After building "dotnet new" package, put it in "artifacts" dir 2017-02-13 15:06:41 +00:00
SteveSandersonMS
b7314359c8 When building "dotnet new" templates, always prebuild and include client-side 'dist' files for all templates, because dotnet new can't run arbitrary post-generation steps 2017-02-13 14:59:43 +00:00
SteveSandersonMS
1bf283877e More updates to "dotnet new" template to produce correct template.json values 2017-02-13 14:42:47 +00:00
SteveSandersonMS
1115a0aff5 Fix package.json files in Angular2Spa and AureliaSpa to have replaceable name (not hardcoded as Angular2Spa, etc.). Other templates were already set up like this. 2017-02-13 14:38:44 +00:00
SteveSandersonMS
70ad900f59 Further update dotnet new template builder to produce correct template.son contents 2017-02-13 14:05:36 +00:00
SteveSandersonMS
fadc5d7b98 Update dotnet new template builder to match tooling preview 4 file structure 2017-02-13 13:12:44 +00:00
SteveSandersonMS
85dcc66723 Update the version of NuGet.exe used when building "dotnet new" templates to v4 (rc) 2017-02-13 13:07:24 +00:00
SteveSandersonMS
f0825bec1d In generator-aspnetcore-spa, change internal name of Angular template from "angular-2" to "angular" to comply with suggested Angular branding 2017-02-13 13:05:59 +00:00
SteveSandersonMS
0ec6178617 Remove the Angular 2.0.2 template; define the 2.4.5+ template as the standard Angular one. Makes issue #665 no longer apply. 2017-02-13 12:55:48 +00:00
SteveSandersonMS
0f3e3ebb3b Fix TypeScript build error with updated lodash type definitions 2017-02-13 12:50:25 +00:00
SteveSandersonMS
9017ef6fdc Fix issues in KnockoutSpa/ReactSpa webpack 2 configs. Bump generator version to 0.8.4. 2017-02-09 18:10:58 +00:00
SteveSandersonMS
520ef02b98 Bump generator-aspnetcore-spa version to 0.8.3 for release 2017-02-09 15:49:39 +00:00
SteveSandersonMS
481ca0a8e0 Replace 'loader' with 'use' in various Webpack configs. Fixes 'loader option has been deprecated' warnings. 2017-02-09 15:43:41 +00:00
SteveSandersonMS
28483e2cf3 In ReactReduxSpa, pin @types/react-router-redux to known-good version 4.0.30, because 4.0.40 is broken. Fixes #651 2017-02-09 15:35:11 +00:00
SteveSandersonMS
e8dd8089d4 In Angular2Spa, fix HMR with 2.4.6 and relax version dependency to allow arbitrary 2.x again. Fixes #643 2017-02-09 15:14:30 +00:00
SteveSandersonMS
4f7091c5d5 Remove "Connection: keep-alive" workaround from domain-task, as it's not needed with .NET Core >= 1.1.0. Fixes #655 2017-02-09 14:28:18 +00:00
SteveSandersonMS
66148dacf3 Move angular2-template-loader dependency version to ^0.6.2 now that the bug in 0.6.1 is fixed. Fixes #649. 2017-02-09 10:12:46 +00:00
SteveSandersonMS
8ca45f0d77 Bump generator-aspnetcore-spa to 0.8.2 for release 2017-02-07 17:57:21 +00:00
SteveSandersonMS
0559bbb9af Temporarily pin to Angular 2.4.5 and angular2-template-loader 0.6.0 because of bugs in their patch releases (#643, #649) 2017-02-07 17:52:37 +00:00
SteveSandersonMS
7b28d05373 Pin AureliaSpa to extract-text-webpack-plugin 2.0.0-beta.4 until it can move to Webpack 2.2.0 final. 2017-02-03 11:50:05 +00:00
SteveSandersonMS
87b9db7579 Add yarn.lock to ignorefiles in aspnet-prerendering 2017-02-03 10:54:00 +00:00
SteveSandersonMS
61ffca6290 In aspnet-prerendering, parse the incoming querystring before passing it to the boot func for convenience. Fixes #638. 2017-02-03 10:54:00 +00:00
Mark Pieszak
a9e97f6783 fix(ng-router): update to 3.4.5 for experimental version
When everything is 4.x they'll all be on the same page / semantic versioning :)
2017-02-03 10:00:52 +00:00
SteveSandersonMS
f8f4016b33 Minor tweak in Yeoman generator source to clarify intent (makes no difference in current cases, so no need to re-release the package for this) 2017-01-31 16:09:36 +00:00
SteveSandersonMS
59dbb1f88f Bump generator-aspnetcore-spa version to 0.8.1 for release 2017-01-31 15:26:27 +00:00
SteveSandersonMS
ac75a3136d In generator-aspnetcore-spa, add option to produce Angular 2.4.5 project (with 'experimental' caveat) 2017-01-31 15:26:12 +00:00
SteveSandersonMS
a3af5c0b25 In Angular2Spa, reference angular2-universal-patch. This will allow for upgrades to Angular 2.4.5. 2017-01-31 14:19:14 +00:00
SteveSandersonMS
847430a0d9 Update Karma config to allow for Webpack 2-style config file. Fixes #625. 2017-01-31 11:49:21 +00:00
SteveSandersonMS
55c3156df2 Bump generator-aspnetcore-spa version to 0.8.0 for release 2017-01-30 13:02:16 +00:00
SteveSandersonMS
e7785dff01 Update templates to match VS2017RC build 26127 project file format, plus latest ASP.NET Core 'Program.cs' convention. Fixes #620. 2017-01-30 13:02:16 +00:00
SteveSandersonMS
6ed34ddd33 Update KnockoutSpa to Webpack 2 (plus awesome-typescript-loader) 2017-01-30 11:21:12 +00:00
SteveSandersonMS
9f7bc75960 Update ReactReduxSpa to Webpack 2 (plus awesome-typescript-loader) 2017-01-30 11:21:12 +00:00
SteveSandersonMS
bdd7cfd38b Update ReactSpa to Webpack 2 (plus awesome-typescript-loader) 2017-01-30 11:21:12 +00:00
SteveSandersonMS
be3fb6e876 Update Angular2Spa to Webpack 2 (plus awesome-typescript-loader for improved build speed) 2017-01-30 11:21:12 +00:00
SteveSandersonMS
d69b65e572 Bump NodeServices/SpaServices package versions to 1.1.0-rtm for release 2017-01-30 09:57:00 +00:00
SteveSandersonMS
e5178d2938 Update peerDependency on aspnet-webpack-react to allow Webpack 2.2.0+ as well as 1.x 2017-01-27 15:32:19 +00:00
SteveSandersonMS
e4d00a2da3 Update aspnet-webpack to support Webpack 2-style configs that export a function 2017-01-27 14:44:25 +00:00
SteveSandersonMS
345b4f64b5 Update aspnet-webpack-react to support Webpack 2.x-style configs 2017-01-27 11:18:26 +00:00
SteveSandersonMS
a9bc1201d1 Fix the build by passing allowConnectionDraining from SocketNodeInstance 2017-01-20 18:09:57 +00:00
SteveSandersonMS
6a1f35da8f Bumping NodeServices/SpaServices versions to 1.1.0-rc2-* for RC2 release 2017-01-20 17:33:34 +00:00
SteveSandersonMS
d7d1a04751 When Node is launched with a debug listener, disable connection draining on restart. Fixes #506. 2017-01-20 17:32:26 +00:00
SteveSandersonMS
351fe3d15c In SpaServices, add new HotModuleReplacementEndpoint option on HMR config. Fixes #579. 2017-01-20 15:03:52 +00:00
SteveSandersonMS
3313a7f0a6 Update Node version to 6.x (current LTS) in Dockerfiles. Fixes #591. 2017-01-20 12:38:57 +00:00
SteveSandersonMS
867d070263 Remove unused references to ThunkAction. Fixes a build error since @types/redux-thunk 2.2.0 removes the ThunkAction export. 2017-01-18 19:14:17 +00:00
SteveSandersonMS
a046bd2021 Temporarily disable tests on AppVeyor until config can be updated to match new chromedriver version requirement 2017-01-18 18:52:21 +00:00
SteveSandersonMS
1a49a27344 Use correct docker base image for csproj projects. Fixes #574. 2017-01-16 15:25:41 +00:00
SteveSandersonMS
f8714e9653 Fix react/redux TypeScript build errors caused by breaking changes in dependencies. Fixes #529, #582, #583 2017-01-16 10:46:28 +00:00
SteveSandersonMS
9d5006107f Bumping NodeServices/SpaServices versions to 1.1.0-rc1-* for RC1 release 2017-01-13 09:02:28 +00:00
SteveSandersonMS
c3316d0caa Hopefully fix BuildQuality setting in build 2017-01-10 19:43:15 +00:00
SteveSandersonMS
38441c6598 Override the build quality determined by KoreBuild 2017-01-10 19:13:15 +00:00
SteveSandersonMS
f8f42a53b2 Increase Mocha timeout. Trying to fix intermittent test failures on AppVeyor. 2017-01-10 10:37:00 +00:00
SteveSandersonMS
a93f59c660 Add Aurelia to generator's README.md. Fixes #561. 2017-01-09 14:43:28 +00:00
SteveSandersonMS
c377e6cd28 For project.json-based projects, fix Kudu deployment by adding a .deployment file at the root 2017-01-07 00:08:06 +00:00
SteveSandersonMS
2a1435d9c1 Bump generator-aspnetcore-spa version to 0.7.3 2017-01-07 00:04:50 +00:00
SteveSandersonMS
6789dbc102 csproj-based projects don't need the _placeholder.txt files, because the publishing code correctly includes dist files even if they were not on disk before 2017-01-07 00:04:20 +00:00
SteveSandersonMS
59526ff16a Update templates' csproj files to fix first-time publishing (previously, it didn't deploy dist files if they were not already on disk) 2017-01-07 00:00:52 +00:00
SteveSandersonMS
8c456aa0fc Make Karma serve .ts files with executable MIME type, otherwise newer versions of Chrome won't run it. Fixes #499 2017-01-03 17:35:30 +00:00
Niclas Emdelius
49bece54c2 Check if response is stream or object without using reference to reflection package. 2017-01-03 13:29:45 +00:00
nicemd
0907379da8 Streamed responses can be be Object or Stream
See issue https://github.com/aspnet/JavaScriptServices/issues/518
2017-01-03 13:29:45 +00:00
SteveSandersonMS
fbb8054c2e Make error message clearer if Node isn't installed (or not found on PATH). Fixes #527 2017-01-03 10:59:26 +00:00
SteveSandersonMS
70f59fc8ec Update core package versions to 1.1.0-beta2-* so that CI will produce non-timestamped packages 2017-01-03 10:29:22 +00:00
SteveSandersonMS
e6cfe93e13 Instruct AppVeyor to regard the generator-aspnetcore-spa.tar.gz file as a build artifact 2017-01-02 12:02:58 +00:00
SteveSandersonMS
dad095b749 Bump generator-aspnetcore-spa version to 0.7.2 2017-01-02 11:59:51 +00:00
SteveSandersonMS
f31b96fe1e Yeoman package builder emits .tar.gz artifact 2017-01-02 11:59:16 +00:00
SteveSandersonMS
312885acde Add AppVeyor status to root README.md 2017-01-02 11:31:40 +00:00
SteveSandersonMS
0763f1062b Add missing trailing slash on publicPath in docs. Fixes #516. 2017-01-02 10:58:03 +00:00
SteveSandersonMS
dcb819f644 Fix port testing logic in tests by being explicit about which interface we're checking 2016-12-15 22:53:12 +00:00
SteveSandersonMS
f722dcf98d Fix PrerenderResult in Webpack sample 2016-12-15 21:45:02 +00:00
SteveSandersonMS
153c9848ca Also build samples/misc 2016-12-15 21:42:40 +00:00
SteveSandersonMS
aefa578e04 Reuse TEST_PROJECT_GLOB to build templates, since the 'test' dir doesn't contain any .NET projects 2016-12-15 21:09:44 +00:00
SteveSandersonMS
953f370336 Wait for port to be freed before continuing 2016-12-15 20:34:55 +00:00
SteveSandersonMS
200d80627c Update Appveyor test environment to Node 6.9.2 to deal with Windows-specific NPM issues 2016-12-15 20:34:46 +00:00
SteveSandersonMS
994653a277 Stop including templates in regular build (they now contain .csproj, which is not supported by that build system, and in any case they are built and end-to-end tested on Appveyor) 2016-12-15 20:32:08 +00:00
SteveSandersonMS
17b107bb42 Fix how 'current' and 'preview' SDKs are merged in Appveyor builds 2016-12-15 19:31:09 +00:00
SteveSandersonMS
67cd5f6093 In csproj projects, reference 1.1.0-preview4-final version of Razor tools 2016-12-15 18:10:43 +00:00
SteveSandersonMS
06ccad4344 Automatically install both the 'current' and 'preview' SDKs when building in Appveyor 2016-12-15 18:07:34 +00:00
SteveSandersonMS
4fd19b4293 Run tests against both csproj and project.json-style projects. Assumes relevant dotnet SDKs are installed locally. 2016-12-15 17:17:04 +00:00
SteveSandersonMS
f34eb582ad Update tests to be compatible with newer Yeoman generator 2016-12-15 16:52:02 +00:00
SteveSandersonMS
4580412f73 Don't leave behind an unused package.json.tmp file 2016-12-15 16:42:25 +00:00
SteveSandersonMS
a76b6e25d4 Add .csproj files for the templates. Some are set up for prerendering; others not. 2016-12-15 16:42:25 +00:00
SteveSandersonMS
2135d77189 generator-aspnetcore-spa support for selecting between project.json and .csproj 2016-12-15 16:42:25 +00:00
SteveSandersonMS
62b676174e generator-aspnetcore-spa now displays its own version number on startup 2016-12-15 16:41:10 +00:00
SteveSandersonMS
67c2cfd84e Add Appveyor builds and webdriver.io tests (tests cover Angular2Spa template only at present) 2016-12-15 16:38:30 +00:00
SteveSandersonMS
6decb30681 Make 'counter' components use <h1> headers to be consistent with other pages 2016-12-15 16:37:29 +00:00
SteveSandersonMS
33ed333f92 Specify SDK at repo root 2016-12-15 16:35:48 +00:00
SteveSandersonMS
6c16ba3da7 Remove template_nodemodules_placeholder.txt files - they are redundant now that we no longer need to publish node_modules to production 2016-12-15 16:35:48 +00:00
SteveSandersonMS
495c95d227 If generator fails because NPM is too old, exit with nonzero status code 2016-12-15 16:35:48 +00:00
SteveSandersonMS
4dd91a9b78 Make template package build script automate running the prepublish steps needed for the Angular2Spa template 2016-12-14 10:21:04 +00:00
SteveSandersonMS
06335eeee1 Fix building generator-aspnetcore-spa (something in a newer TypeScript version broke it) 2016-12-14 09:56:23 +00:00
SteveSandersonMS
eb98067e86 Update docs about using socket transport. Fixes #500 2016-12-13 15:59:43 +00:00
SteveSandersonMS
a25d1f4731 Publish aspnet-webpack 1.0.26 2016-12-13 15:48:44 +00:00
SteveSandersonMS
4c3834361e Stop WebpackDevMiddleware littering the 'dist' directories with blah.host-update.js.map files (they never need to be written to disk) 2016-12-13 15:47:43 +00:00
SteveSandersonMS
4cc61d79e2 Add json-loader to add webpack.config.js files. Helps with #507. 2016-12-13 12:57:06 +00:00
SteveSandersonMS
1c4682e50d Fix all the ConditionalProxyMiddleware errors that happened if you ctrl+c on a "dotnet run" (not "dotnet watch run") since beta-000002. 2016-12-13 11:32:32 +00:00
Sébastien Ros
6545e11bf2 Fix filename in sample
Mismatch between code sample and described js filename.
2016-12-08 10:04:50 +00:00
SteveSandersonMS
8d1cb3a537 Remove angular2-aspnet NPM package source as none of its functionality is still in use (and hasn't been since beta versions of Angular 2) 2016-12-08 10:02:37 +00:00
SteveSandersonMS
8705a4b353 Remove redux-typed from source tree. It's no longer in use and doesn't need to be maintained. Source can be re-added in the future if needed. 2016-12-07 19:02:53 +00:00
SteveSandersonMS
7c685c1354 Add AssemblyInfo.cs to Microsoft.AspNetCore.NodeServices.Sockets package 2016-12-07 18:09:11 +00:00
SteveSandersonMS
633969c7b4 Update Dockerfiles for .NET Core 1.1.0. Fixes #489 2016-12-07 11:15:40 +00:00
SteveSandersonMS
9c1c1b4023 Publish aspnet-prerendering 2.0.2 2016-12-01 17:31:32 +00:00
SteveSandersonMS
0a116ba2a1 Prerendering logic supplies PathBase (formatted as baseUrl) to boot logic 2016-12-01 17:24:24 +00:00
SteveSandersonMS
2b2465ad2e Update prerendering docs to account for aspnet-prerendering 2.0.0 and the new createServerRenderer API. Fixes #479 2016-12-01 14:41:48 +00:00
SteveSandersonMS
dc130adc91 Update aspnet-prerendering to reference baseUrl in domain-task's main module, not domain-task/fetch. Goal is to avoid Webpack having to bundle isomorphic-fetch on server unless you're actually using it. 2016-12-01 14:32:54 +00:00
SteveSandersonMS
b8913d29dd Update domain-task to export baseUrl from 'main' instead of only 'fetch'. Goal is to avoid Webpack detecting dependency on isomorphic-fetch unless you're actually using it. 2016-12-01 14:31:27 +00:00
SteveSandersonMS
5e669d6e7a Publish Yeoman generators 0.7.1 2016-11-30 17:54:46 +00:00
SteveSandersonMS
5d02728159 Publish Yeoman generators version 0.7.0. From here on, the templates produce ASP.NET Core 1.1.0 projects. 2016-11-30 17:09:24 +00:00
SteveSandersonMS
78632617f8 Publish aspnet-webpack 1.0.25 2016-11-30 16:53:39 +00:00
SteveSandersonMS
e2f8031bb8 For HMR, proxy all requests including /__webpack_hmr. Fixes #271. 2016-11-30 16:53:16 +00:00
SteveSandersonMS
2cffab14f6 StringAsTempFile cleans up in a wider range of circumstances (not relying on finalizer running). Helps with #7 but still doesn't cover all cases. 2016-11-30 15:04:08 +00:00
SteveSandersonMS
9001c191c1 Make Angular and React templates reference SpaServices directly. The AngularServices/ReactServices packages might be used in the future, but don't presently contain any extra usable functionality. 2016-11-30 12:19:05 +00:00
SteveSandersonMS
832da2a451 Split out 'socket' hosting model into a separate optional NuGet package, since most developers won't need it 2016-11-30 12:18:57 +00:00
SteveSandersonMS
ebf5a18344 Eliminate the NodeHostingModel enum because it isn't extensible. Instead, use extension methods on NodeServicesOptions to configure a NodeInstanceFactory. 2016-11-30 11:29:57 +00:00
SteveSandersonMS
d865e1f28b Specify latest 'current' SDK in all template global.json files 2016-11-30 11:29:57 +00:00
SteveSandersonMS
62c0680193 Make 'fetchdata' components in templates compatible with latest TypeScript compiler 2016-11-29 19:11:03 +00:00
SteveSandersonMS
a8d3eed32c Detect legacy aspnet-prerendering mode earlier to fix #470 2016-11-29 18:37:43 +00:00
SteveSandersonMS
5d14f11b1b Update all the project templates to .NET Core 1.1.0 2016-11-29 16:36:05 +00:00
SteveSandersonMS
196c0dd32e Update other samples to 1.1.0 dependencies 2016-11-29 16:36:05 +00:00
SteveSandersonMS
31d8eb69a7 Updated all 'misc' samples to 1.1.0 dependencies (and to TypeScript 2 for the Webpack sample) 2016-11-29 16:36:05 +00:00
SteveSandersonMS
4cbbf58600 Update core packages to version 1.1.0-*, upgrading .NET Core dependencies to match 1.1.0 release 2016-11-29 16:35:59 +00:00
545 changed files with 4231 additions and 14415 deletions

3
.gitignore vendored
View File

@@ -13,7 +13,6 @@ PublishProfiles/
*.docstates *.docstates
_ReSharper.* _ReSharper.*
nuget.exe nuget.exe
project.lock.json
*net45.csproj *net45.csproj
*net451.csproj *net451.csproj
*k10.csproj *k10.csproj
@@ -39,3 +38,5 @@ npm-debug.log
/templates/*/ClientApp/dist/ /templates/*/ClientApp/dist/
/templates/*/yarn.lock /templates/*/yarn.lock
.vscode/ .vscode/
/templates/*/Properties/launchSettings.json

View File

@@ -10,11 +10,10 @@ addons:
- libssl-dev - libssl-dev
- libunwind8 - libunwind8
- zlib1g - zlib1g
mono: mono: none
- 4.0.5
os: os:
- linux - linux
- osx - osx
osx_image: xcode7.1 osx_image: xcode7.1
script: script:
- ./build.sh verify - ./build.sh

View File

@@ -1,54 +1,40 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 15
VisualStudioVersion = 14.0.25420.1 VisualStudioVersion = 15.0.26430.4
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{E6E88944-4800-40BA-8AF5-069EA3ADFEB8}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{27304DDE-AFB2-4F8B-B765-E3E2F11E886C}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.NodeServices", "src\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.xproj", "{B0FA4175-8B29-4904-9780-28B3C24B0567}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.NodeServices", "src\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj", "{66B77203-1469-41DF-92F2-2BE6900BD36F}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NodeServicesExamples", "samples\misc\NodeServicesExamples\NodeServicesExamples.xproj", "{6D4BCDD6-7951-449B-BE55-CB7F014B7430}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.NodeServices.Sockets", "src\Microsoft.AspNetCore.NodeServices.Sockets\Microsoft.AspNetCore.NodeServices.Sockets.csproj", "{F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{78DAC76C-1092-45AB-BF0D-594B8C7B6569}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices", "src\Microsoft.AspNetCore.SpaServices\Microsoft.AspNetCore.SpaServices.csproj", "{66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{99EAF1FE-22C8-4526-BE78-74B24125D37F}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
global.json = global.json global.json = global.json
README.md = README.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore", "samples\angular\MusicStore\MusicStore.xproj", "{1A74148F-9DC0-435D-B5AC-7D1B0D3D5E0B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{23836492-E7F4-4376-85BF-A635C304AC46}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ReactGrid", "samples\react\ReactGrid\ReactGrid.xproj", "{ABF90A5B-F4E0-438C-A6E4-9549FB43690B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{E6A161EA-646C-4033-9090-95BE809AB8D9}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.AngularServices", "src\Microsoft.AspNetCore.AngularServices\Microsoft.AspNetCore.AngularServices.xproj", "{421807E6-B62C-417B-B901-46C5DEDAA8F1}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LatencyTest", "samples\misc\LatencyTest\LatencyTest.csproj", "{1931B19A-EC42-4D56-B2D0-FB06D17244DA}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.ReactServices", "src\Microsoft.AspNetCore.ReactServices\Microsoft.AspNetCore.ReactServices.xproj", "{B04381DE-991F-4831-A0B5-FE1BD3EF80C4}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Webpack", "samples\misc\Webpack\Webpack.csproj", "{DE479DC3-1461-4EAD-A188-4AF7AA4AE344}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.SpaServices", "src\Microsoft.AspNetCore.SpaServices\Microsoft.AspNetCore.SpaServices.xproj", "{4624F728-6DFF-44B6-93B5-3C7D9C94BF3F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodeServicesExamples", "samples\misc\NodeServicesExamples\NodeServicesExamples.csproj", "{93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Templates", "Templates", "{727E6D58-6830-4792-96C6-E138A33959FB}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{1598B415-73F1-4B37-B3B4-0A10677ABB2D}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Angular2Spa", "templates\Angular2Spa\Angular2Spa.xproj", "{8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{E415FE14-13B0-469F-836D-95059E6BAA6E}"
EndProject ProjectSection(SolutionItems) = preProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ReactReduxSpa", "templates\ReactReduxSpa\ReactReduxSpa.xproj", "{DBFC6DB0-A6D1-4694-A108-1C604B988DA3}" src\build\common.props = src\build\common.props
EndProject src\build\Key.snk = src\build\Key.snk
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ReactSpa", "templates\ReactSpa\ReactSpa.xproj", "{E9D1A695-F0E6-46F2-B5E3-72F4AF805387}" EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WebApplicationBasic", "templates\WebApplicationBasic\WebApplicationBasic.xproj", "{CB4398D6-B7F1-449A-AE02-828769679232}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{E0771531-BE20-40CD-A1B0-A57E09511060}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Webpack", "samples\misc\Webpack\Webpack.xproj", "{A8905301-8492-42FD-9E83-F715A0FDC3A2}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LatencyTest", "samples\misc\LatencyTest\LatencyTest.xproj", "{A64AF9D9-72AA-4433-BE1D-DC2524B6808A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "React", "React", "{E0EBA813-4478-4C02-B11D-FB3793113FE4}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore", "samples\react\MusicStore\MusicStore.xproj", "{C870A92C-9E3F-4BF2-82B8-5758545A8B7C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Angular", "Angular", "{4867A616-83D6-48DC-964D-6AE743596631}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Aurelia", "templates\AureliaSpa\Aurelia.xproj", "{33D8DAD9-74F9-471D-8BAD-55F828FAA736}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "KnockoutSpa", "templates\KnockoutSpa\KnockoutSpa.xproj", "{85231B41-6998-49AE-ABD2-5124C83DBEF2}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -56,89 +42,41 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B0FA4175-8B29-4904-9780-28B3C24B0567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66B77203-1469-41DF-92F2-2BE6900BD36F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0FA4175-8B29-4904-9780-28B3C24B0567}.Debug|Any CPU.Build.0 = Debug|Any CPU {66B77203-1469-41DF-92F2-2BE6900BD36F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0FA4175-8B29-4904-9780-28B3C24B0567}.Release|Any CPU.ActiveCfg = Release|Any CPU {66B77203-1469-41DF-92F2-2BE6900BD36F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0FA4175-8B29-4904-9780-28B3C24B0567}.Release|Any CPU.Build.0 = Release|Any CPU {66B77203-1469-41DF-92F2-2BE6900BD36F}.Release|Any CPU.Build.0 = Release|Any CPU
{6D4BCDD6-7951-449B-BE55-CB7F014B7430}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D4BCDD6-7951-449B-BE55-CB7F014B7430}.Debug|Any CPU.Build.0 = Debug|Any CPU {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D4BCDD6-7951-449B-BE55-CB7F014B7430}.Release|Any CPU.ActiveCfg = Release|Any CPU {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D4BCDD6-7951-449B-BE55-CB7F014B7430}.Release|Any CPU.Build.0 = Release|Any CPU {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3}.Release|Any CPU.Build.0 = Release|Any CPU
{1A74148F-9DC0-435D-B5AC-7D1B0D3D5E0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A74148F-9DC0-435D-B5AC-7D1B0D3D5E0B}.Debug|Any CPU.Build.0 = Debug|Any CPU {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A74148F-9DC0-435D-B5AC-7D1B0D3D5E0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A74148F-9DC0-435D-B5AC-7D1B0D3D5E0B}.Release|Any CPU.Build.0 = Release|Any CPU {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC}.Release|Any CPU.Build.0 = Release|Any CPU
{ABF90A5B-F4E0-438C-A6E4-9549FB43690B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABF90A5B-F4E0-438C-A6E4-9549FB43690B}.Debug|Any CPU.Build.0 = Debug|Any CPU {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABF90A5B-F4E0-438C-A6E4-9549FB43690B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABF90A5B-F4E0-438C-A6E4-9549FB43690B}.Release|Any CPU.Build.0 = Release|Any CPU {1931B19A-EC42-4D56-B2D0-FB06D17244DA}.Release|Any CPU.Build.0 = Release|Any CPU
{421807E6-B62C-417B-B901-46C5DEDAA8F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{421807E6-B62C-417B-B901-46C5DEDAA8F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Debug|Any CPU.Build.0 = Debug|Any CPU
{421807E6-B62C-417B-B901-46C5DEDAA8F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Release|Any CPU.ActiveCfg = Release|Any CPU
{421807E6-B62C-417B-B901-46C5DEDAA8F1}.Release|Any CPU.Build.0 = Release|Any CPU {DE479DC3-1461-4EAD-A188-4AF7AA4AE344}.Release|Any CPU.Build.0 = Release|Any CPU
{B04381DE-991F-4831-A0B5-FE1BD3EF80C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B04381DE-991F-4831-A0B5-FE1BD3EF80C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B04381DE-991F-4831-A0B5-FE1BD3EF80C4}.Release|Any CPU.ActiveCfg = Release|Any CPU {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B04381DE-991F-4831-A0B5-FE1BD3EF80C4}.Release|Any CPU.Build.0 = Release|Any CPU {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE}.Release|Any CPU.Build.0 = Release|Any CPU
{4624F728-6DFF-44B6-93B5-3C7D9C94BF3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4624F728-6DFF-44B6-93B5-3C7D9C94BF3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4624F728-6DFF-44B6-93B5-3C7D9C94BF3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4624F728-6DFF-44B6-93B5-3C7D9C94BF3F}.Release|Any CPU.Build.0 = Release|Any CPU
{8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Release|Any CPU.Build.0 = Release|Any CPU
{DBFC6DB0-A6D1-4694-A108-1C604B988DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBFC6DB0-A6D1-4694-A108-1C604B988DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBFC6DB0-A6D1-4694-A108-1C604B988DA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBFC6DB0-A6D1-4694-A108-1C604B988DA3}.Release|Any CPU.Build.0 = Release|Any CPU
{E9D1A695-F0E6-46F2-B5E3-72F4AF805387}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9D1A695-F0E6-46F2-B5E3-72F4AF805387}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9D1A695-F0E6-46F2-B5E3-72F4AF805387}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9D1A695-F0E6-46F2-B5E3-72F4AF805387}.Release|Any CPU.Build.0 = Release|Any CPU
{CB4398D6-B7F1-449A-AE02-828769679232}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB4398D6-B7F1-449A-AE02-828769679232}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB4398D6-B7F1-449A-AE02-828769679232}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB4398D6-B7F1-449A-AE02-828769679232}.Release|Any CPU.Build.0 = Release|Any CPU
{A8905301-8492-42FD-9E83-F715A0FDC3A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8905301-8492-42FD-9E83-F715A0FDC3A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8905301-8492-42FD-9E83-F715A0FDC3A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8905301-8492-42FD-9E83-F715A0FDC3A2}.Release|Any CPU.Build.0 = Release|Any CPU
{A64AF9D9-72AA-4433-BE1D-DC2524B6808A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A64AF9D9-72AA-4433-BE1D-DC2524B6808A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A64AF9D9-72AA-4433-BE1D-DC2524B6808A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A64AF9D9-72AA-4433-BE1D-DC2524B6808A}.Release|Any CPU.Build.0 = Release|Any CPU
{C870A92C-9E3F-4BF2-82B8-5758545A8B7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C870A92C-9E3F-4BF2-82B8-5758545A8B7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C870A92C-9E3F-4BF2-82B8-5758545A8B7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C870A92C-9E3F-4BF2-82B8-5758545A8B7C}.Release|Any CPU.Build.0 = Release|Any CPU
{33D8DAD9-74F9-471D-8BAD-55F828FAA736}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33D8DAD9-74F9-471D-8BAD-55F828FAA736}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33D8DAD9-74F9-471D-8BAD-55F828FAA736}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33D8DAD9-74F9-471D-8BAD-55F828FAA736}.Release|Any CPU.Build.0 = Release|Any CPU
{85231B41-6998-49AE-ABD2-5124C83DBEF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85231B41-6998-49AE-ABD2-5124C83DBEF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85231B41-6998-49AE-ABD2-5124C83DBEF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85231B41-6998-49AE-ABD2-5124C83DBEF2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{6D4BCDD6-7951-449B-BE55-CB7F014B7430} = {E0771531-BE20-40CD-A1B0-A57E09511060} {66B77203-1469-41DF-92F2-2BE6900BD36F} = {27304DDE-AFB2-4F8B-B765-E3E2F11E886C}
{1A74148F-9DC0-435D-B5AC-7D1B0D3D5E0B} = {4867A616-83D6-48DC-964D-6AE743596631} {F46DEF99-6FAA-4406-B5D8-6FF34EF669E3} = {27304DDE-AFB2-4F8B-B765-E3E2F11E886C}
{ABF90A5B-F4E0-438C-A6E4-9549FB43690B} = {E0EBA813-4478-4C02-B11D-FB3793113FE4} {66B071A8-EFC8-4A06-BEF6-06B99AE27EEC} = {27304DDE-AFB2-4F8B-B765-E3E2F11E886C}
{8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11} = {727E6D58-6830-4792-96C6-E138A33959FB} {E6A161EA-646C-4033-9090-95BE809AB8D9} = {23836492-E7F4-4376-85BF-A635C304AC46}
{DBFC6DB0-A6D1-4694-A108-1C604B988DA3} = {727E6D58-6830-4792-96C6-E138A33959FB} {1931B19A-EC42-4D56-B2D0-FB06D17244DA} = {E6A161EA-646C-4033-9090-95BE809AB8D9}
{E9D1A695-F0E6-46F2-B5E3-72F4AF805387} = {727E6D58-6830-4792-96C6-E138A33959FB} {DE479DC3-1461-4EAD-A188-4AF7AA4AE344} = {E6A161EA-646C-4033-9090-95BE809AB8D9}
{CB4398D6-B7F1-449A-AE02-828769679232} = {727E6D58-6830-4792-96C6-E138A33959FB} {93EFCC5F-C6EE-4623-894F-A42B22C0B6FE} = {E6A161EA-646C-4033-9090-95BE809AB8D9}
{E0771531-BE20-40CD-A1B0-A57E09511060} = {E6E88944-4800-40BA-8AF5-069EA3ADFEB8}
{A8905301-8492-42FD-9E83-F715A0FDC3A2} = {E0771531-BE20-40CD-A1B0-A57E09511060}
{A64AF9D9-72AA-4433-BE1D-DC2524B6808A} = {E0771531-BE20-40CD-A1B0-A57E09511060}
{E0EBA813-4478-4C02-B11D-FB3793113FE4} = {E6E88944-4800-40BA-8AF5-069EA3ADFEB8}
{C870A92C-9E3F-4BF2-82B8-5758545A8B7C} = {E0EBA813-4478-4C02-B11D-FB3793113FE4}
{4867A616-83D6-48DC-964D-6AE743596631} = {E6E88944-4800-40BA-8AF5-069EA3ADFEB8}
{33D8DAD9-74F9-471D-8BAD-55F828FAA736} = {727E6D58-6830-4792-96C6-E138A33959FB}
{85231B41-6998-49AE-ABD2-5124C83DBEF2} = {727E6D58-6830-4792-96C6-E138A33959FB}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

8
NuGet.config Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
<add key="AspNetCoreTools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="AspNetVNext" value="https://www.myget.org/f/aspnetmaster/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View File

@@ -1,10 +1,18 @@
# JavaScriptServices # JavaScriptServices
AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/gprilrckx116vc9m/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/javascriptservices/branch/dev)
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo. This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
## What is this? ## What is this?
`JavaScriptServices` is a set of technologies for ASP.NET Core developers. It provides infrastructure that you'll find useful if you use Angular 2 / React / Knockout / etc. on the client, or if you build your client-side resources using Webpack, or otherwise want to execute JavaScript on the server at runtime. `JavaScriptServices` is a set of client-side technologies for ASP.NET Core. It provides infrastructure that you'll find useful if you:
- Use Angular / React / Vue / Aurelia / Knockout / etc.
- Build your client-side resources using Webpack.
- Execute JavaScript on the server at runtime.
Read [Building Single Page Applications on ASP.NET Core with JavaScriptServices](https://blogs.msdn.microsoft.com/webdev/2017/02/14/building-single-page-applications-on-asp-net-core-with-javascriptservices/) for more details.
This repo contains: This repo contains:
@@ -15,27 +23,26 @@ This repo contains:
* Hot module replacement (HMR) ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#webpack-hot-module-replacement)) * Hot module replacement (HMR) ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#webpack-hot-module-replacement))
* Server-side and client-side routing integration ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#routing-helper-mapspafallbackroute)) * Server-side and client-side routing integration ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#routing-helper-mapspafallbackroute))
* Server-side and client-side validation integration * Server-side and client-side validation integration
* "Cache priming" for Angular 2 apps
* "Lazy loading" for Knockout apps * "Lazy loading" for Knockout apps
* A Yeoman generator that creates preconfigured app starting points ([guide](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/)) * A Yeoman generator that creates preconfigured app starting points ([guide](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/))
* Samples and docs * Samples and docs
Everything here is cross-platform, and works with .NET Core 1.0.1 or later on Windows, Linux, or OS X. It's cross-platform (Windows, Linux, or macOS) and works with .NET Core 1.0.1 or later.
## Creating new applications ## Creating new applications
If you want to build a brand-new ASP.NET Core app that uses Angular 2 / React / Knockout on the client, consider starting with the `aspnetcore-spa` generator. This lets you choose your client-side framework, and generates a starting point that includes applicable features such as Webpack dev middleware, server-side prerendering, and efficient production builds. It's much easier than configuring everything to work together manually! If you want to build a brand-new ASP.NET Core app that uses Angular / React / Knockout on the client, consider starting with the `aspnetcore-spa` generator. This lets you choose your client-side framework. It generates a starting point that includes applicable features such as Webpack dev middleware, server-side prerendering, and efficient production builds. It's much easier than configuring everything to work together manually!
To do this, first install Yeoman and these generator templates: To do this, install Yeoman and these generator templates:
npm install -g yo generator-aspnetcore-spa npm install -g yo generator-aspnetcore-spa
Then you can generate your new application starting point: Generate your new application starting point:
cd some-empty-directory cd some-empty-directory
yo aspnetcore-spa yo aspnetcore-spa
Finally, once the generator has run and restored all the dependencies, you can start up your new ASP.NET Core Single Page Application: Once the generator has run and restored all the dependencies, you can start up your new ASP.NET Core SPA:
dotnet run dotnet run
@@ -50,11 +57,11 @@ If you have an existing ASP.NET Core application, or if you just want to use the
* Most applications developers don't need to use this directly, but you can do so if you want to implement your own functionality that involves calling Node.js code from .NET at runtime. * Most applications developers don't need to use this directly, but you can do so if you want to implement your own functionality that involves calling Node.js code from .NET at runtime.
* Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.NodeServices#microsoftaspnetcorenodeservices). * Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.NodeServices#microsoftaspnetcorenodeservices).
* `Microsoft.AspNetCore.SpaServices` * `Microsoft.AspNetCore.SpaServices`
* This provides infrastructure that's generally useful when building Single Page Applications (SPAs) with technologies such as Angular 2 or React (for example, server-side prerendering and webpack middleware). Internally, it uses the `NodeServices` package to implement its features. * This provides infrastructure that's generally useful when building Single Page Applications (SPAs) with technologies such as Angular or React (for example, server-side prerendering and webpack middleware). Internally, it uses the `NodeServices` package to implement its features.
* Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#microsoftaspnetcorespaservices). * Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#microsoftaspnetcorespaservices).
* `Microsoft.AspNetCore.AngularServices` * `Microsoft.AspNetCore.AngularServices`
* This builds on the `SpaServices` package and includes features specific to Angular 2. Currently, this includes validation helpers and a "cache priming" feature, which let you pre-evaluate ajax requests on the server so that client-side code doesn't need to make network calls once it's loaded. * This builds on the `SpaServices` package and includes features specific to Angular. Currently, this includes validation helpers.
* The code is [here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.AngularServices), and you'll find a usage example for [the validation helper here](https://github.com/aspnet/JavaScriptServices/blob/dev/samples/angular/MusicStore/wwwroot/ng-app/components/admin/album-edit/album-edit.ts), and for the [cache priming here](https://github.com/aspnet/JavaScriptServices/blob/dev/samples/angular/MusicStore/Views/Home/Index.cshtml#L7-8). Full docs are to be written. * The code is [here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.AngularServices). You'll find a usage example for [the validation helper here](https://github.com/aspnet/JavaScriptServices/blob/dev/samples/angular/MusicStore/wwwroot/ng-app/components/admin/album-edit/album-edit.ts).
There was previously a `Microsoft.AspNetCore.ReactServices` but this is not currently needed - all applicable functionality is in `Microsoft.AspNetCore.SpaServices`, because it's sufficiently general. We might add a new `Microsoft.AspNetCore.ReactServices` package in the future if new React-specific requirements emerge. There was previously a `Microsoft.AspNetCore.ReactServices` but this is not currently needed - all applicable functionality is in `Microsoft.AspNetCore.SpaServices`, because it's sufficiently general. We might add a new `Microsoft.AspNetCore.ReactServices` package in the future if new React-specific requirements emerge.
@@ -62,28 +69,32 @@ If you want to build a helper library for some other SPA framework, you can do s
## Samples and templates ## Samples and templates
Inside this repo, [the `templates` directory](https://github.com/aspnet/JavaScriptServices/tree/dev/templates) contains the application starting points that the `aspnetcore-spa` generator emits. If you want, you can clone this repo and run those applications directly. But it's easier to [use the Yeoman tool to run the generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/). Inside this repo, [the `templates` directory](https://github.com/aspnet/JavaScriptServices/tree/dev/templates) contains the application starting points that the `aspnetcore-spa` generator emits. You can clone this repo and run those applications directly. But it's easier to [use the Yeoman tool to run the generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/).
Also in this repo, [the `samples` directory](https://github.com/aspnet/JavaScriptServices/tree/dev/samples) contains examples of using the JavaScript services family of packages with Angular 2 and React, plus examples of standalone `NodeServices` usage for runtime code transpilation and image processing. The [`samples` directory](https://github.com/aspnet/JavaScriptServices/tree/dev/samples) contains examples of:
- Using the JavaScript services family of packages with Angular and React.
- A standalone `NodeServices` usage for runtime code transpilation and image processing.
**To run the samples:** **To run the samples:**
* Clone this repo * Clone this repo
* At the repo's root directory (the one containing `src`, `samples`, etc.), run `dotnet restore` * At the repo's root directory (the one containing `src`, `samples`, etc.), run `dotnet restore`
* Change directory to the sample you want to run (e.g., `cd samples/angular/MusicStore`) * Change directory to the sample you want to run (for example, `cd samples/angular/MusicStore`)
* Restore Node dependencies by running `npm install` * Restore Node dependencies by running `npm install`
* If you're trying to run the Angular 2 "Music Store" sample, then also run `gulp` (which you need to have installed globally). None of the other samples require this. * If you're trying to run the Angular "Music Store" sample, then also run `gulp` (which you need to have installed globally). None of the other samples require this.
* Run the application (`dotnet run`) * Run the application (`dotnet run`)
* Browse to [http://localhost:5000](http://localhost:5000) * Browse to [http://localhost:5000](http://localhost:5000)
## Contributing ## Contributing
If you're interested in contributing to the various packages, samples, and project templates in this repo, that's great! You can run the code in this repo just by: If you're interested in contributing to the various packages, samples, and project templates in this repo, that's great! You can run the code in this repo as follows:
* Cloning the repo * Clone the repo
* Running `dotnet restore` at the repo root dir * Run `dotnet restore` at the repo root dir
* Going to whatever sample or template you want to run (e.g., `cd templates/Angular2Spa`) * Go to whatever sample or template you want to run (for example, `cd templates/AngularSpa`)
* Restoring NPM dependencies (run `npm install`) * Restore NPM dependencies (run `npm install`)
* Launching it (`dotnet run`) * If the sample/template you're trying to run has a file called `webpack.config.vendor.js` at its root, run `webpack --config webpack.config.vendor.js`. It it has a file called `webpack.config.js`, run `webpack` (no args). You might need to install webpack first, by running `npm install -g webpack`.
* Launch it (`dotnet run`)
If you're planning to submit a pull request, and if it's more than a trivial fix (e.g., for a typo), it's usually a good idea first to file an issue describing what you're proposing to do and how it will work. Then you can find out if it's likely that such a pull request will be accepted, and how it fits into wider ongoing plans. If you're planning to submit a pull request, and if it's more than a trivial fix (for example, for a typo), it's usually a good idea first to file an issue describing what you're proposing to do and how it will work. Then you can find out if it's likely that such a pull request will be accepted, and how it fits into wider ongoing plans.

View File

@@ -1,7 +1,40 @@
init: init:
- git config --global core.autocrlf true - git config --global core.autocrlf true
install:
- ps: Install-Product node 6.9.2 x64
# .NET Core SDK binaries
# Download .NET Core 2.0 Preview 1 SDK and add to PATH
- ps: $urlCurrent = "https://download.microsoft.com/download/0/6/5/0656B047-5F2F-4281-A851-F30776F8616D/dotnet-dev-win-x64.2.0.0-preview1-005977.zip"
- ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk"
- ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null
- ps: $tempFileCurrent = [System.IO.Path]::GetTempFileName()
- ps: (New-Object System.Net.WebClient).DownloadFile($urlCurrent, $tempFileCurrent)
- ps: Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($tempFileCurrent, $env:DOTNET_INSTALL_DIR)
- ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
build_script: build_script:
- build.cmd verify - ps: Push-Location
- cd templates/package-builder
- npm install
- npm run build
- ps: Pop-Location
artifacts:
- path: templates\package-builder\dist\artifacts\generator-aspnetcore-spa.tar.gz
name: generator-aspnetcore-spa
- path: templates\package-builder\dist\artifacts\*.nupkg
name: Microsoft.AspNetCore.SpaTemplates
type: NuGetPackage
# - ps: .\build.ps1
clone_depth: 1 clone_depth: 1
test: off test_script:
- dotnet restore
- ps: Push-Location
- cd test
- npm install selenium-standalone
- ps: Start-Process node './start-selenium.js'
- npm install
- npm test
on_finish :
- ps: Pop-Location
# After running tests, upload results to Appveyor
- ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\test\tmp\junit\*.xml))
deploy: off deploy: off

View File

@@ -64,4 +64,4 @@ if (!(Test-Path $buildFolder)) {
} }
} }
&"$buildFile" $args &"$buildFile" @args

View File

@@ -38,7 +38,7 @@ if test ! -d $buildFolder; then
chmod +x $buildFile chmod +x $buildFile
# Cleanup # Cleanup
if test ! -d $tempFolder; then if test -d $tempFolder; then
rm -rf $tempFolder rm -rf $tempFolder
fi fi
fi fi

22
build/common.props Normal file
View File

@@ -0,0 +1,22 @@
<Project>
<Import Project="dependencies.props" />
<Import Project="..\version.props" />
<PropertyGroup>
<Product>Microsoft ASP.NET Core</Product>
<RepositoryUrl>https://github.com/aspnet/javascriptservices</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)Key.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
<VersionSuffix Condition="'$(VersionSuffix)'!='' AND '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkVersion)" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)'=='.NETFramework' AND '$(OutputType)'=='library'">
<PackageReference Include="NETStandard.Library" Version="$(BundledNETStandardPackageVersion)" />
</ItemGroup>
</Project>

11
build/dependencies.props Normal file
View File

@@ -0,0 +1,11 @@
<Project>
<PropertyGroup>
<AspNetCoreVersion>2.0.0-*</AspNetCoreVersion>
<AutoMapperVersion>5.0.2</AutoMapperVersion>
<CoreFxVersion>4.3.0</CoreFxVersion>
<InternalAspNetCoreSdkVersion>2.1.0-*</InternalAspNetCoreSdkVersion>
<JsonNetVersion>10.0.1</JsonNetVersion>
<NETStandardImplicitPackageVersion>$(BundledNETStandardPackageVersion)</NETStandardImplicitPackageVersion>
<ThreadingDataflowVersion>4.7.0</ThreadingDataflowVersion>
</PropertyGroup>
</Project>

12
build/repo.targets Normal file
View File

@@ -0,0 +1,12 @@
<Project>
<Target Name="NpmRestore" AfterTargets="Restore" Condition="'$(PreflightRestore)' != 'true'">
<ItemGroup>
<NpmModules Include="$(RepositoryRoot)**\package.json"
Exclude="$(RepositoryRoot)**\node_modules\**\*" />
</ItemGroup>
<Message Text="Restoring NPM modules for: %0A - @(NpmModules -> '%(FullPath)','%0A - ')" Importance="high" />
<Exec Command="npm install" WorkingDirectory="%(NpmModules.RootDir)%(Directory)" />
</Target>
</Project>

View File

@@ -1,3 +0,0 @@
{
"projects": ["src"]
}

View File

@@ -1,8 +0,0 @@
var VERSION='0.1'
var FULL_VERSION='0.1'
var AUTHORS='Microsoft Open Technologies, Inc.'
var SAMPLES_PROJECT_GLOB='templates/*/project.json'
use-standard-lifecycle
k-standard-goals

View File

@@ -1,22 +0,0 @@
versionSuffix=$1
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
projects=(
./src/Microsoft.AspNetCore.NodeServices
./src/Microsoft.AspNetCore.SpaServices
./src/Microsoft.AspNetCore.AngularServices
./src/Microsoft.AspNetCore.ReactServices
)
if [ -z "$versionSuffix" ]; then
echo "Usage: pack-local.sh <versionsuffix>"
echo "Example: pack-local.sh beta-000001"
exit 1
fi
pushd $dir > /dev/null
for proj in "${projects[@]}"; do
dotnet pack $proj --version-suffix $versionSuffix -o ./artifacts/
done
popd > /dev/null

View File

@@ -1,10 +0,0 @@
/node_modules/
/wwwroot/lib/
/wwwroot/ng-app/**/*.js
/project.lock.json
/music-db.sqlite
/Properties/launchSettings.json
# Obviously you don't really want your DB to go in wwwroot, but due to https://github.com/aspnet/Microsoft.Data.Sqlite/issues/188
# it currently does when run from IIS Express. Will resolve this once RC2 is out, which is supposed to eliminate the inconsistency.
/wwwroot/music-db.sqlite

View File

@@ -1,216 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using MusicStore.Models;
using MusicStore.Infrastructure;
namespace MusicStore.Apis
{
[Route("api/albums")]
public class AlbumsApiController : Controller
{
private readonly MusicStoreContext _storeContext;
public AlbumsApiController(MusicStoreContext storeContext)
{
_storeContext = storeContext;
}
[HttpGet]
[NoCache]
public async Task<ActionResult> Paged(int page = 1, int pageSize = 50, string sortBy = null)
{
await _storeContext.Genres.LoadAsync();
await _storeContext.Artists.LoadAsync();
var albums = await _storeContext.Albums
.Include(a => a.Genre)
.Include(a => a.Artist)
.ToPagedListAsync(page, pageSize, sortBy,
a => a.Title, // sortExpression
SortDirection.Ascending, // defaultSortDirection
a => Mapper.Map(a, new AlbumResultDto())); // selector
return Json(albums);
}
[HttpGet("all")]
[NoCache]
public async Task<ActionResult> All()
{
var albums = await _storeContext.Albums
.Include(a => a.Genre)
.Include(a => a.Artist)
.OrderBy(a => a.Title)
.ToListAsync();
return Json(albums.Select(a => Mapper.Map(a, new AlbumResultDto())));
}
[HttpGet("mostPopular")]
[NoCache]
public async Task<ActionResult> MostPopular(int count = 6)
{
count = count > 0 && count < 20 ? count : 6;
var albums = await _storeContext.Albums
.OrderByDescending(a => a.OrderDetails.Count())
.Take(count)
.ToListAsync();
// TODO: Move the .Select() to end of albums query when EF supports it
return Json(albums.Select(a => Mapper.Map(a, new AlbumResultDto())));
}
[HttpGet("{albumId:int}")]
[NoCache]
public async Task<ActionResult> Details(int albumId)
{
await _storeContext.Genres.LoadAsync();
await _storeContext.Artists.LoadAsync();
var album = await _storeContext.Albums
.Include(a => a.Artist)
.Include(a => a.Genre)
.Where(a => a.AlbumId == albumId)
.SingleOrDefaultAsync();
var albumResult = Mapper.Map(album, new AlbumResultDto());
// TODO: Add null checking and return 404 in that case
return Json(albumResult);
}
[HttpPost]
[Authorize("app-ManageStore")]
public async Task<ActionResult> CreateAlbum([FromBody]AlbumChangeDto album)
{
if (!ModelState.IsValid)
{
// Return the model errors
return BadRequest(ModelState);
}
// Save the changes to the DB
var dbAlbum = new Album();
_storeContext.Albums.Add(Mapper.Map(album, dbAlbum));
await _storeContext.SaveChangesAsync();
// TODO: Handle missing record, key violations, concurrency issues, etc.
return new ObjectResult(new {
Data = dbAlbum.AlbumId,
Message = "Album created successfully."
});
}
[HttpPut("{albumId:int}/update")]
public async Task<ActionResult> UpdateAlbum(int albumId, [FromBody] AlbumChangeDto album)
{
if (!ModelState.IsValid)
{
// Return the model errors
return BadRequest(ModelState);
}
var dbAlbum = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
if (dbAlbum == null)
{
return new ObjectResult(new {
Message = string.Format("The album with ID {0} was not found.", albumId)
}) { StatusCode = 404 };
}
// Save the changes to the DB
Mapper.Map(album, dbAlbum);
await _storeContext.SaveChangesAsync();
// TODO: Handle missing record, key violations, concurrency issues, etc.
return new ObjectResult (new {
Message = "Album updated successfully."
});
}
[HttpDelete("{albumId:int}")]
[Authorize("app-ManageStore")]
public async Task<ActionResult> DeleteAlbum(int albumId)
{
var album = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
if (album != null)
{
_storeContext.Albums.Remove(album);
// Save the changes to the DB
await _storeContext.SaveChangesAsync();
// TODO: Handle missing record, key violations, concurrency issues, etc.
}
return new ObjectResult (new {
Message = "Album deleted successfully."
});
}
}
[ModelMetadataType(typeof(Album))]
public class AlbumChangeDto : IValidatableObject
{
public int GenreId { get; set; }
public int ArtistId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// An example of object-level (i.e., multi-property) validation
if (this.GenreId == 13 /* Indie */) {
switch (SentimentAnalysis.GetSentiment(Title)) {
case SentimentAnalysis.SentimentResult.Positive:
yield return new ValidationResult("Sounds too positive. Indie music requires more ambiguity.");
break;
case SentimentAnalysis.SentimentResult.Negative:
yield return new ValidationResult("Sounds too negative. Indie music requires more ambiguity.");
break;
}
}
}
}
public class AlbumResultDto : AlbumChangeDto
{
public AlbumResultDto()
{
Artist = new ArtistResultDto();
Genre = new GenreResultDto();
}
public int AlbumId { get; set; }
public ArtistResultDto Artist { get; private set; }
public GenreResultDto Genre { get; private set; }
}
public class ArtistResultDto
{
public string Name { get; set; }
}
public class GenreResultDto
{
public string Name { get; set; }
}
}

View File

@@ -1,30 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MusicStore.Models;
namespace MusicStore.Apis
{
[Route("api/artists")]
public class ArtistsApiController : Controller
{
private readonly MusicStoreContext _storeContext;
public ArtistsApiController(MusicStoreContext storeContext)
{
_storeContext = storeContext;
}
[HttpGet("lookup")]
public async Task<ActionResult> Lookup()
{
var artists = await _storeContext.Artists
.OrderBy(a => a.Name)
.ToListAsync();
return Json(artists);
}
}
}

View File

@@ -1,70 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MusicStore.Models;
using MusicStore.Infrastructure;
namespace MusicStore.Apis
{
[Route("api/genres")]
public class GenresApiController : Controller
{
private readonly MusicStoreContext _storeContext;
public GenresApiController(MusicStoreContext storeContext)
{
_storeContext = storeContext;
}
[HttpGet]
public async Task<ActionResult> GenreList()
{
var genres = await _storeContext.Genres
//.Include(g => g.Albums)
.OrderBy(g => g.Name)
.ToListAsync();
return Json(genres);
}
[HttpGet("genre-lookup")]
public async Task<ActionResult> Lookup()
{
var genres = await _storeContext.Genres
.Select(g => new { g.GenreId, g.Name })
.ToListAsync();
return Json(genres);
}
[HttpGet("menu")]
public async Task<ActionResult> GenreMenuList(int count = 9)
{
count = count > 0 && count < 20 ? count : 9;
var genres = await _storeContext.Genres
.OrderByDescending(g =>
g.Albums.Sum(a =>
a.OrderDetails.Sum(od => od.Quantity)))
.Take(count)
.ToListAsync();
return Json(genres);
}
[HttpGet("{genreId:int}/albums")]
[NoCache]
public async Task<ActionResult> GenreAlbums(int genreId)
{
var albums = await _storeContext.Albums
.Where(a => a.GenreId == genreId)
//.Include(a => a.Genre)
//.Include(a => a.Artist)
//.OrderBy(a => a.Genre.Name)
.ToListAsync();
return Json(albums);
}
}
}

View File

@@ -1,63 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace MusicStore.Models
{
public class ExternalLoginConfirmationViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
}
public class ManageUserViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class LoginViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public class RegisterViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
}

View File

@@ -1,40 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MusicStore.Models
{
public class Album
{
public Album()
{
// TODO: Temporary hack to populate the orderdetails until EF does this automatically.
OrderDetails = new List<OrderDetail>();
}
[ScaffoldColumn(false)]
public int AlbumId { get; set; }
public int GenreId { get; set; }
public int ArtistId { get; set; }
[Required]
[StringLength(160, MinimumLength = 2)]
public string Title { get; set; }
[Required]
[RangeAttribute(typeof(decimal), "0.01", "100")] // Long-form constructor to work around https://github.com/dotnet/coreclr/issues/2172
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[Display(Name = "Album Art URL")]
[StringLength(1024)]
public string AlbumArtUrl { get; set; }
public virtual Genre Genre { get; set; }
public virtual Artist Artist { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
}
}

View File

@@ -1,12 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace MusicStore.Models
{
public class Artist
{
public int ArtistId { get; set; }
[Required]
public string Name { get; set; }
}
}

View File

@@ -1,21 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace MusicStore.Models
{
public class CartItem
{
[Key]
public int CartItemId { get; set; }
[Required]
public string CartId { get; set; }
public int AlbumId { get; set; }
public int Count { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateCreated { get; set; }
public virtual Album Album { get; set; }
}
}

View File

@@ -1,24 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace MusicStore.Models
{
public class Genre
{
public Genre()
{
Albums = new List<Album>();
}
public int GenreId { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[JsonIgnore]
public virtual ICollection<Album> Albums { get; set; }
}
}

View File

@@ -1,34 +0,0 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace MusicStore.Models
{
public class ApplicationUser : IdentityUser { }
public class MusicStoreContext : IdentityDbContext<ApplicationUser>
{
public MusicStoreContext(DbContextOptions options) : base(options)
{
}
public DbSet<Album> Albums { get; set; }
public DbSet<Artist> Artists { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<CartItem> CartItems { get; set; }
public DbSet<OrderDetail> OrderDetails { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
// Configure pluralization
builder.Entity<Album>().ToTable("Albums");
builder.Entity<Artist>().ToTable("Artists");
builder.Entity<Order>().ToTable("Orders");
builder.Entity<Genre>().ToTable("Genres");
builder.Entity<CartItem>().ToTable("CartItems");
builder.Entity<OrderDetail>().ToTable("OrderDetails");
base.OnModelCreating(builder);
}
}
}

View File

@@ -1,73 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MusicStore.Models
{
//[Bind(Include = "FirstName,LastName,Address,City,State,PostalCode,Country,Phone,Email")]
public class Order
{
public Order()
{
OrderDetails = new List<OrderDetail>();
}
[ScaffoldColumn(false)]
public int OrderId { get; set; }
[ScaffoldColumn(false)]
public DateTime OrderDate { get; set; }
[Required]
[ScaffoldColumn(false)]
public string Username { get; set; }
[Required]
[Display(Name = "First Name")]
[StringLength(160)]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
[StringLength(160)]
public string LastName { get; set; }
[Required]
[StringLength(70, MinimumLength = 3)]
public string Address { get; set; }
[Required]
[StringLength(40)]
public string City { get; set; }
[Required]
[StringLength(40)]
public string State { get; set; }
[Required]
[Display(Name = "Postal Code")]
[StringLength(10, MinimumLength = 5)]
public string PostalCode { get; set; }
[Required]
[StringLength(40)]
public string Country { get; set; }
[Required]
[StringLength(24)]
[DataType(DataType.PhoneNumber)]
public string Phone { get; set; }
[Required]
[Display(Name = "Email Address")]
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
ErrorMessage = "Email is not valid.")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[ScaffoldColumn(false)]
public decimal Total { get; set; }
public ICollection<OrderDetail> OrderDetails { get; set; }
}
}

View File

@@ -1,14 +0,0 @@
namespace MusicStore.Models
{
public class OrderDetail
{
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public int AlbumId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public virtual Album Album { get; set; }
public virtual Order Order { get; set; }
}
}

View File

@@ -1,915 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace MusicStore.Models
{
public static class SampleData
{
const string imgUrl = "/images/placeholder.png";
public static async Task InitializeMusicStoreDatabaseAsync(IServiceProvider serviceProvider)
{
using (var db = serviceProvider.GetService<MusicStoreContext>())
{
if (await db.Database.EnsureCreatedAsync())
{
await InsertTestData(serviceProvider);
}
}
}
private static async Task InsertTestData(IServiceProvider serviceProvider)
{
var albums = GetAlbums(imgUrl, Genres, Artists);
await AddOrUpdateAsync(serviceProvider, g => g.GenreId, Genres.Select(genre => genre.Value));
await AddOrUpdateAsync(serviceProvider, a => a.ArtistId, Artists.Select(artist => artist.Value));
await AddOrUpdateAsync(serviceProvider, a => a.AlbumId, albums);
}
// TODO [EF] This may be replaced by a first class mechanism in EF
private static async Task AddOrUpdateAsync<TEntity>(
IServiceProvider serviceProvider,
Func<TEntity, object> propertyToMatch, IEnumerable<TEntity> entities)
where TEntity : class
{
// Query in a separate context so that we can attach existing entities as modified
List<TEntity> existingData;
using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
using (var db = scope.ServiceProvider.GetService<MusicStoreContext>())
{
existingData = db.Set<TEntity>().ToList();
}
using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
using (var db = scope.ServiceProvider.GetService<MusicStoreContext>())
{
foreach (var item in entities)
{
db.Entry(item).State = existingData.Any(g => propertyToMatch(g).Equals(propertyToMatch(item)))
? EntityState.Modified
: EntityState.Added;
}
await db.SaveChangesAsync();
}
}
private static Album[] GetAlbums(string imgUrl, Dictionary<string, Genre> genres, Dictionary<string, Artist> artists)
{
var albums = new Album[]
{
new Album { Title = "The Best Of The Men At Work", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Men At Work"], AlbumArtUrl = imgUrl },
new Album { Title = "...And Justice For All", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "עד גבול האור", Genre = genres["World"], Price = 8.99M, Artist = artists["אריק אינשטיין"], AlbumArtUrl = imgUrl },
new Album { Title = "Black Light Syndrome", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Terry Bozzio, Tony Levin & Steve Stevens"], AlbumArtUrl = imgUrl },
new Album { Title = "10,000 Days", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
new Album { Title = "11i", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Supreme Beings of Leisure"], AlbumArtUrl = imgUrl },
new Album { Title = "1960", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Soul-Junk"], AlbumArtUrl = imgUrl },
new Album { Title = "4x4=12 ", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["deadmau5"], AlbumArtUrl = imgUrl },
new Album { Title = "A Copland Celebration, Vol. I", Genre = genres["Classical"], Price = 8.99M, Artist = artists["London Symphony Orchestra"], AlbumArtUrl = imgUrl },
new Album { Title = "A Lively Mind", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Paul Oakenfold"], AlbumArtUrl = imgUrl },
new Album { Title = "A Matter of Life and Death", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "A Real Dead One", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "A Real Live One", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "A Rush of Blood to the Head", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Coldplay"], AlbumArtUrl = imgUrl },
new Album { Title = "A Soprano Inspired", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Britten Sinfonia, Ivor Bolton & Lesley Garrett"], AlbumArtUrl = imgUrl },
new Album { Title = "A Winter Symphony", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "Abbey Road", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Beatles"], AlbumArtUrl = imgUrl },
new Album { Title = "Ace Of Spades", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Motörhead"], AlbumArtUrl = imgUrl },
new Album { Title = "Achtung Baby", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "Acústico MTV", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Os Paralamas Do Sucesso"], AlbumArtUrl = imgUrl },
new Album { Title = "Adams, John: The Chairman Dances", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Edo de Waart & San Francisco Symphony"], AlbumArtUrl = imgUrl },
new Album { Title = "Adrenaline", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deftones"], AlbumArtUrl = imgUrl },
new Album { Title = "Ænima", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
new Album { Title = "Afrociberdelia", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Chico Science & Nação Zumbi"], AlbumArtUrl = imgUrl },
new Album { Title = "After the Goldrush", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Neil Young"], AlbumArtUrl = imgUrl },
new Album { Title = "Airdrawn Dagger", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Sasha"], AlbumArtUrl = imgUrl },
new Album { Title = "Album Title Goes Here", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["deadmau5"], AlbumArtUrl = imgUrl },
new Album { Title = "Alcohol Fueled Brewtality Live! [Disc 1]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Label Society"], AlbumArtUrl = imgUrl },
new Album { Title = "Alcohol Fueled Brewtality Live! [Disc 2]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Label Society"], AlbumArtUrl = imgUrl },
new Album { Title = "Alive 2007", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Daft Punk"], AlbumArtUrl = imgUrl },
new Album { Title = "All I Ask of You", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "Amen (So Be It)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Paddy Casey"], AlbumArtUrl = imgUrl },
new Album { Title = "Animal Vehicle", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Axis of Awesome"], AlbumArtUrl = imgUrl },
new Album { Title = "Ao Vivo [IMPORT]", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Zeca Pagodinho"], AlbumArtUrl = imgUrl },
new Album { Title = "Apocalyptic Love", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Slash"], AlbumArtUrl = imgUrl },
new Album { Title = "Appetite for Destruction", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
new Album { Title = "Are You Experienced?", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Jimi Hendrix"], AlbumArtUrl = imgUrl },
new Album { Title = "Arquivo II", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Os Paralamas Do Sucesso"], AlbumArtUrl = imgUrl },
new Album { Title = "Arquivo Os Paralamas Do Sucesso", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Os Paralamas Do Sucesso"], AlbumArtUrl = imgUrl },
new Album { Title = "A-Sides", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Soundgarden"], AlbumArtUrl = imgUrl },
new Album { Title = "Audioslave", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Audioslave"], AlbumArtUrl = imgUrl },
new Album { Title = "Automatic for the People", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["R.E.M."], AlbumArtUrl = imgUrl },
new Album { Title = "Axé Bahia 2001", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Various Artists"], AlbumArtUrl = imgUrl },
new Album { Title = "Babel", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Mumford & Sons"], AlbumArtUrl = imgUrl },
new Album { Title = "Bach: Goldberg Variations", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Wilhelm Kempff"], AlbumArtUrl = imgUrl },
new Album { Title = "Bach: The Brandenburg Concertos", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Orchestra of The Age of Enlightenment"], AlbumArtUrl = imgUrl },
new Album { Title = "Bach: The Cello Suites", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Yo-Yo Ma"], AlbumArtUrl = imgUrl },
new Album { Title = "Bach: Toccata & Fugue in D Minor", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Ton Koopman"], AlbumArtUrl = imgUrl },
new Album { Title = "Bad Motorfinger", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Soundgarden"], AlbumArtUrl = imgUrl },
new Album { Title = "Balls to the Wall", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Accept"], AlbumArtUrl = imgUrl },
new Album { Title = "Banadeek Ta'ala", Genre = genres["World"], Price = 8.99M, Artist = artists["Amr Diab"], AlbumArtUrl = imgUrl },
new Album { Title = "Barbie Girl", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Aqua"], AlbumArtUrl = imgUrl },
new Album { Title = "Bark at the Moon (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
new Album { Title = "Bartok: Violin & Viola Concertos", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Yehudi Menuhin"], AlbumArtUrl = imgUrl },
new Album { Title = "Barulhinho Bom", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Marisa Monte"], AlbumArtUrl = imgUrl },
new Album { Title = "BBC Sessions [Disc 1] [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "BBC Sessions [Disc 2] [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Be Here Now", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Oasis"], AlbumArtUrl = imgUrl },
new Album { Title = "Bedrock 11 Compiled & Mixed", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["John Digweed"], AlbumArtUrl = imgUrl },
new Album { Title = "Berlioz: Symphonie Fantastique", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Michael Tilson Thomas"], AlbumArtUrl = imgUrl },
new Album { Title = "Beyond Good And Evil", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Cult"], AlbumArtUrl = imgUrl },
new Album { Title = "Big Bad Wolf ", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Armand Van Helden"], AlbumArtUrl = imgUrl },
new Album { Title = "Big Ones", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Aerosmith"], AlbumArtUrl = imgUrl },
new Album { Title = "Black Album", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Black Sabbath Vol. 4 (Remaster)", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Sabbath"], AlbumArtUrl = imgUrl },
new Album { Title = "Black Sabbath", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Sabbath"], AlbumArtUrl = imgUrl },
new Album { Title = "Black", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Blackwater Park", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Opeth"], AlbumArtUrl = imgUrl },
new Album { Title = "Blizzard of Ozz", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
new Album { Title = "Blood", Genre = genres["Rock"], Price = 8.99M, Artist = artists["In This Moment"], AlbumArtUrl = imgUrl },
new Album { Title = "Blue Moods", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Incognito"], AlbumArtUrl = imgUrl },
new Album { Title = "Blue", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Weezer"], AlbumArtUrl = imgUrl },
new Album { Title = "Bongo Fury", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Frank Zappa & Captain Beefheart"], AlbumArtUrl = imgUrl },
new Album { Title = "Boys & Girls", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alabama Shakes"], AlbumArtUrl = imgUrl },
new Album { Title = "Brave New World", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "B-Sides 1980-1990", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "Bunkka", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Paul Oakenfold"], AlbumArtUrl = imgUrl },
new Album { Title = "By The Way", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Red Hot Chili Peppers"], AlbumArtUrl = imgUrl },
new Album { Title = "Cake: B-Sides and Rarities", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Cake"], AlbumArtUrl = imgUrl },
new Album { Title = "Californication", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Red Hot Chili Peppers"], AlbumArtUrl = imgUrl },
new Album { Title = "Carmina Burana", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Boston Symphony Orchestra & Seiji Ozawa"], AlbumArtUrl = imgUrl },
new Album { Title = "Carried to Dust (Bonus Track Version)", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Calexico"], AlbumArtUrl = imgUrl },
new Album { Title = "Carry On", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Chris Cornell"], AlbumArtUrl = imgUrl },
new Album { Title = "Cássia Eller - Sem Limite [Disc 1]", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Cássia Eller"], AlbumArtUrl = imgUrl },
new Album { Title = "Chemical Wedding", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Bruce Dickinson"], AlbumArtUrl = imgUrl },
new Album { Title = "Chill: Brazil (Disc 1)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Marcos Valle"], AlbumArtUrl = imgUrl },
new Album { Title = "Chill: Brazil (Disc 2)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Antônio Carlos Jobim"], AlbumArtUrl = imgUrl },
new Album { Title = "Chocolate Starfish And The Hot Dog Flavored Water", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Limp Bizkit"], AlbumArtUrl = imgUrl },
new Album { Title = "Chronicle, Vol. 1", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Creedence Clearwater Revival"], AlbumArtUrl = imgUrl },
new Album { Title = "Chronicle, Vol. 2", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Creedence Clearwater Revival"], AlbumArtUrl = imgUrl },
new Album { Title = "Ciao, Baby", Genre = genres["Rock"], Price = 8.99M, Artist = artists["TheStart"], AlbumArtUrl = imgUrl },
new Album { Title = "Cidade Negra - Hits", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Cidade Negra"], AlbumArtUrl = imgUrl },
new Album { Title = "Classic Munkle: Turbo Edition", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Munkle"], AlbumArtUrl = imgUrl },
new Album { Title = "Classics: The Best of Sarah Brightman", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "Coda", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Come Away With Me", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Norah Jones"], AlbumArtUrl = imgUrl },
new Album { Title = "Come Taste The Band", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Comfort Eagle", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Cake"], AlbumArtUrl = imgUrl },
new Album { Title = "Common Reaction", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Uh Huh Her "], AlbumArtUrl = imgUrl },
new Album { Title = "Compositores", Genre = genres["Rock"], Price = 8.99M, Artist = artists["O Terço"], AlbumArtUrl = imgUrl },
new Album { Title = "Contraband", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Velvet Revolver"], AlbumArtUrl = imgUrl },
new Album { Title = "Core", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Stone Temple Pilots"], AlbumArtUrl = imgUrl },
new Album { Title = "Cornerstone", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Styx"], AlbumArtUrl = imgUrl },
new Album { Title = "Cosmicolor", Genre = genres["Rap"], Price = 8.99M, Artist = artists["M-Flo"], AlbumArtUrl = imgUrl },
new Album { Title = "Cross", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Justice"], AlbumArtUrl = imgUrl },
new Album { Title = "Culture of Fear", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Thievery Corporation"], AlbumArtUrl = imgUrl },
new Album { Title = "Da Lama Ao Caos", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Chico Science & Nação Zumbi"], AlbumArtUrl = imgUrl },
new Album { Title = "Dakshina", Genre = genres["World"], Price = 8.99M, Artist = artists["Deva Premal"], AlbumArtUrl = imgUrl },
new Album { Title = "Dark Side of the Moon", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
new Album { Title = "Death Magnetic", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Deep End of Down", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Above the Fold"], AlbumArtUrl = imgUrl },
new Album { Title = "Deep Purple In Rock", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Deixa Entrar", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Falamansa"], AlbumArtUrl = imgUrl },
new Album { Title = "Deja Vu", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Crosby, Stills, Nash, and Young"], AlbumArtUrl = imgUrl },
new Album { Title = "Di Korpu Ku Alma", Genre = genres["World"], Price = 8.99M, Artist = artists["Lura"], AlbumArtUrl = imgUrl },
new Album { Title = "Diary of a Madman (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
new Album { Title = "Diary of a Madman", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
new Album { Title = "Dirt", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alice in Chains"], AlbumArtUrl = imgUrl },
new Album { Title = "Diver Down", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
new Album { Title = "Djavan Ao Vivo - Vol. 02", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Djavan"], AlbumArtUrl = imgUrl },
new Album { Title = "Djavan Ao Vivo - Vol. 1", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Djavan"], AlbumArtUrl = imgUrl },
new Album { Title = "Drum'n'bass for Papa", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Plug"], AlbumArtUrl = imgUrl },
new Album { Title = "Duluth", Genre = genres["Country"], Price = 8.99M, Artist = artists["Trampled By Turtles"], AlbumArtUrl = imgUrl },
new Album { Title = "Dummy", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Portishead"], AlbumArtUrl = imgUrl },
new Album { Title = "Duos II", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Luciana Souza/Romero Lubambo"], AlbumArtUrl = imgUrl },
new Album { Title = "Earl Scruggs and Friends", Genre = genres["Country"], Price = 8.99M, Artist = artists["Earl Scruggs"], AlbumArtUrl = imgUrl },
new Album { Title = "Eden", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "El Camino", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Black Keys"], AlbumArtUrl = imgUrl },
new Album { Title = "Elegant Gypsy", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Al di Meola"], AlbumArtUrl = imgUrl },
new Album { Title = "Elements Of Life", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Tiësto"], AlbumArtUrl = imgUrl },
new Album { Title = "Elis Regina-Minha História", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Elis Regina"], AlbumArtUrl = imgUrl },
new Album { Title = "Emergency On Planet Earth", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Jamiroquai"], AlbumArtUrl = imgUrl },
new Album { Title = "Emotion", Genre = genres["World"], Price = 8.99M, Artist = artists["Papa Wemba"], AlbumArtUrl = imgUrl },
new Album { Title = "English Renaissance", Genre = genres["Classical"], Price = 8.99M, Artist = artists["The King's Singers"], AlbumArtUrl = imgUrl },
new Album { Title = "Every Kind of Light", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Posies"], AlbumArtUrl = imgUrl },
new Album { Title = "Faceless", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Godsmack"], AlbumArtUrl = imgUrl },
new Album { Title = "Facelift", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alice in Chains"], AlbumArtUrl = imgUrl },
new Album { Title = "Fair Warning", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
new Album { Title = "Fear of a Black Planet", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Public Enemy"], AlbumArtUrl = imgUrl },
new Album { Title = "Fear Of The Dark", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Feels Like Home", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Norah Jones"], AlbumArtUrl = imgUrl },
new Album { Title = "Fireball", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Fly", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "For Those About To Rock We Salute You", Genre = genres["Rock"], Price = 8.99M, Artist = artists["AC/DC"], AlbumArtUrl = imgUrl },
new Album { Title = "Four", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Blues Traveler"], AlbumArtUrl = imgUrl },
new Album { Title = "Frank", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Amy Winehouse"], AlbumArtUrl = imgUrl },
new Album { Title = "Further Down the Spiral", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Nine Inch Nails"], AlbumArtUrl = imgUrl },
new Album { Title = "Garage Inc. (Disc 1)", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Garage Inc. (Disc 2)", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Garbage", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Garbage"], AlbumArtUrl = imgUrl },
new Album { Title = "Good News For People Who Love Bad News", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Modest Mouse"], AlbumArtUrl = imgUrl },
new Album { Title = "Gordon", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Barenaked Ladies"], AlbumArtUrl = imgUrl },
new Album { Title = "Górecki: Symphony No. 3", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Adrian Leaper & Doreen de Feis"], AlbumArtUrl = imgUrl },
new Album { Title = "Greatest Hits I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
new Album { Title = "Greatest Hits II", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
new Album { Title = "Greatest Hits", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Duck Sauce"], AlbumArtUrl = imgUrl },
new Album { Title = "Greatest Hits", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Lenny Kravitz"], AlbumArtUrl = imgUrl },
new Album { Title = "Greatest Hits", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Lenny Kravitz"], AlbumArtUrl = imgUrl },
new Album { Title = "Greatest Kiss", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Kiss"], AlbumArtUrl = imgUrl },
new Album { Title = "Greetings from Michigan", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Sufjan Stevens"], AlbumArtUrl = imgUrl },
new Album { Title = "Group Therapy", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Above & Beyond"], AlbumArtUrl = imgUrl },
new Album { Title = "Handel: The Messiah (Highlights)", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Scholars Baroque Ensemble"], AlbumArtUrl = imgUrl },
new Album { Title = "Haydn: Symphonies 99 - 104", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Royal Philharmonic Orchestra"], AlbumArtUrl = imgUrl },
new Album { Title = "Heart of the Night", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Spyro Gyra"], AlbumArtUrl = imgUrl },
new Album { Title = "Heart On", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Eagles of Death Metal"], AlbumArtUrl = imgUrl },
new Album { Title = "Holy Diver", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Dio"], AlbumArtUrl = imgUrl },
new Album { Title = "Homework", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Daft Punk"], AlbumArtUrl = imgUrl },
new Album { Title = "Hot Rocks, 1964-1971 (Disc 1)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Rolling Stones"], AlbumArtUrl = imgUrl },
new Album { Title = "Houses Of The Holy", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "How To Dismantle An Atomic Bomb", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "Human", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Projected"], AlbumArtUrl = imgUrl },
new Album { Title = "Hunky Dory", Genre = genres["Rock"], Price = 8.99M, Artist = artists["David Bowie"], AlbumArtUrl = imgUrl },
new Album { Title = "Hymns", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Projected"], AlbumArtUrl = imgUrl },
new Album { Title = "Hysteria", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Def Leppard"], AlbumArtUrl = imgUrl },
new Album { Title = "In Absentia", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Porcupine Tree"], AlbumArtUrl = imgUrl },
new Album { Title = "In Between", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Paul Van Dyk"], AlbumArtUrl = imgUrl },
new Album { Title = "In Rainbows", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Radiohead"], AlbumArtUrl = imgUrl },
new Album { Title = "In Step", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Stevie Ray Vaughan & Double Trouble"], AlbumArtUrl = imgUrl },
new Album { Title = "In the court of the Crimson King", Genre = genres["Rock"], Price = 8.99M, Artist = artists["King Crimson"], AlbumArtUrl = imgUrl },
new Album { Title = "In Through The Out Door", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "In Your Honor [Disc 1]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Foo Fighters"], AlbumArtUrl = imgUrl },
new Album { Title = "In Your Honor [Disc 2]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Foo Fighters"], AlbumArtUrl = imgUrl },
new Album { Title = "Indestructible", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Rancid"], AlbumArtUrl = imgUrl },
new Album { Title = "Infinity", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Journey"], AlbumArtUrl = imgUrl },
new Album { Title = "Into The Light", Genre = genres["Rock"], Price = 8.99M, Artist = artists["David Coverdale"], AlbumArtUrl = imgUrl },
new Album { Title = "Introspective", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Pet Shop Boys"], AlbumArtUrl = imgUrl },
new Album { Title = "Iron Maiden", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "ISAM", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Amon Tobin"], AlbumArtUrl = imgUrl },
new Album { Title = "IV", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Jagged Little Pill", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Alanis Morissette"], AlbumArtUrl = imgUrl },
new Album { Title = "Jagged Little Pill", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alanis Morissette"], AlbumArtUrl = imgUrl },
new Album { Title = "Jorge Ben Jor 25 Anos", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Jorge Ben"], AlbumArtUrl = imgUrl },
new Album { Title = "Jota Quest-1995", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Jota Quest"], AlbumArtUrl = imgUrl },
new Album { Title = "Kick", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["INXS"], AlbumArtUrl = imgUrl },
new Album { Title = "Kill 'Em All", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Kind of Blue", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
new Album { Title = "King For A Day Fool For A Lifetime", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Faith No More"], AlbumArtUrl = imgUrl },
new Album { Title = "Kiss", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Carly Rae Jepsen"], AlbumArtUrl = imgUrl },
new Album { Title = "Last Call", Genre = genres["Country"], Price = 8.99M, Artist = artists["Cayouche"], AlbumArtUrl = imgUrl },
new Album { Title = "Le Freak", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Chic"], AlbumArtUrl = imgUrl },
new Album { Title = "Le Tigre", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Le Tigre"], AlbumArtUrl = imgUrl },
new Album { Title = "Led Zeppelin I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Led Zeppelin II", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Led Zeppelin III", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Let There Be Rock", Genre = genres["Rock"], Price = 8.99M, Artist = artists["AC/DC"], AlbumArtUrl = imgUrl },
new Album { Title = "Little Earthquakes", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Tori Amos"], AlbumArtUrl = imgUrl },
new Album { Title = "Live [Disc 1]", Genre = genres["Blues"], Price = 8.99M, Artist = artists["The Black Crowes"], AlbumArtUrl = imgUrl },
new Album { Title = "Live [Disc 2]", Genre = genres["Blues"], Price = 8.99M, Artist = artists["The Black Crowes"], AlbumArtUrl = imgUrl },
new Album { Title = "Live After Death", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Live At Donington 1992 (Disc 1)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Live At Donington 1992 (Disc 2)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Live on Earth", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["The Cat Empire"], AlbumArtUrl = imgUrl },
new Album { Title = "Live On Two Legs [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
new Album { Title = "Living After Midnight", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Judas Priest"], AlbumArtUrl = imgUrl },
new Album { Title = "Living", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Paddy Casey"], AlbumArtUrl = imgUrl },
new Album { Title = "Load", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Love Changes Everything", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "MacArthur Park Suite", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Donna Summer"], AlbumArtUrl = imgUrl },
new Album { Title = "Machine Head", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Magical Mystery Tour", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Beatles"], AlbumArtUrl = imgUrl },
new Album { Title = "Mais Do Mesmo", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Legião Urbana"], AlbumArtUrl = imgUrl },
new Album { Title = "Maquinarama", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Skank"], AlbumArtUrl = imgUrl },
new Album { Title = "Marasim", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Jagjit Singh"], AlbumArtUrl = imgUrl },
new Album { Title = "Mascagni: Cavalleria Rusticana", Genre = genres["Classical"], Price = 8.99M, Artist = artists["James Levine"], AlbumArtUrl = imgUrl },
new Album { Title = "Master of Puppets", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Mechanics & Mathematics", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Venus Hum"], AlbumArtUrl = imgUrl },
new Album { Title = "Mental Jewelry", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Live"], AlbumArtUrl = imgUrl },
new Album { Title = "Metallics", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "meteora", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Linkin Park"], AlbumArtUrl = imgUrl },
new Album { Title = "Meus Momentos", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Gonzaguinha"], AlbumArtUrl = imgUrl },
new Album { Title = "Mezmerize", Genre = genres["Metal"], Price = 8.99M, Artist = artists["System Of A Down"], AlbumArtUrl = imgUrl },
new Album { Title = "Mezzanine", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Massive Attack"], AlbumArtUrl = imgUrl },
new Album { Title = "Miles Ahead", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
new Album { Title = "Milton Nascimento Ao Vivo", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Milton Nascimento"], AlbumArtUrl = imgUrl },
new Album { Title = "Minas", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Milton Nascimento"], AlbumArtUrl = imgUrl },
new Album { Title = "Minha Historia", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Chico Buarque"], AlbumArtUrl = imgUrl },
new Album { Title = "Misplaced Childhood", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Marillion"], AlbumArtUrl = imgUrl },
new Album { Title = "MK III The Final Concerts [Disc 1]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Morning Dance", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Spyro Gyra"], AlbumArtUrl = imgUrl },
new Album { Title = "Motley Crue Greatest Hits", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Mötley Crüe"], AlbumArtUrl = imgUrl },
new Album { Title = "Moving Pictures", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Rush"], AlbumArtUrl = imgUrl },
new Album { Title = "Mozart: Chamber Music", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Nash Ensemble"], AlbumArtUrl = imgUrl },
new Album { Title = "Mozart: Symphonies Nos. 40 & 41", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Berliner Philharmoniker"], AlbumArtUrl = imgUrl },
new Album { Title = "Murder Ballads", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Nick Cave and the Bad Seeds"], AlbumArtUrl = imgUrl },
new Album { Title = "Music For The Jilted Generation", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["The Prodigy"], AlbumArtUrl = imgUrl },
new Album { Title = "My Generation - The Very Best Of The Who", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Who"], AlbumArtUrl = imgUrl },
new Album { Title = "My Name is Skrillex", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Skrillex"], AlbumArtUrl = imgUrl },
new Album { Title = "Na Pista", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Cláudio Zoli"], AlbumArtUrl = imgUrl },
new Album { Title = "Nevermind", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Nirvana"], AlbumArtUrl = imgUrl },
new Album { Title = "New Adventures In Hi-Fi", Genre = genres["Rock"], Price = 8.99M, Artist = artists["R.E.M."], AlbumArtUrl = imgUrl },
new Album { Title = "New Divide", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Linkin Park"], AlbumArtUrl = imgUrl },
new Album { Title = "New York Dolls", Genre = genres["Punk"], Price = 8.99M, Artist = artists["New York Dolls"], AlbumArtUrl = imgUrl },
new Album { Title = "News Of The World", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
new Album { Title = "Nielsen: The Six Symphonies", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Göteborgs Symfoniker & Neeme Järvi"], AlbumArtUrl = imgUrl },
new Album { Title = "Night At The Opera", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
new Album { Title = "Night Castle", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Trans-Siberian Orchestra"], AlbumArtUrl = imgUrl },
new Album { Title = "Nkolo", Genre = genres["World"], Price = 8.99M, Artist = artists["Lokua Kanza"], AlbumArtUrl = imgUrl },
new Album { Title = "No More Tears (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
new Album { Title = "No Prayer For The Dying", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "No Security", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Rolling Stones"], AlbumArtUrl = imgUrl },
new Album { Title = "O Brother, Where Art Thou?", Genre = genres["Country"], Price = 8.99M, Artist = artists["Alison Krauss"], AlbumArtUrl = imgUrl },
new Album { Title = "O Samba Poconé", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Skank"], AlbumArtUrl = imgUrl },
new Album { Title = "O(+>", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Prince"], AlbumArtUrl = imgUrl },
new Album { Title = "Oceania", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Smashing Pumpkins"], AlbumArtUrl = imgUrl },
new Album { Title = "Off the Deep End", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Weird Al"], AlbumArtUrl = imgUrl },
new Album { Title = "OK Computer", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Radiohead"], AlbumArtUrl = imgUrl },
new Album { Title = "Olodum", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Olodum"], AlbumArtUrl = imgUrl },
new Album { Title = "One Love", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["David Guetta"], AlbumArtUrl = imgUrl },
new Album { Title = "Operation: Mindcrime", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Queensrÿche"], AlbumArtUrl = imgUrl },
new Album { Title = "Opiate", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
new Album { Title = "Outbreak", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Dennis Chambers"], AlbumArtUrl = imgUrl },
new Album { Title = "Pachelbel: Canon & Gigue", Genre = genres["Classical"], Price = 8.99M, Artist = artists["English Concert & Trevor Pinnock"], AlbumArtUrl = imgUrl },
new Album { Title = "Paid in Full", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Eric B. and Rakim"], AlbumArtUrl = imgUrl },
new Album { Title = "Para Siempre", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Vicente Fernandez"], AlbumArtUrl = imgUrl },
new Album { Title = "Pause", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Four Tet"], AlbumArtUrl = imgUrl },
new Album { Title = "Peace Sells... but Who's Buying", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Megadeth"], AlbumArtUrl = imgUrl },
new Album { Title = "Physical Graffiti [Disc 1]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Physical Graffiti [Disc 2]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Physical Graffiti", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Piece Of Mind", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Pinkerton", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Weezer"], AlbumArtUrl = imgUrl },
new Album { Title = "Plays Metallica By Four Cellos", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Apocalyptica"], AlbumArtUrl = imgUrl },
new Album { Title = "Pop", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "Powerslave", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Prenda Minha", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Caetano Veloso"], AlbumArtUrl = imgUrl },
new Album { Title = "Presence", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Pretty Hate Machine", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Nine Inch Nails"], AlbumArtUrl = imgUrl },
new Album { Title = "Prisoner", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Jezabels"], AlbumArtUrl = imgUrl },
new Album { Title = "Privateering", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Mark Knopfler"], AlbumArtUrl = imgUrl },
new Album { Title = "Prokofiev: Romeo & Juliet", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Michael Tilson Thomas"], AlbumArtUrl = imgUrl },
new Album { Title = "Prokofiev: Symphony No.1", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sergei Prokofiev & Yuri Temirkanov"], AlbumArtUrl = imgUrl },
new Album { Title = "PSY's Best 6th Part 1", Genre = genres["Pop"], Price = 8.99M, Artist = artists["PSY"], AlbumArtUrl = imgUrl },
new Album { Title = "Purcell: The Fairy Queen", Genre = genres["Classical"], Price = 8.99M, Artist = artists["London Classical Players"], AlbumArtUrl = imgUrl },
new Album { Title = "Purpendicular", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Purple", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Stone Temple Pilots"], AlbumArtUrl = imgUrl },
new Album { Title = "Quanta Gente Veio Ver (Live)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Gilberto Gil"], AlbumArtUrl = imgUrl },
new Album { Title = "Quanta Gente Veio ver--Bônus De Carnaval", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Gilberto Gil"], AlbumArtUrl = imgUrl },
new Album { Title = "Quiet Songs", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Aisha Duo"], AlbumArtUrl = imgUrl },
new Album { Title = "Raices", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Los Tigres del Norte"], AlbumArtUrl = imgUrl },
new Album { Title = "Raising Hell", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Run DMC"], AlbumArtUrl = imgUrl },
new Album { Title = "Raoul and the Kings of Spain ", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tears For Fears"], AlbumArtUrl = imgUrl },
new Album { Title = "Rattle And Hum", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "Raul Seixas", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Raul Seixas"], AlbumArtUrl = imgUrl },
new Album { Title = "Recovery [Explicit]", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Eminem"], AlbumArtUrl = imgUrl },
new Album { Title = "Reign In Blood", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Slayer"], AlbumArtUrl = imgUrl },
new Album { Title = "Relayed", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Yes"], AlbumArtUrl = imgUrl },
new Album { Title = "ReLoad", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Respighi:Pines of Rome", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Eugene Ormandy"], AlbumArtUrl = imgUrl },
new Album { Title = "Restless and Wild", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Accept"], AlbumArtUrl = imgUrl },
new Album { Title = "Retrospective I (1974-1980)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Rush"], AlbumArtUrl = imgUrl },
new Album { Title = "Revelations", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Audioslave"], AlbumArtUrl = imgUrl },
new Album { Title = "Revolver", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Beatles"], AlbumArtUrl = imgUrl },
new Album { Title = "Ride the Lighting ", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Ride The Lightning", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Ring My Bell", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Anita Ward"], AlbumArtUrl = imgUrl },
new Album { Title = "Riot Act", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
new Album { Title = "Rise of the Phoenix", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Before the Dawn"], AlbumArtUrl = imgUrl },
new Album { Title = "Rock In Rio [CD1]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Rock In Rio [CD2]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Rock In Rio [CD2]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Roda De Funk", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Funk Como Le Gusta"], AlbumArtUrl = imgUrl },
new Album { Title = "Room for Squares", Genre = genres["Pop"], Price = 8.99M, Artist = artists["John Mayer"], AlbumArtUrl = imgUrl },
new Album { Title = "Root Down", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Jimmy Smith"], AlbumArtUrl = imgUrl },
new Album { Title = "Rounds", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Four Tet"], AlbumArtUrl = imgUrl },
new Album { Title = "Rubber Factory", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Black Keys"], AlbumArtUrl = imgUrl },
new Album { Title = "Rust in Peace", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Megadeth"], AlbumArtUrl = imgUrl },
new Album { Title = "Sambas De Enredo 2001", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Various Artists"], AlbumArtUrl = imgUrl },
new Album { Title = "Santana - As Years Go By", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Santana"], AlbumArtUrl = imgUrl },
new Album { Title = "Santana Live", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Santana"], AlbumArtUrl = imgUrl },
new Album { Title = "Saturday Night Fever", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Bee Gees"], AlbumArtUrl = imgUrl },
new Album { Title = "Scary Monsters and Nice Sprites", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Skrillex"], AlbumArtUrl = imgUrl },
new Album { Title = "Scheherazade", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Chicago Symphony Orchestra & Fritz Reiner"], AlbumArtUrl = imgUrl },
new Album { Title = "SCRIABIN: Vers la flamme", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Christopher O'Riley"], AlbumArtUrl = imgUrl },
new Album { Title = "Second Coming", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Stone Roses"], AlbumArtUrl = imgUrl },
new Album { Title = "Serie Sem Limite (Disc 1)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Tim Maia"], AlbumArtUrl = imgUrl },
new Album { Title = "Serie Sem Limite (Disc 2)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Tim Maia"], AlbumArtUrl = imgUrl },
new Album { Title = "Serious About Men", Genre = genres["Rap"], Price = 8.99M, Artist = artists["The Rubberbandits"], AlbumArtUrl = imgUrl },
new Album { Title = "Seventh Son of a Seventh Son", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Short Bus", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Filter"], AlbumArtUrl = imgUrl },
new Album { Title = "Sibelius: Finlandia", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Berliner Philharmoniker"], AlbumArtUrl = imgUrl },
new Album { Title = "Singles Collection", Genre = genres["Rock"], Price = 8.99M, Artist = artists["David Bowie"], AlbumArtUrl = imgUrl },
new Album { Title = "Six Degrees of Inner Turbulence", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Dream Theater"], AlbumArtUrl = imgUrl },
new Album { Title = "Slave To The Empire", Genre = genres["Metal"], Price = 8.99M, Artist = artists["T&N"], AlbumArtUrl = imgUrl },
new Album { Title = "Slaves And Masters", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Slouching Towards Bethlehem", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Robert James"], AlbumArtUrl = imgUrl },
new Album { Title = "Smash", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Offspring"], AlbumArtUrl = imgUrl },
new Album { Title = "Something Special", Genre = genres["Country"], Price = 8.99M, Artist = artists["Dolly Parton"], AlbumArtUrl = imgUrl },
new Album { Title = "Somewhere in Time", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Song(s) You Know By Heart", Genre = genres["Country"], Price = 8.99M, Artist = artists["Jimmy Buffett"], AlbumArtUrl = imgUrl },
new Album { Title = "Sound of Music", Genre = genres["Punk"], Price = 8.99M, Artist = artists["Adicts"], AlbumArtUrl = imgUrl },
new Album { Title = "South American Getaway", Genre = genres["Classical"], Price = 8.99M, Artist = artists["The 12 Cellists of The Berlin Philharmonic"], AlbumArtUrl = imgUrl },
new Album { Title = "Sozinho Remix Ao Vivo", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Caetano Veloso"], AlbumArtUrl = imgUrl },
new Album { Title = "Speak of the Devil", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
new Album { Title = "Spiritual State", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Nujabes"], AlbumArtUrl = imgUrl },
new Album { Title = "St. Anger", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
new Album { Title = "Still Life", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Opeth"], AlbumArtUrl = imgUrl },
new Album { Title = "Stop Making Sense", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Talking Heads"], AlbumArtUrl = imgUrl },
new Album { Title = "Stormbringer", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "Stranger than Fiction", Genre = genres["Punk"], Price = 8.99M, Artist = artists["Bad Religion"], AlbumArtUrl = imgUrl },
new Album { Title = "Strauss: Waltzes", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Eugene Ormandy"], AlbumArtUrl = imgUrl },
new Album { Title = "Supermodified", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Amon Tobin"], AlbumArtUrl = imgUrl },
new Album { Title = "Supernatural", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Santana"], AlbumArtUrl = imgUrl },
new Album { Title = "Surfing with the Alien (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Joe Satriani"], AlbumArtUrl = imgUrl },
new Album { Title = "Switched-On Bach", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Wendy Carlos"], AlbumArtUrl = imgUrl },
new Album { Title = "Symphony", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "Szymanowski: Piano Works, Vol. 1", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Martin Roscoe"], AlbumArtUrl = imgUrl },
new Album { Title = "Tchaikovsky: The Nutcracker", Genre = genres["Classical"], Price = 8.99M, Artist = artists["London Symphony Orchestra"], AlbumArtUrl = imgUrl },
new Album { Title = "Ted Nugent", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ted Nugent"], AlbumArtUrl = imgUrl },
new Album { Title = "Teflon Don", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Rick Ross"], AlbumArtUrl = imgUrl },
new Album { Title = "Tell Another Joke at the Ol' Choppin' Block", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Danielson Famile"], AlbumArtUrl = imgUrl },
new Album { Title = "Temple of the Dog", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Temple of the Dog"], AlbumArtUrl = imgUrl },
new Album { Title = "Ten", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
new Album { Title = "Texas Flood", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Stevie Ray Vaughan"], AlbumArtUrl = imgUrl },
new Album { Title = "The Battle Rages On", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "The Beast Live", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Paul D'Ianno"], AlbumArtUrl = imgUrl },
new Album { Title = "The Best Of 1980-1990", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "The Best of 19902000", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "The Best of Beethoven", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Nicolaus Esterhazy Sinfonia"], AlbumArtUrl = imgUrl },
new Album { Title = "The Best Of Billy Cobham", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Billy Cobham"], AlbumArtUrl = imgUrl },
new Album { Title = "The Best of Ed Motta", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Ed Motta"], AlbumArtUrl = imgUrl },
new Album { Title = "The Best Of Van Halen, Vol. I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
new Album { Title = "The Bridge", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Melanie Fiona"], AlbumArtUrl = imgUrl },
new Album { Title = "The Cage", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tygers of Pan Tang"], AlbumArtUrl = imgUrl },
new Album { Title = "The Chicago Transit Authority", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Chicago "], AlbumArtUrl = imgUrl },
new Album { Title = "The Chronic", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Dr. Dre"], AlbumArtUrl = imgUrl },
new Album { Title = "The Colour And The Shape", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Foo Fighters"], AlbumArtUrl = imgUrl },
new Album { Title = "The Crane Wife", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["The Decemberists"], AlbumArtUrl = imgUrl },
new Album { Title = "The Cream Of Clapton", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Eric Clapton"], AlbumArtUrl = imgUrl },
new Album { Title = "The Cure", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Cure"], AlbumArtUrl = imgUrl },
new Album { Title = "The Dark Side Of The Moon", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
new Album { Title = "The Divine Conspiracy", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Epica"], AlbumArtUrl = imgUrl },
new Album { Title = "The Doors", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Doors"], AlbumArtUrl = imgUrl },
new Album { Title = "The Dream of the Blue Turtles", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Sting"], AlbumArtUrl = imgUrl },
new Album { Title = "The Essential Miles Davis [Disc 1]", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
new Album { Title = "The Essential Miles Davis [Disc 2]", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
new Album { Title = "The Final Concerts (Disc 2)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
new Album { Title = "The Final Frontier", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "The Head and the Heart", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Head and the Heart"], AlbumArtUrl = imgUrl },
new Album { Title = "The Joshua Tree", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "The Last Night of the Proms", Genre = genres["Classical"], Price = 8.99M, Artist = artists["BBC Concert Orchestra"], AlbumArtUrl = imgUrl },
new Album { Title = "The Lumineers", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Lumineers"], AlbumArtUrl = imgUrl },
new Album { Title = "The Number of The Beast", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "The Number of The Beast", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "The Police Greatest Hits", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Police"], AlbumArtUrl = imgUrl },
new Album { Title = "The Song Remains The Same (Disc 1)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "The Song Remains The Same (Disc 2)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "The Southern Harmony and Musical Companion", Genre = genres["Blues"], Price = 8.99M, Artist = artists["The Black Crowes"], AlbumArtUrl = imgUrl },
new Album { Title = "The Spade", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Butch Walker & The Black Widows"], AlbumArtUrl = imgUrl },
new Album { Title = "The Stone Roses", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Stone Roses"], AlbumArtUrl = imgUrl },
new Album { Title = "The Suburbs", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Arcade Fire"], AlbumArtUrl = imgUrl },
new Album { Title = "The Three Tenors Disc1/Disc2", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Carreras, Pavarotti, Domingo"], AlbumArtUrl = imgUrl },
new Album { Title = "The Trees They Grow So High", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "The Wall", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
new Album { Title = "The X Factor", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Them Crooked Vultures", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Them Crooked Vultures"], AlbumArtUrl = imgUrl },
new Album { Title = "This Is Happening", Genre = genres["Rock"], Price = 8.99M, Artist = artists["LCD Soundsystem"], AlbumArtUrl = imgUrl },
new Album { Title = "Thunder, Lightning, Strike", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Go! Team"], AlbumArtUrl = imgUrl },
new Album { Title = "Time to Say Goodbye", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
new Album { Title = "Time, Love & Tenderness", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Michael Bolton"], AlbumArtUrl = imgUrl },
new Album { Title = "Tomorrow Starts Today", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Mobile"], AlbumArtUrl = imgUrl },
new Album { Title = "Tribute", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
new Album { Title = "Tuesday Night Music Club", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Sheryl Crow"], AlbumArtUrl = imgUrl },
new Album { Title = "Umoja", Genre = genres["Rock"], Price = 8.99M, Artist = artists["BLØF"], AlbumArtUrl = imgUrl },
new Album { Title = "Under the Pink", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Tori Amos"], AlbumArtUrl = imgUrl },
new Album { Title = "Undertow", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
new Album { Title = "Un-Led-Ed", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Dread Zeppelin"], AlbumArtUrl = imgUrl },
new Album { Title = "Unplugged [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Kiss"], AlbumArtUrl = imgUrl },
new Album { Title = "Unplugged", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Eric Clapton"], AlbumArtUrl = imgUrl },
new Album { Title = "Unplugged", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Eric Clapton"], AlbumArtUrl = imgUrl },
new Album { Title = "Untrue", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Burial"], AlbumArtUrl = imgUrl },
new Album { Title = "Use Your Illusion I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
new Album { Title = "Use Your Illusion II", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
new Album { Title = "Use Your Illusion II", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
new Album { Title = "Van Halen III", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
new Album { Title = "Van Halen", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
new Album { Title = "Version 2.0", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Garbage"], AlbumArtUrl = imgUrl },
new Album { Title = "Vinicius De Moraes", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Vinícius De Moraes"], AlbumArtUrl = imgUrl },
new Album { Title = "Virtual XI", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
new Album { Title = "Voodoo Lounge", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Rolling Stones"], AlbumArtUrl = imgUrl },
new Album { Title = "Vozes do MPB", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Various Artists"], AlbumArtUrl = imgUrl },
new Album { Title = "Vs.", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
new Album { Title = "Wagner: Favourite Overtures", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sir Georg Solti & Wiener Philharmoniker"], AlbumArtUrl = imgUrl },
new Album { Title = "Walking Into Clarksdale", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Page & Plant"], AlbumArtUrl = imgUrl },
new Album { Title = "Wapi Yo", Genre = genres["World"], Price = 8.99M, Artist = artists["Lokua Kanza"], AlbumArtUrl = imgUrl },
new Album { Title = "War", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "Warner 25 Anos", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Antônio Carlos Jobim"], AlbumArtUrl = imgUrl },
new Album { Title = "Wasteland R&Btheque", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Raunchy"], AlbumArtUrl = imgUrl },
new Album { Title = "Watermark", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Enya"], AlbumArtUrl = imgUrl },
new Album { Title = "We Were Exploding Anyway", Genre = genres["Rock"], Price = 8.99M, Artist = artists["65daysofstatic"], AlbumArtUrl = imgUrl },
new Album { Title = "Weill: The Seven Deadly Sins", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Orchestre de l'Opéra de Lyon"], AlbumArtUrl = imgUrl },
new Album { Title = "White Pony", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deftones"], AlbumArtUrl = imgUrl },
new Album { Title = "Who's Next", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Who"], AlbumArtUrl = imgUrl },
new Album { Title = "Wish You Were Here", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
new Album { Title = "With Oden on Our Side", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Amon Amarth"], AlbumArtUrl = imgUrl },
new Album { Title = "Worlds", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Aaron Goldberg"], AlbumArtUrl = imgUrl },
new Album { Title = "Worship Music", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Anthrax"], AlbumArtUrl = imgUrl },
new Album { Title = "X&Y", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Coldplay"], AlbumArtUrl = imgUrl },
new Album { Title = "Xinti", Genre = genres["World"], Price = 8.99M, Artist = artists["Sara Tavares"], AlbumArtUrl = imgUrl },
new Album { Title = "Yano", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Yano"], AlbumArtUrl = imgUrl },
new Album { Title = "Yesterday Once More Disc 1/Disc 2", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Carpenters"], AlbumArtUrl = imgUrl },
new Album { Title = "Zooropa", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
new Album { Title = "Zoso", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
};
foreach (var album in albums)
{
album.ArtistId = album.Artist.ArtistId;
album.GenreId = album.Genre.GenreId;
}
return albums;
}
private static Dictionary<string, Artist> artists;
public static Dictionary<string, Artist> Artists
{
get
{
if (artists == null)
{
var artistsList = new Artist[]
{
new Artist { Name = "65daysofstatic" },
new Artist { Name = "Aaron Goldberg" },
new Artist { Name = "Above & Beyond" },
new Artist { Name = "Above the Fold" },
new Artist { Name = "AC/DC" },
new Artist { Name = "Accept" },
new Artist { Name = "Adicts" },
new Artist { Name = "Adrian Leaper & Doreen de Feis" },
new Artist { Name = "Aerosmith" },
new Artist { Name = "Aisha Duo" },
new Artist { Name = "Al di Meola" },
new Artist { Name = "Alabama Shakes" },
new Artist { Name = "Alanis Morissette" },
new Artist { Name = "Alberto Turco & Nova Schola Gregoriana" },
new Artist { Name = "Alice in Chains" },
new Artist { Name = "Alison Krauss" },
new Artist { Name = "Amon Amarth" },
new Artist { Name = "Amon Tobin" },
new Artist { Name = "Amr Diab" },
new Artist { Name = "Amy Winehouse" },
new Artist { Name = "Anita Ward" },
new Artist { Name = "Anthrax" },
new Artist { Name = "Antônio Carlos Jobim" },
new Artist { Name = "Apocalyptica" },
new Artist { Name = "Aqua" },
new Artist { Name = "Armand Van Helden" },
new Artist { Name = "Arcade Fire" },
new Artist { Name = "Audioslave" },
new Artist { Name = "Bad Religion" },
new Artist { Name = "Barenaked Ladies" },
new Artist { Name = "BBC Concert Orchestra" },
new Artist { Name = "Bee Gees" },
new Artist { Name = "Before the Dawn" },
new Artist { Name = "Berliner Philharmoniker" },
new Artist { Name = "Billy Cobham" },
new Artist { Name = "Black Label Society" },
new Artist { Name = "Black Sabbath" },
new Artist { Name = "BLØF" },
new Artist { Name = "Blues Traveler" },
new Artist { Name = "Boston Symphony Orchestra & Seiji Ozawa" },
new Artist { Name = "Britten Sinfonia, Ivor Bolton & Lesley Garrett" },
new Artist { Name = "Bruce Dickinson" },
new Artist { Name = "Buddy Guy" },
new Artist { Name = "Burial" },
new Artist { Name = "Butch Walker & The Black Widows" },
new Artist { Name = "Caetano Veloso" },
new Artist { Name = "Cake" },
new Artist { Name = "Calexico" },
new Artist { Name = "Carly Rae Jepsen" },
new Artist { Name = "Carreras, Pavarotti, Domingo" },
new Artist { Name = "Cássia Eller" },
new Artist { Name = "Cayouche" },
new Artist { Name = "Chic" },
new Artist { Name = "Chicago " },
new Artist { Name = "Chicago Symphony Orchestra & Fritz Reiner" },
new Artist { Name = "Chico Buarque" },
new Artist { Name = "Chico Science & Nação Zumbi" },
new Artist { Name = "Choir Of Westminster Abbey & Simon Preston" },
new Artist { Name = "Chris Cornell" },
new Artist { Name = "Christopher O'Riley" },
new Artist { Name = "Cidade Negra" },
new Artist { Name = "Cláudio Zoli" },
new Artist { Name = "Coldplay" },
new Artist { Name = "Creedence Clearwater Revival" },
new Artist { Name = "Crosby, Stills, Nash, and Young" },
new Artist { Name = "Daft Punk" },
new Artist { Name = "Danielson Famile" },
new Artist { Name = "David Bowie" },
new Artist { Name = "David Coverdale" },
new Artist { Name = "David Guetta" },
new Artist { Name = "deadmau5" },
new Artist { Name = "Deep Purple" },
new Artist { Name = "Def Leppard" },
new Artist { Name = "Deftones" },
new Artist { Name = "Dennis Chambers" },
new Artist { Name = "Deva Premal" },
new Artist { Name = "Dio" },
new Artist { Name = "Djavan" },
new Artist { Name = "Dolly Parton" },
new Artist { Name = "Donna Summer" },
new Artist { Name = "Dr. Dre" },
new Artist { Name = "Dread Zeppelin" },
new Artist { Name = "Dream Theater" },
new Artist { Name = "Duck Sauce" },
new Artist { Name = "Earl Scruggs" },
new Artist { Name = "Ed Motta" },
new Artist { Name = "Edo de Waart & San Francisco Symphony" },
new Artist { Name = "Elis Regina" },
new Artist { Name = "Eminem" },
new Artist { Name = "English Concert & Trevor Pinnock" },
new Artist { Name = "Enya" },
new Artist { Name = "Epica" },
new Artist { Name = "Eric B. and Rakim" },
new Artist { Name = "Eric Clapton" },
new Artist { Name = "Eugene Ormandy" },
new Artist { Name = "Faith No More" },
new Artist { Name = "Falamansa" },
new Artist { Name = "Filter" },
new Artist { Name = "Foo Fighters" },
new Artist { Name = "Four Tet" },
new Artist { Name = "Frank Zappa & Captain Beefheart" },
new Artist { Name = "Fretwork" },
new Artist { Name = "Funk Como Le Gusta" },
new Artist { Name = "Garbage" },
new Artist { Name = "Gerald Moore" },
new Artist { Name = "Gilberto Gil" },
new Artist { Name = "Godsmack" },
new Artist { Name = "Gonzaguinha" },
new Artist { Name = "Göteborgs Symfoniker & Neeme Järvi" },
new Artist { Name = "Guns N' Roses" },
new Artist { Name = "Gustav Mahler" },
new Artist { Name = "In This Moment" },
new Artist { Name = "Incognito" },
new Artist { Name = "INXS" },
new Artist { Name = "Iron Maiden" },
new Artist { Name = "Jagjit Singh" },
new Artist { Name = "James Levine" },
new Artist { Name = "Jamiroquai" },
new Artist { Name = "Jimi Hendrix" },
new Artist { Name = "Jimmy Buffett" },
new Artist { Name = "Jimmy Smith" },
new Artist { Name = "Joe Satriani" },
new Artist { Name = "John Digweed" },
new Artist { Name = "John Mayer" },
new Artist { Name = "Jorge Ben" },
new Artist { Name = "Jota Quest" },
new Artist { Name = "Journey" },
new Artist { Name = "Judas Priest" },
new Artist { Name = "Julian Bream" },
new Artist { Name = "Justice" },
new Artist { Name = "Orchestre de l'Opéra de Lyon" },
new Artist { Name = "King Crimson" },
new Artist { Name = "Kiss" },
new Artist { Name = "LCD Soundsystem" },
new Artist { Name = "Le Tigre" },
new Artist { Name = "Led Zeppelin" },
new Artist { Name = "Legião Urbana" },
new Artist { Name = "Lenny Kravitz" },
new Artist { Name = "Les Arts Florissants & William Christie" },
new Artist { Name = "Limp Bizkit" },
new Artist { Name = "Linkin Park" },
new Artist { Name = "Live" },
new Artist { Name = "Lokua Kanza" },
new Artist { Name = "London Symphony Orchestra" },
new Artist { Name = "Los Tigres del Norte" },
new Artist { Name = "Luciana Souza/Romero Lubambo" },
new Artist { Name = "Lulu Santos" },
new Artist { Name = "Lura" },
new Artist { Name = "Marcos Valle" },
new Artist { Name = "Marillion" },
new Artist { Name = "Marisa Monte" },
new Artist { Name = "Mark Knopfler" },
new Artist { Name = "Martin Roscoe" },
new Artist { Name = "Massive Attack" },
new Artist { Name = "Maurizio Pollini" },
new Artist { Name = "Megadeth" },
new Artist { Name = "Mela Tenenbaum, Pro Musica Prague & Richard Kapp" },
new Artist { Name = "Melanie Fiona" },
new Artist { Name = "Men At Work" },
new Artist { Name = "Metallica" },
new Artist { Name = "M-Flo" },
new Artist { Name = "Michael Bolton" },
new Artist { Name = "Michael Tilson Thomas" },
new Artist { Name = "Miles Davis" },
new Artist { Name = "Milton Nascimento" },
new Artist { Name = "Mobile" },
new Artist { Name = "Modest Mouse" },
new Artist { Name = "Mötley Crüe" },
new Artist { Name = "Motörhead" },
new Artist { Name = "Mumford & Sons" },
new Artist { Name = "Munkle" },
new Artist { Name = "Nash Ensemble" },
new Artist { Name = "Neil Young" },
new Artist { Name = "New York Dolls" },
new Artist { Name = "Nick Cave and the Bad Seeds" },
new Artist { Name = "Nicolaus Esterhazy Sinfonia" },
new Artist { Name = "Nine Inch Nails" },
new Artist { Name = "Nirvana" },
new Artist { Name = "Norah Jones" },
new Artist { Name = "Nujabes" },
new Artist { Name = "O Terço" },
new Artist { Name = "Oasis" },
new Artist { Name = "Olodum" },
new Artist { Name = "Opeth" },
new Artist { Name = "Orchestra of The Age of Enlightenment" },
new Artist { Name = "Os Paralamas Do Sucesso" },
new Artist { Name = "Ozzy Osbourne" },
new Artist { Name = "Paddy Casey" },
new Artist { Name = "Page & Plant" },
new Artist { Name = "Papa Wemba" },
new Artist { Name = "Paul D'Ianno" },
new Artist { Name = "Paul Oakenfold" },
new Artist { Name = "Paul Van Dyk" },
new Artist { Name = "Pearl Jam" },
new Artist { Name = "Pet Shop Boys" },
new Artist { Name = "Pink Floyd" },
new Artist { Name = "Plug" },
new Artist { Name = "Porcupine Tree" },
new Artist { Name = "Portishead" },
new Artist { Name = "Prince" },
new Artist { Name = "Projected" },
new Artist { Name = "PSY" },
new Artist { Name = "Public Enemy" },
new Artist { Name = "Queen" },
new Artist { Name = "Queensrÿche" },
new Artist { Name = "R.E.M." },
new Artist { Name = "Radiohead" },
new Artist { Name = "Rancid" },
new Artist { Name = "Raul Seixas" },
new Artist { Name = "Raunchy" },
new Artist { Name = "Red Hot Chili Peppers" },
new Artist { Name = "Rick Ross" },
new Artist { Name = "Robert James" },
new Artist { Name = "London Classical Players" },
new Artist { Name = "Royal Philharmonic Orchestra" },
new Artist { Name = "Run DMC" },
new Artist { Name = "Rush" },
new Artist { Name = "Santana" },
new Artist { Name = "Sara Tavares" },
new Artist { Name = "Sarah Brightman" },
new Artist { Name = "Sasha" },
new Artist { Name = "Scholars Baroque Ensemble" },
new Artist { Name = "Scorpions" },
new Artist { Name = "Sergei Prokofiev & Yuri Temirkanov" },
new Artist { Name = "Sheryl Crow" },
new Artist { Name = "Sir Georg Solti & Wiener Philharmoniker" },
new Artist { Name = "Skank" },
new Artist { Name = "Skrillex" },
new Artist { Name = "Slash" },
new Artist { Name = "Slayer" },
new Artist { Name = "Soul-Junk" },
new Artist { Name = "Soundgarden" },
new Artist { Name = "Spyro Gyra" },
new Artist { Name = "Stevie Ray Vaughan & Double Trouble" },
new Artist { Name = "Stevie Ray Vaughan" },
new Artist { Name = "Sting" },
new Artist { Name = "Stone Temple Pilots" },
new Artist { Name = "Styx" },
new Artist { Name = "Sufjan Stevens" },
new Artist { Name = "Supreme Beings of Leisure" },
new Artist { Name = "System Of A Down" },
new Artist { Name = "T&N" },
new Artist { Name = "Talking Heads" },
new Artist { Name = "Tears For Fears" },
new Artist { Name = "Ted Nugent" },
new Artist { Name = "Temple of the Dog" },
new Artist { Name = "Terry Bozzio, Tony Levin & Steve Stevens" },
new Artist { Name = "The 12 Cellists of The Berlin Philharmonic" },
new Artist { Name = "The Axis of Awesome" },
new Artist { Name = "The Beatles" },
new Artist { Name = "The Black Crowes" },
new Artist { Name = "The Black Keys" },
new Artist { Name = "The Carpenters" },
new Artist { Name = "The Cat Empire" },
new Artist { Name = "The Cult" },
new Artist { Name = "The Cure" },
new Artist { Name = "The Decemberists" },
new Artist { Name = "The Doors" },
new Artist { Name = "The Eagles of Death Metal" },
new Artist { Name = "The Go! Team" },
new Artist { Name = "The Head and the Heart" },
new Artist { Name = "The Jezabels" },
new Artist { Name = "The King's Singers" },
new Artist { Name = "The Lumineers" },
new Artist { Name = "The Offspring" },
new Artist { Name = "The Police" },
new Artist { Name = "The Posies" },
new Artist { Name = "The Prodigy" },
new Artist { Name = "The Rolling Stones" },
new Artist { Name = "The Rubberbandits" },
new Artist { Name = "The Smashing Pumpkins" },
new Artist { Name = "The Stone Roses" },
new Artist { Name = "The Who" },
new Artist { Name = "Them Crooked Vultures" },
new Artist { Name = "TheStart" },
new Artist { Name = "Thievery Corporation" },
new Artist { Name = "Tiësto" },
new Artist { Name = "Tim Maia" },
new Artist { Name = "Ton Koopman" },
new Artist { Name = "Tool" },
new Artist { Name = "Tori Amos" },
new Artist { Name = "Trampled By Turtles" },
new Artist { Name = "Trans-Siberian Orchestra" },
new Artist { Name = "Tygers of Pan Tang" },
new Artist { Name = "U2" },
new Artist { Name = "UB40" },
new Artist { Name = "Uh Huh Her " },
new Artist { Name = "Van Halen" },
new Artist { Name = "Various Artists" },
new Artist { Name = "Velvet Revolver" },
new Artist { Name = "Venus Hum" },
new Artist { Name = "Vicente Fernandez" },
new Artist { Name = "Vinícius De Moraes" },
new Artist { Name = "Weezer" },
new Artist { Name = "Weird Al" },
new Artist { Name = "Wendy Carlos" },
new Artist { Name = "Wilhelm Kempff" },
new Artist { Name = "Yano" },
new Artist { Name = "Yehudi Menuhin" },
new Artist { Name = "Yes" },
new Artist { Name = "Yo-Yo Ma" },
new Artist { Name = "Zeca Pagodinho" },
new Artist { Name = "אריק אינשטיין"}
};
// TODO [EF] Swap to store generated keys when available
int artistId = 1;
artists = new Dictionary<string, Artist>();
foreach (Artist artist in artistsList)
{
artist.ArtistId = artistId++;
artists.Add(artist.Name, artist);
}
}
return artists;
}
}
private static Dictionary<string, Genre> genres;
public static Dictionary<string, Genre> Genres
{
get
{
if (genres == null)
{
var genresList = new Genre[]
{
new Genre { Name = "Pop" },
new Genre { Name = "Rock" },
new Genre { Name = "Jazz" },
new Genre { Name = "Metal" },
new Genre { Name = "Electronic" },
new Genre { Name = "Blues" },
new Genre { Name = "Latin" },
new Genre { Name = "Rap" },
new Genre { Name = "Classical" },
new Genre { Name = "Alternative" },
new Genre { Name = "Country" },
new Genre { Name = "R&B" },
new Genre { Name = "Indie" },
new Genre { Name = "Punk" },
new Genre { Name = "World" }
};
genres = new Dictionary<string, Genre>();
// TODO [EF] Swap to store generated keys when available
int genreId = 1;
foreach (Genre genre in genresList)
{
genre.GenreId = genreId++;
// TODO [EF] Remove when null values are supported by update pipeline
genre.Description = genre.Name + " is great music (if you like it).";
genres.Add(genre.Name, genre);
}
}
return genres;
}
}
}
}

View File

@@ -1,38 +0,0 @@
using System.Linq;
using System.Text.RegularExpressions;
namespace MusicStore.Models
{
// Obviously this is not a serious sentiment analyser. It is only here to provide an amusing demonstration of cross-property
// validation in AlbumsApiController.
public static class SentimentAnalysis
{
private static string[] positiveSentimentWords = new[] { "happy", "fun", "joy", "love", "delight", "bunny", "bunnies", "asp.net" };
private static string[] negativeSentimentWords = new[] { "sad", "pain", "despair", "hate", "scorn", "death", "package management" };
public static SentimentResult GetSentiment(string text) {
var numPositiveWords = CountWordOccurrences(text, positiveSentimentWords);
var numNegativeWords = CountWordOccurrences(text, negativeSentimentWords);
if (numPositiveWords > numNegativeWords) {
return SentimentResult.Positive;
} else if (numNegativeWords > numPositiveWords) {
return SentimentResult.Negative;
} else {
return SentimentResult.Neutral;
}
}
private static int CountWordOccurrences(string text, string[] words)
{
// Very simplistic matching technique for this sample. Not scalable and not really even correct.
return new Regex(string.Join("|", words), RegexOptions.IgnoreCase).Matches(text).Count;
}
public enum SentimentResult {
Negative,
Neutral,
Positive,
}
}
}

View File

@@ -1,207 +0,0 @@
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MusicStore.Models
{
public partial class ShoppingCart
{
MusicStoreContext _db;
string ShoppingCartId { get; set; }
public ShoppingCart(MusicStoreContext db)
{
_db = db;
}
public static ShoppingCart GetCart(MusicStoreContext db, HttpContext context)
{
var cart = new ShoppingCart(db);
cart.ShoppingCartId = cart.GetCartId(context);
return cart;
}
public void AddToCart(Album album)
{
// Get the matching cart and album instances
var cartItem = _db.CartItems.SingleOrDefault(
c => c.CartId == ShoppingCartId
&& c.AlbumId == album.AlbumId);
if (cartItem == null)
{
// TODO [EF] Swap to store generated key once we support identity pattern
var nextCartItemId = _db.CartItems.Any()
? _db.CartItems.Max(c => c.CartItemId) + 1
: 1;
// Create a new cart item if no cart item exists
cartItem = new CartItem
{
CartItemId = nextCartItemId,
AlbumId = album.AlbumId,
CartId = ShoppingCartId,
Count = 1,
DateCreated = DateTime.Now
};
_db.CartItems.Add(cartItem);
}
else
{
// If the item does exist in the cart, then add one to the quantity
cartItem.Count++;
// TODO [EF] Remove this line once change detection is available
_db.Update(cartItem);
}
}
public int RemoveFromCart(int id)
{
// Get the cart
var cartItem = _db.CartItems.Single(
cart => cart.CartId == ShoppingCartId
&& cart.CartItemId == id);
int itemCount = 0;
if (cartItem != null)
{
if (cartItem.Count > 1)
{
cartItem.Count--;
// TODO [EF] Remove this line once change detection is available
_db.Update(cartItem);
itemCount = cartItem.Count;
}
else
{
_db.CartItems.Remove(cartItem);
}
}
return itemCount;
}
public void EmptyCart()
{
var cartItems = _db.CartItems.Where(cart => cart.CartId == ShoppingCartId);
foreach (var cartItem in cartItems)
{
_db.Remove(cartItem);
}
}
public List<CartItem> GetCartItems()
{
var cartItems = _db.CartItems.Where(cart => cart.CartId == ShoppingCartId).ToList();
//TODO: Auto population of the related album data not available until EF feature is lighted up.
foreach (var cartItem in cartItems)
{
cartItem.Album = _db.Albums.Single(a => a.AlbumId == cartItem.AlbumId);
}
return cartItems;
}
public int GetCount()
{
// Get the count of each item in the cart and sum them up
int? count = (from cartItems in _db.CartItems
where cartItems.CartId == ShoppingCartId
select (int?)cartItems.Count).Sum();
// Return 0 if all entries are null
return count ?? 0;
}
public decimal GetTotal()
{
// Multiply album price by count of that album to get
// the current price for each of those albums in the cart
// sum all album price totals to get the cart total
// TODO Collapse to a single query once EF supports querying related data
decimal total = 0;
foreach (var item in _db.CartItems.Where(c => c.CartId == ShoppingCartId))
{
var album = _db.Albums.Single(a => a.AlbumId == item.AlbumId);
total += item.Count * album.Price;
}
return total;
}
public int CreateOrder(Order order)
{
decimal orderTotal = 0;
var cartItems = GetCartItems();
// TODO [EF] Swap to store generated identity key when supported
var nextId = _db.OrderDetails.Any()
? _db.OrderDetails.Max(o => o.OrderDetailId) + 1
: 1;
// Iterate over the items in the cart, adding the order details for each
foreach (var item in cartItems)
{
//var album = _db.Albums.Find(item.AlbumId);
var album = _db.Albums.Single(a => a.AlbumId == item.AlbumId);
var orderDetail = new OrderDetail
{
OrderDetailId = nextId,
AlbumId = item.AlbumId,
OrderId = order.OrderId,
UnitPrice = album.Price,
Quantity = item.Count,
};
// Set the order total of the shopping cart
orderTotal += (item.Count * album.Price);
_db.OrderDetails.Add(orderDetail);
nextId++;
}
// Set the order's total to the orderTotal count
order.Total = orderTotal;
// Empty the shopping cart
EmptyCart();
// Return the OrderId as the confirmation number
return order.OrderId;
}
// We're using HttpContextBase to allow access to cookies.
public string GetCartId(HttpContext context)
{
var sessionCookie = context.Request.Cookies["Session"];
string cartId = null;
if (string.IsNullOrWhiteSpace(sessionCookie))
{
//A GUID to hold the cartId.
cartId = Guid.NewGuid().ToString();
// Send cart Id as a cookie to the client.
context.Response.Cookies.Append("Session", cartId);
}
else
{
cartId = sessionCookie;
}
return cartId;
}
}
}

View File

@@ -1,17 +0,0 @@
using Microsoft.AspNetCore.Mvc;
namespace MusicStore.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Error()
{
return View("~/Views/Shared/Error.cshtml");
}
}
}

View File

@@ -1,19 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System;
using Microsoft.AspNetCore.Mvc.Filters;
namespace MusicStore.Infrastructure
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers["Cache-Control"] = "no-cache, no-store, max-age=0";
context.HttpContext.Response.Headers["Pragma"] = "no-cache";
context.HttpContext.Response.Headers["Expires"] = "-1";
base.OnResultExecuting(context);
}
}
}

View File

@@ -1,150 +0,0 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace MusicStore.Infrastructure
{
public interface IPagedList<T>
{
IEnumerable<T> Data { get; }
int Page { get; }
int PageSize { get; }
int TotalCount { get; }
}
internal class PagedList<T> : IPagedList<T>
{
public PagedList(IEnumerable<T> data, int page, int pageSize, int totalCount)
{
Data = data;
Page = page;
PageSize = pageSize;
TotalCount = totalCount;
}
public IEnumerable<T> Data { get; private set; }
public int Page { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
}
public static class PagedListExtensions
{
public static IPagedList<T> ToPagedList<T>(this IQueryable<T> query, int page, int pageSize)
{
if (query == null)
{
throw new ArgumentNullException("query");
}
var pagingConfig = new PagingConfig(page, pageSize);
var skipCount = ValidatePagePropertiesAndGetSkipCount(pagingConfig);
var data = query
.Skip(skipCount)
.Take(pagingConfig.PageSize)
.ToList();
if (skipCount > 0 && data.Count == 0)
{
// Requested page has no records, just return the first page
pagingConfig.Page = 1;
data = query
.Take(pagingConfig.PageSize)
.ToList();
}
return new PagedList<T>(data, pagingConfig.Page, pagingConfig.PageSize, query.Count());
}
public static Task<IPagedList<TModel>> ToPagedListAsync<TModel, TProperty>(this IQueryable<TModel> query, int page, int pageSize, string sortExpression, Expression<Func<TModel, TProperty>> defaultSortExpression, SortDirection defaultSortDirection = SortDirection.Ascending)
where TModel : class
{
return ToPagedListAsync<TModel, TProperty, TModel>(query, page, pageSize, sortExpression, defaultSortExpression, defaultSortDirection, null);
}
public static async Task<IPagedList<TResult>> ToPagedListAsync<TModel, TProperty, TResult>(this IQueryable<TModel> query, int page, int pageSize, string sortExpression, Expression<Func<TModel, TProperty>> defaultSortExpression, SortDirection defaultSortDirection, Func<TModel, TResult> selector)
where TModel : class
where TResult : class
{
if (query == null)
{
throw new ArgumentNullException("query");
}
var pagingConfig = new PagingConfig(page, pageSize);
var skipCount = ValidatePagePropertiesAndGetSkipCount(pagingConfig);
var dataQuery = query;
if (defaultSortExpression != null)
{
dataQuery = dataQuery
.SortBy(sortExpression, defaultSortExpression);
}
var data = await dataQuery
.Skip(skipCount)
.Take(pagingConfig.PageSize)
.ToListAsync();
if (skipCount > 0 && data.Count == 0)
{
// Requested page has no records, just return the first page
pagingConfig.Page = 1;
data = await dataQuery
.Take(pagingConfig.PageSize)
.ToListAsync();
}
var count = await query.CountAsync();
var resultData = selector != null
? data.Select(selector)
: data.Cast<TResult>();
return new PagedList<TResult>(resultData, pagingConfig.Page, pagingConfig.PageSize, count);
}
private static int ValidatePagePropertiesAndGetSkipCount(PagingConfig pagingConfig)
{
if (pagingConfig.Page < 1)
{
pagingConfig.Page = 1;
}
if (pagingConfig.PageSize < 10)
{
pagingConfig.PageSize = 10;
}
if (pagingConfig.PageSize > 100)
{
pagingConfig.PageSize = 100;
}
return pagingConfig.PageSize * (pagingConfig.Page - 1);
}
internal class PagingConfig
{
public PagingConfig(int page, int pageSize)
{
Page = page;
PageSize = pageSize;
}
public int Page { get; set; }
public int PageSize { get; set; }
}
}
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MusicStore.Infrastructure
{
public enum SortDirection
{
Ascending,
Descending
}
}

View File

@@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
namespace MusicStore.Infrastructure
{
public static class SortExpression
{
private const string SORT_DIRECTION_DESC = " DESC";
public static IQueryable<TModel> SortBy<TModel, TProperty>(this IQueryable<TModel> query, string sortExpression, Expression<Func<TModel, TProperty>> defaultSortExpression, SortDirection defaultSortDirection = SortDirection.Ascending) where TModel : class
{
return SortBy(query, sortExpression ?? Create(defaultSortExpression, defaultSortDirection));
}
public static string Create<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, SortDirection sortDirection = SortDirection.Ascending) where TModel : class
{
var expressionText = ExpressionHelper.GetExpressionText(expression);
// TODO: Validate the expression depth, etc.
var sortExpression = expressionText;
if (sortDirection == SortDirection.Descending)
{
sortExpression += SORT_DIRECTION_DESC;
}
return sortExpression;
}
public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string sortExpression) where T : class
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (String.IsNullOrWhiteSpace(sortExpression))
{
return source;
}
sortExpression = sortExpression.Trim();
var isDescending = false;
// DataSource control passes the sort parameter with a direction
// if the direction is descending
if (sortExpression.EndsWith(SORT_DIRECTION_DESC, StringComparison.OrdinalIgnoreCase))
{
isDescending = true;
var descIndex = sortExpression.Length - SORT_DIRECTION_DESC.Length;
sortExpression = sortExpression.Substring(0, descIndex).Trim();
}
if (string.IsNullOrEmpty(sortExpression))
{
return source;
}
ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty);
// Build up the property expression, e.g.: (m => m.Foo.Bar)
var sortExpressionParts = sortExpression.Split('.');
Expression propertyExpression = parameter;
foreach (var property in sortExpressionParts)
{
propertyExpression = Expression.Property(propertyExpression, property);
}
LambdaExpression lambda = Expression.Lambda(propertyExpression, parameter);
var methodName = (isDescending) ? "OrderByDescending" : "OrderBy";
Expression methodCallExpression = Expression.Call(
typeof(Queryable),
methodName,
new[] { source.ElementType, propertyExpression.Type },
source.Expression,
Expression.Quote(lambda));
return (IQueryable<T>)source.Provider.CreateQuery(methodCallExpression);
}
}
}

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>1a74148f-9dc0-435d-b5ac-7d1b0d3d5e0b</ProjectGuid>
<RootNamespace>MusicStore</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>5068</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -1,103 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using AutoMapper;
using MusicStore.Apis;
using MusicStore.Models;
namespace MusicStore
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = null;
});
// Add EF services to the service container
services.AddEntityFramework()
.AddEntityFrameworkSqlite()
.AddDbContext<MusicStoreContext>(options => {
options.UseSqlite("Data Source=music-db.sqlite");
});
// Add Identity services to the services container
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<MusicStoreContext>()
.AddDefaultTokenProviders();
// Configure Auth
services.Configure<AuthorizationOptions>(options =>
{
options.AddPolicy("app-ManageStore", new AuthorizationPolicyBuilder().RequireClaim("app-ManageStore", "Allowed").Build());
});
Mapper.Initialize(cfg =>
{
cfg.CreateMap<AlbumChangeDto, Album>();
cfg.CreateMap<Album, AlbumChangeDto>();
cfg.CreateMap<Album, AlbumResultDto>();
cfg.CreateMap<AlbumResultDto, Album>();
cfg.CreateMap<Artist, ArtistResultDto>();
cfg.CreateMap<ArtistResultDto, Artist>();
cfg.CreateMap<Genre, GenreResultDto>();
cfg.CreateMap<GenreResultDto, Genre>();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
// Initialize the sample data
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
app.UseStaticFiles();
loggerFactory.AddConsole();
// Add MVC to the request pipeline.
app.UseMvc(routes =>
{
// Matches requests that correspond to an existent controller/action pair
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
// Matches any other request that doesn't appear to have a filename extension (defined as 'having a dot in the last URI segment').
// This means you'll correctly get 404s for /some/dir/non-existent-image.png instead of returning the SPA HTML.
// However, it means requests like /customers/isaac.newton will *not* be mapped into the SPA, so if you need to accept
// URIs like that you'll need to match all URIs, e.g.:
// routes.MapRoute("spa-fallback", "{*anything}", new { controller = "Home", action = "Index" });
// (which of course will match /customers/isaac.png too, so in that case it would serve the PNG image at that URL if one is on disk,
// or the SPA HTML if not).
routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

@@ -1,23 +0,0 @@
@{
ViewData["Title"] = "Home Page";
}
<cache vary-by="@Context.Request.Path">
<app asp-prerender-module="wwwroot/ng-app/boot-server">Loading...</app>
@await Html.PrimeCacheAsync(Url.Action("GenreMenuList", "GenresApi"))
@await Html.PrimeCacheAsync(Url.Action("MostPopular", "AlbumsApi"))
</cache>
@section scripts {
<script src="~/lib/angular2/bundles/angular2-polyfills.js"></script>
<script src="~/lib/traceur/bin/traceur-runtime.js"></script>
<script src="~/lib/es6-module-loader/dist/es6-module-loader-sans-promises.js"></script>
<script src="~/lib/systemjs/dist/system.src.js"></script>
<script src="~/system.config.js"></script>
<script src="~/lib/rxjs/bundles/Rx.js"></script>
<script src="~/lib/angular2/bundles/angular2.dev.js"></script>
<script src="~/lib/angular2/bundles/router.dev.js"></script>
<script src="~/lib/angular2/bundles/http.dev.js"></script>
<script src="~/lib/angular2-aspnet/bundles/angular2-aspnet.js"></script>
<script>System.import('./ng-app/boot-client');</script>
}

View File

@@ -1,6 +0,0 @@
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

View File

@@ -1,40 +0,0 @@
<!doctype html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Music Store</title>
<base href="/" />
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="hidden" asp-fallback-test-property="visibility" asp-fallback-test-value="hidden" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</environment>
</head>
<body>
@RenderBody()
<environment names="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
</script>
</environment>
@RenderSection("scripts", required: false)
</body>
</html>

View File

@@ -1,4 +0,0 @@
@using MusicStore
@using Microsoft.AspNetCore.AngularServices
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNetCore.SpaServices"

View File

@@ -1,53 +0,0 @@
/// <binding AfterBuild='build' Clean='clean' />
"use strict";
var path = require('path');
var gulp = require('gulp');
var del = require('del');
var typescript = require('gulp-typescript');
var inlineNg2Template = require('gulp-inline-ng2-template');
var sourcemaps = require('gulp-sourcemaps');
var webroot = "./wwwroot/";
var config = {
libBase: 'node_modules',
lib: [
require.resolve('bootstrap/dist/css/bootstrap.css'),
path.dirname(require.resolve('bootstrap/dist/fonts/glyphicons-halflings-regular.woff')) + '/**',
require.resolve('angular2/bundles/angular2-polyfills.js'),
require.resolve('traceur/bin/traceur-runtime.js'),
require.resolve('es6-module-loader/dist/es6-module-loader-sans-promises.js'),
require.resolve('systemjs/dist/system.src.js'),
require.resolve('angular2/bundles/angular2.dev.js'),
require.resolve('angular2/bundles/router.dev.js'),
require.resolve('angular2/bundles/http.dev.js'),
require.resolve('angular2-aspnet/bundles/angular2-aspnet.js'),
require.resolve('jquery/dist/jquery.js'),
require.resolve('bootstrap/dist/js/bootstrap.js'),
require.resolve('rxjs/bundles/Rx.js')
]
};
gulp.task('build.lib', function () {
return gulp.src(config.lib, { base: config.libBase })
.pipe(gulp.dest(webroot + 'lib'));
});
gulp.task('build', ['build.lib'], function () {
var tsProject = typescript.createProject('./tsconfig.json', { typescript: require('typescript') });
var tsSrcInlined = gulp.src([webroot + '**/*.ts', 'typings/**/*.d.ts'], { base: webroot })
.pipe(inlineNg2Template({ base: webroot }));
return tsSrcInlined
.pipe(sourcemaps.init())
.pipe(typescript(tsProject))
.pipe(sourcemaps.write())
.pipe(gulp.dest(webroot));
});
gulp.task('clean', function () {
return del([webroot + 'lib']);
});
gulp.task('default', ['build']);

View File

@@ -1,32 +0,0 @@
{
"name": "MusicStore",
"version": "0.0.0",
"dependencies": {
"angular2": "2.0.0-beta.15",
"angular2-aspnet": "^0.0.6",
"angular2-universal": "0.98.1",
"angular2-express-engine": "0.11.1",
"angular2-hapi-engine": "0.11.1",
"aspnet-prerendering": "^1.0.1",
"bootstrap": "^3.3.5",
"css": "^2.2.1",
"es6-module-loader": "0.15.0",
"es6-shim": "^0.35.0",
"isomorphic-fetch": "^2.2.1",
"jquery": "^2.1.4",
"less": "^2.5.3",
"preboot": "2.0.5",
"rxjs": "5.0.0-beta.2",
"systemjs": "^0.19.3",
"traceur": "0.0.106",
"zone.js": "^0.6.10"
},
"devDependencies": {
"del": "^2.0.2",
"gulp": "^3.9.0",
"gulp-inline-ng2-template": "0.0.7",
"gulp-sourcemaps": "^1.6.0",
"gulp-typescript": "^2.9.0",
"typescript": "^1.6.2"
}
}

View File

@@ -1,79 +0,0 @@
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true
}
},
"tooling": {
"defaultNamespace": "MusicStore"
},
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.1",
"type": "platform"
},
"Microsoft.AspNetCore.AngularServices": "1.0.0-*",
"Microsoft.AspNetCore.Diagnostics": "1.0.0",
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0",
"Microsoft.AspNetCore.Mvc": "1.0.1",
"Microsoft.AspNetCore.Razor.Tools": {
"version": "1.0.0-preview2-final",
"type": "build"
},
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
"Microsoft.AspNetCore.StaticFiles": "1.0.0",
"Microsoft.EntityFrameworkCore.SQLite": "1.0.0",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0",
"Microsoft.Extensions.Configuration.CommandLine": "1.0.0",
"Microsoft.Extensions.Logging": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
"AutoMapper": "5.0.2"
},
"tools": {
"Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final",
"Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final"
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"dotnet5.6",
"portable-net45+win8"
]
}
},
"publishOptions": {
"include": [
"appsettings.json",
"ClientApp",
"node_modules",
"typings",
"Views",
"tsconfig.json",
"tsd.json",
"web.config",
"webpack.*.js",
"wwwroot"
]
},
"scripts": {
"prepublish": [
"npm install",
"gulp"
],
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
}

View File

@@ -1,13 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noLib": false
},
"exclude": [
"node_modules"
]
}

View File

@@ -1,12 +0,0 @@
{
"version": "v4",
"repo": "borisyankov/DefinitelyTyped",
"ref": "master",
"path": "typings",
"bundle": "typings/tsd.d.ts",
"installed": {
"es6-shim/es6-shim.d.ts": {
"commit": "ec9eb4b28c74665a602c22db3457f0a76fa0fa23"
}
}
}

View File

@@ -1,668 +0,0 @@
// Type definitions for es6-shim v0.31.2
// Project: https://github.com/paulmillr/es6-shim
// Definitions by: Ron Buckton <http://github.com/rbuckton>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare type PropertyKey = string | number | symbol;
interface IteratorResult<T> {
done: boolean;
value?: T;
}
interface IterableShim<T> {
/**
* Shim for an ES6 iterable. Not intended for direct use by user code.
*/
"_es6-shim iterator_"(): Iterator<T>;
}
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
interface IterableIteratorShim<T> extends IterableShim<T>, Iterator<T> {
/**
* Shim for an ES6 iterable iterator. Not intended for direct use by user code.
*/
"_es6-shim iterator_"(): IterableIteratorShim<T>;
}
interface StringConstructor {
/**
* Return the String value whose elements are, in order, the elements in the List elements.
* If length is 0, the empty string is returned.
*/
fromCodePoint(...codePoints: number[]): string;
/**
* String.raw is intended for use as a tag function of a Tagged Template String. When called
* as such the first argument will be a well formed template call site object and the rest
* parameter will contain the substitution values.
* @param template A well-formed template string call site representation.
* @param substitutions A set of substitution values.
*/
raw(template: TemplateStringsArray, ...substitutions: any[]): string;
}
interface String {
/**
* Returns a nonnegative integer Number less than 1114112 (0x110000) that is the code point
* value of the UTF-16 encoded code point starting at the string element at position pos in
* the String resulting from converting this object to a String.
* If there is no element at that position, the result is undefined.
* If a valid UTF-16 surrogate pair does not begin at pos, the result is the code unit at pos.
*/
codePointAt(pos: number): number;
/**
* Returns true if searchString appears as a substring of the result of converting this
* object to a String, at one or more positions that are
* greater than or equal to position; otherwise, returns false.
* @param searchString search string
* @param position If position is undefined, 0 is assumed, so as to search all of the String.
*/
includes(searchString: string, position?: number): boolean;
/**
* Returns true if the sequence of elements of searchString converted to a String is the
* same as the corresponding elements of this object (converted to a String) starting at
* endPosition length(this). Otherwise returns false.
*/
endsWith(searchString: string, endPosition?: number): boolean;
/**
* Returns a String value that is made from count copies appended together. If count is 0,
* T is the empty String is returned.
* @param count number of copies to append
*/
repeat(count: number): string;
/**
* Returns true if the sequence of elements of searchString converted to a String is the
* same as the corresponding elements of this object (converted to a String) starting at
* position. Otherwise returns false.
*/
startsWith(searchString: string, position?: number): boolean;
/**
* Returns an <a> HTML anchor element and sets the name attribute to the text value
* @param name
*/
anchor(name: string): string;
/** Returns a <big> HTML element */
big(): string;
/** Returns a <blink> HTML element */
blink(): string;
/** Returns a <b> HTML element */
bold(): string;
/** Returns a <tt> HTML element */
fixed(): string
/** Returns a <font> HTML element and sets the color attribute value */
fontcolor(color: string): string
/** Returns a <font> HTML element and sets the size attribute value */
fontsize(size: number): string;
/** Returns a <font> HTML element and sets the size attribute value */
fontsize(size: string): string;
/** Returns an <i> HTML element */
italics(): string;
/** Returns an <a> HTML element and sets the href attribute value */
link(url: string): string;
/** Returns a <small> HTML element */
small(): string;
/** Returns a <strike> HTML element */
strike(): string;
/** Returns a <sub> HTML element */
sub(): string;
/** Returns a <sup> HTML element */
sup(): string;
/**
* Shim for an ES6 iterable. Not intended for direct use by user code.
*/
"_es6-shim iterator_"(): IterableIteratorShim<string>;
}
interface ArrayConstructor {
/**
* Creates an array from an array-like object.
* @param arrayLike An array-like object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T, U>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): Array<U>;
/**
* Creates an array from an iterable object.
* @param iterable An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T, U>(iterable: IterableShim<T>, mapfn: (v: T, k: number) => U, thisArg?: any): Array<U>;
/**
* Creates an array from an array-like object.
* @param arrayLike An array-like object to convert to an array.
*/
from<T>(arrayLike: ArrayLike<T>): Array<T>;
/**
* Creates an array from an iterable object.
* @param iterable An iterable object to convert to an array.
*/
from<T>(iterable: IterableShim<T>): Array<T>;
/**
* Returns a new array from a set of elements.
* @param items A set of elements to include in the new array object.
*/
of<T>(...items: T[]): Array<T>;
}
interface Array<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): T;
/**
* Returns the index of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T) => boolean, thisArg?: any): number;
/**
* Returns the this object after filling the section identified by start and end with value
* @param value value to fill array section with
* @param start index to start filling the array at. If start is negative, it is treated as
* length+start where length is the length of the array.
* @param end index to stop filling the array at. If end is negative, it is treated as
* length+end.
*/
fill(value: T, start?: number, end?: number): T[];
/**
* Returns the this object after copying a section of the array identified by start and end
* to the same array starting at position target
* @param target If target is negative, it is treated as length+target where length is the
* length of the array.
* @param start If start is negative, it is treated as length+start. If end is negative, it
* is treated as length+end.
* @param end If not specified, length of the this object is used as its default value.
*/
copyWithin(target: number, start: number, end?: number): T[];
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): IterableIteratorShim<[number, T]>;
/**
* Returns an list of keys in the array
*/
keys(): IterableIteratorShim<number>;
/**
* Returns an list of values in the array
*/
values(): IterableIteratorShim<T>;
/**
* Shim for an ES6 iterable. Not intended for direct use by user code.
*/
"_es6-shim iterator_"(): IterableIteratorShim<T>;
}
interface NumberConstructor {
/**
* The value of Number.EPSILON is the difference between 1 and the smallest value greater than 1
* that is representable as a Number value, which is approximately:
* 2.2204460492503130808472633361816 x 1016.
*/
EPSILON: number;
/**
* Returns true if passed value is finite.
* Unlike the global isFininte, Number.isFinite doesn't forcibly convert the parameter to a
* number. Only finite values of the type number, result in true.
* @param number A numeric value.
*/
isFinite(number: number): boolean;
/**
* Returns true if the value passed is an integer, false otherwise.
* @param number A numeric value.
*/
isInteger(number: number): boolean;
/**
* Returns a Boolean value that indicates whether a value is the reserved value NaN (not a
* number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter
* to a number. Only values of the type number, that are also NaN, result in true.
* @param number A numeric value.
*/
isNaN(number: number): boolean;
/**
* Returns true if the value passed is a safe integer.
* @param number A numeric value.
*/
isSafeInteger(number: number): boolean;
/**
* The value of the largest integer n such that n and n + 1 are both exactly representable as
* a Number value.
* The value of Number.MIN_SAFE_INTEGER is 9007199254740991 2^53 1.
*/
MAX_SAFE_INTEGER: number;
/**
* The value of the smallest integer n such that n and n 1 are both exactly representable as
* a Number value.
* The value of Number.MIN_SAFE_INTEGER is 9007199254740991 ((2^53 1)).
*/
MIN_SAFE_INTEGER: number;
/**
* Converts a string to a floating-point number.
* @param string A string that contains a floating-point number.
*/
parseFloat(string: string): number;
/**
* Converts A string to an integer.
* @param s A string to convert into a number.
* @param radix A value between 2 and 36 that specifies the base of the number in numString.
* If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
* All other strings are considered decimal.
*/
parseInt(string: string, radix?: number): number;
}
interface ObjectConstructor {
/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param sources One or more source objects to copy properties from.
*/
assign(target: any, ...sources: any[]): any;
/**
* Returns true if the values are the same value, false otherwise.
* @param value1 The first value.
* @param value2 The second value.
*/
is(value1: any, value2: any): boolean;
/**
* Sets the prototype of a specified object o to object proto or null. Returns the object o.
* @param o The object to change its prototype.
* @param proto The value of the new prototype or null.
* @remarks Requires `__proto__` support.
*/
setPrototypeOf(o: any, proto: any): any;
}
interface RegExp {
/**
* Returns a string indicating the flags of the regular expression in question. This field is read-only.
* The characters in this string are sequenced and concatenated in the following order:
*
* - "g" for global
* - "i" for ignoreCase
* - "m" for multiline
* - "u" for unicode
* - "y" for sticky
*
* If no flags are set, the value is the empty string.
*/
flags: string;
}
interface Math {
/**
* Returns the number of leading zero bits in the 32-bit binary representation of a number.
* @param x A numeric expression.
*/
clz32(x: number): number;
/**
* Returns the result of 32-bit multiplication of two numbers.
* @param x First number
* @param y Second number
*/
imul(x: number, y: number): number;
/**
* Returns the sign of the x, indicating whether x is positive, negative or zero.
* @param x The numeric expression to test
*/
sign(x: number): number;
/**
* Returns the base 10 logarithm of a number.
* @param x A numeric expression.
*/
log10(x: number): number;
/**
* Returns the base 2 logarithm of a number.
* @param x A numeric expression.
*/
log2(x: number): number;
/**
* Returns the natural logarithm of 1 + x.
* @param x A numeric expression.
*/
log1p(x: number): number;
/**
* Returns the result of (e^x - 1) of x (e raised to the power of x, where e is the base of
* the natural logarithms).
* @param x A numeric expression.
*/
expm1(x: number): number;
/**
* Returns the hyperbolic cosine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
cosh(x: number): number;
/**
* Returns the hyperbolic sine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
sinh(x: number): number;
/**
* Returns the hyperbolic tangent of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
tanh(x: number): number;
/**
* Returns the inverse hyperbolic cosine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
acosh(x: number): number;
/**
* Returns the inverse hyperbolic sine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
asinh(x: number): number;
/**
* Returns the inverse hyperbolic tangent of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
atanh(x: number): number;
/**
* Returns the square root of the sum of squares of its arguments.
* @param values Values to compute the square root for.
* If no arguments are passed, the result is +0.
* If there is only one argument, the result is the absolute value.
* If any argument is +Infinity or -Infinity, the result is +Infinity.
* If any argument is NaN, the result is NaN.
* If all arguments are either +0 or 0, the result is +0.
*/
hypot(...values: number[]): number;
/**
* Returns the integral part of the a numeric expression, x, removing any fractional digits.
* If x is already an integer, the result is x.
* @param x A numeric expression.
*/
trunc(x: number): number;
/**
* Returns the nearest single precision float representation of a number.
* @param x A numeric expression.
*/
fround(x: number): number;
/**
* Returns an implementation-dependent approximation to the cube root of number.
* @param x A numeric expression.
*/
cbrt(x: number): number;
}
interface PromiseLike<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<TResult>;
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): PromiseLike<TResult>;
}
/**
* Represents the completion of an asynchronous operation
*/
interface Promise<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): Promise<TResult>;
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): Promise<TResult>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch(onrejected?: (reason: any) => T | PromiseLike<T>): Promise<T>;
catch(onrejected?: (reason: any) => void): Promise<T>;
}
interface PromiseConstructor {
/**
* A reference to the prototype.
*/
prototype: Promise<any>;
/**
* Creates a new Promise.
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
* a resolve callback used resolve the promise with a value or the result of another promise,
* and a reject callback used to reject the promise with a provided reason or error.
*/
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
/**
* Creates a Promise that is resolved with an array of results when all of the provided Promises
* resolve, or rejected when any Promise is rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
all<T>(values: IterableShim<T | PromiseLike<T>>): Promise<T[]>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T>(values: IterableShim<T | PromiseLike<T>>): Promise<T>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject(reason: any): Promise<void>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject<T>(reason: any): Promise<T>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T | PromiseLike<T>): Promise<T>;
/**
* Creates a new resolved promise .
* @returns A resolved promise.
*/
resolve(): Promise<void>;
}
declare var Promise: PromiseConstructor;
interface Map<K, V> {
clear(): void;
delete(key: K): boolean;
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
get(key: K): V;
has(key: K): boolean;
set(key: K, value?: V): Map<K, V>;
size: number;
entries(): IterableIteratorShim<[K, V]>;
keys(): IterableIteratorShim<K>;
values(): IterableIteratorShim<V>;
}
interface MapConstructor {
new <K, V>(): Map<K, V>;
new <K, V>(iterable: IterableShim<[K, V]>): Map<K, V>;
prototype: Map<any, any>;
}
declare var Map: MapConstructor;
interface Set<T> {
add(value: T): Set<T>;
clear(): void;
delete(value: T): boolean;
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
has(value: T): boolean;
size: number;
entries(): IterableIteratorShim<[T, T]>;
keys(): IterableIteratorShim<T>;
values(): IterableIteratorShim<T>;
}
interface SetConstructor {
new <T>(): Set<T>;
new <T>(iterable: IterableShim<T>): Set<T>;
prototype: Set<any>;
}
declare var Set: SetConstructor;
interface WeakMap<K, V> {
delete(key: K): boolean;
get(key: K): V;
has(key: K): boolean;
set(key: K, value?: V): WeakMap<K, V>;
}
interface WeakMapConstructor {
new <K, V>(): WeakMap<K, V>;
new <K, V>(iterable: IterableShim<[K, V]>): WeakMap<K, V>;
prototype: WeakMap<any, any>;
}
declare var WeakMap: WeakMapConstructor;
interface WeakSet<T> {
add(value: T): WeakSet<T>;
delete(value: T): boolean;
has(value: T): boolean;
}
interface WeakSetConstructor {
new <T>(): WeakSet<T>;
new <T>(iterable: IterableShim<T>): WeakSet<T>;
prototype: WeakSet<any>;
}
declare var WeakSet: WeakSetConstructor;
declare namespace Reflect {
function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
function construct(target: Function, argumentsList: ArrayLike<any>): any;
function defineProperty(target: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
function deleteProperty(target: any, propertyKey: PropertyKey): boolean;
function enumerate(target: any): IterableIteratorShim<any>;
function get(target: any, propertyKey: PropertyKey, receiver?: any): any;
function getOwnPropertyDescriptor(target: any, propertyKey: PropertyKey): PropertyDescriptor;
function getPrototypeOf(target: any): any;
function has(target: any, propertyKey: PropertyKey): boolean;
function isExtensible(target: any): boolean;
function ownKeys(target: any): Array<PropertyKey>;
function preventExtensions(target: any): boolean;
function set(target: any, propertyKey: PropertyKey, value: any, receiver?: any): boolean;
function setPrototypeOf(target: any, proto: any): boolean;
}
declare module "es6-shim" {
var String: StringConstructor;
var Array: ArrayConstructor;
var Number: NumberConstructor;
var Math: Math;
var Object: ObjectConstructor;
var Map: MapConstructor;
var Set: SetConstructor;
var WeakMap: WeakMapConstructor;
var WeakSet: WeakSetConstructor;
var Promise: PromiseConstructor;
namespace Reflect {
function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
function construct(target: Function, argumentsList: ArrayLike<any>): any;
function defineProperty(target: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
function deleteProperty(target: any, propertyKey: PropertyKey): boolean;
function enumerate(target: any): Iterator<any>;
function get(target: any, propertyKey: PropertyKey, receiver?: any): any;
function getOwnPropertyDescriptor(target: any, propertyKey: PropertyKey): PropertyDescriptor;
function getPrototypeOf(target: any): any;
function has(target: any, propertyKey: PropertyKey): boolean;
function isExtensible(target: any): boolean;
function ownKeys(target: any): Array<PropertyKey>;
function preventExtensions(target: any): boolean;
function set(target: any, propertyKey: PropertyKey, value: any, receiver?: any): boolean;
function setPrototypeOf(target: any, proto: any): boolean;
}
}

View File

@@ -1,2 +0,0 @@
/// <reference path="es6-shim/es6-shim.d.ts" />

View File

@@ -1,7 +0,0 @@
// This file is a workaround for angular2-universal-preview version 0.84.2 relying on the declaration of
// Node's 'url' module. Ideally it would not declare dependencies on Node APIs except where it also supplies
// the definitions itself.
declare module 'url' {
export interface Url {}
}

View File

@@ -1,3 +0,0 @@
body {
padding-top: 50px;
}

View File

@@ -1,14 +0,0 @@
@base: #f938ab;
.box-shadow(@style, @c) when (iscolor(@c)) {
-webkit-box-shadow: @style @c;
box-shadow: @style @c;
}
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
.box-shadow(@style, rgba(0, 0, 0, @alpha));
}
.box {
color: saturate(@base, 5%);
border-color: lighten(@base, 30%);
div { .box-shadow(0 0 5px, 30%) }
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,8 +0,0 @@
import { bootstrap } from 'angular2/platform/browser';
import { FormBuilder } from 'angular2/common';
import * as router from 'angular2/router';
import { Http, HTTP_PROVIDERS } from 'angular2/http';
import { CACHE_PRIMED_HTTP_PROVIDERS } from 'angular2-aspnet';
import { App } from './components/app/app';
bootstrap(App, [router.ROUTER_BINDINGS, HTTP_PROVIDERS, CACHE_PRIMED_HTTP_PROVIDERS, FormBuilder]);

View File

@@ -1,30 +0,0 @@
import 'angular2-universal/polyfills';
import { FormBuilder } from 'angular2/common';
import * as ngCore from 'angular2/core';
import * as ngRouter from 'angular2/router';
import * as ngUniversal from 'angular2-universal';
import { BASE_URL, ORIGIN_URL, REQUEST_URL } from 'angular2-universal/common';
import { App } from './components/app/app';
export default function (params: any): Promise<{ html: string, globals?: any }> {
const serverBindings = [
ngCore.provide(BASE_URL, { useValue: '/' }),
ngCore.provide(ORIGIN_URL, { useValue: params.origin }),
ngCore.provide(REQUEST_URL, { useValue: params.url }),
ngUniversal.NODE_HTTP_PROVIDERS,
ngUniversal.NODE_ROUTER_PROVIDERS,
FormBuilder
];
return ngUniversal.bootloader({
directives: [App],
providers: serverBindings,
async: true,
preboot: false,
// TODO: Render just the <app> component instead of wrapping it inside an extra HTML document
// Waiting on https://github.com/angular/universal/issues/347
template: '<!DOCTYPE html>\n<html><head></head><body><app></app></body></html>'
}).serializeApplication().then(html => {
return { html };
});
}

View File

@@ -1,3 +0,0 @@
<h1>Store Manager</h1>
<router-outlet></router-outlet>

View File

@@ -1,19 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import { AlbumsList } from '../albums-list/albums-list';
import { AlbumDetails } from '../album-details/album-details';
import { AlbumEdit } from '../album-edit/album-edit';
@ng.Component({
selector: 'admin-home',
templateUrl: './ng-app/components/admin/admin-home/admin-home.html',
directives: [router.ROUTER_DIRECTIVES]
})
@router.RouteConfig([
{ path: 'albums', name: 'Albums', component: AlbumsList },
{ path: 'album/details/:albumId', name: 'AlbumDetails', component: AlbumDetails },
{ path: 'album/edit/:albumId', name: 'AlbumEdit', component: AlbumEdit }
])
export class AdminHome {
}

View File

@@ -1,17 +0,0 @@
<div class="modal fade">
<div class="modal-dialog" *ngIf="album">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Delete {{ album.Title }}</h4>
</div>
<div class="modal-body">
<p>Really delete <strong>{{ album.Title }}</strong> by <strong>{{ album.Artist.Name }}</strong>?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger">Confirm Delete</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View File

@@ -1,20 +0,0 @@
import * as ng from 'angular2/core';
import * as models from '../../../models/models';
@ng.Component({
selector: 'album-delete-prompt',
templateUrl: './ng-app/components/admin/album-delete-prompt/album-delete-prompt.html'
})
export class AlbumDeletePrompt {
public album: models.Album;
constructor(@ng.Inject(ng.ElementRef) private _elementRef: ng.ElementRef) {
}
public show(album: models.Album) {
this.album = album;
// Consider rewriting this using Angular 2's "Renderer" API so as to avoid direct DOM access
(<any>window).jQuery(".modal", this._elementRef.nativeElement).modal();
}
}

View File

@@ -1,50 +0,0 @@
<h2>Album <small>Details</small></h2>
<hr />
<form class="form-horizontal" role="form" *ngIf="albumData">
<div class="form-group">
<label class="col-md-2 control-label">Artist</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Artist.Name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Genre</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Genre.Name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Title</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Title }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Price</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Price | currency:'USD':true }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Album Art URL</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.AlbumArtUrl }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Album Art</label>
<div class="col-md-10">
<img alt="{{ albumData.Title }}" src="{{ albumData.AlbumArtUrl }}">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<a class="btn btn-primary" [routerLink]="['/Admin/AlbumEdit', { albumId: albumData.AlbumId }]">Edit</a>
<button type="button" class="btn btn-danger" (click)="deleteprompt.show(albumData)">Delete</button>
<a class="btn btn-default" [routerLink]="['/Admin/Albums']">Back to List</a>
</div>
</div>
</form>
<album-delete-prompt #deleteprompt></album-delete-prompt>

View File

@@ -1,20 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import * as models from '../../../models/models';
import { Http, HTTP_BINDINGS } from 'angular2/http';
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
@ng.Component({
selector: 'album-details',
templateUrl: './ng-app/components/admin/album-details/album-details.html',
directives: [router.ROUTER_DIRECTIVES, AlbumDeletePrompt]
})
export class AlbumDetails {
public albumData: models.Album;
constructor(http: Http, routeParam: router.RouteParams) {
http.get('/api/albums/' + routeParam.params['albumId']).subscribe(result => {
this.albumData = result.json();
});
}
}

View File

@@ -1,47 +0,0 @@
<h2>Album <small>Edit</small></h2>
<hr />
<form class="form-horizontal" [ngFormModel]="form" (ngSubmit)="onSubmitModelBased()">
<form-field label="Artist" [validate]="form.controls.ArtistId">
<select class="form-control" ngControl="ArtistId">
<option value="0">-- choose Artist --</option>
<option *ngFor="#artist of artists" [value]="artist.ArtistId">{{ artist.Name }}</option>
</select>
</form-field>
<form-field label="Genre" [validate]="form.controls.GenreId">
<select class="form-control" ngControl="GenreId">
<option value="0">-- choose Genre --</option>
<option *ngFor="#genre of genres" [value]="genre.GenreId">{{ genre.Name }}</option>
</select>
</form-field>
<form-field label="Title" [validate]="form.controls.Title">
<input class="form-control" type="text" ngControl="Title">
</form-field>
<form-field label="Price" [validate]="form.controls.Price">
<div class="input-group">
<span class="input-group-addon">$</span>
<input class="form-control" type="text" ngControl="Price">
</div>
</form-field>
<form-field label="Album Art URL" [validate]="form.controls.AlbumArtUrl">
<input class="form-control" ngControl="AlbumArtUrl">
</form-field>
<form-field label="Album Art">
<img src="{{ form.controls.AlbumArtUrl.value }}">
</form-field>
<form-field>
<div *ngIf="changesSaved" class="alert alert-success"><b>Done!</b> Your changes were saved.</div>
<div *ngFor="#errorMessage of formErrors" class="alert alert-danger">{{ errorMessage }}</div>
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-danger" (click)="deleteprompt.show(originalAlbum)">Delete</button>
<a class="btn btn-default" [routerLink]="['/Admin/Albums']">Back to List</a>
</form-field>
</form>
<album-delete-prompt #deleteprompt></album-delete-prompt>

View File

@@ -1,94 +0,0 @@
import * as ng from 'angular2/core';
import { Observable } from 'rxjs';
import { Control, ControlGroup, FormBuilder, Validators, FORM_DIRECTIVES } from 'angular2/common';
import * as router from 'angular2/router';
import * as models from '../../../models/models';
import { Http, HTTP_BINDINGS, Headers, Response } from 'angular2/http';
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
import { FormField } from '../form-field/form-field';
import * as AspNet from 'angular2-aspnet';
@ng.Component({
selector: 'album-edit',
templateUrl: './ng-app/components/admin/album-edit/album-edit.html',
directives: [router.ROUTER_DIRECTIVES, AlbumDeletePrompt, FormField, FORM_DIRECTIVES]
})
export class AlbumEdit {
public form: ControlGroup;
public artists: models.Artist[];
public genres: models.Genre[];
public originalAlbum: models.Album;
public changesSaved: boolean;
public formErrors: string[] = [];
private _http: Http;
constructor(fb: FormBuilder, http: Http, routeParam: router.RouteParams) {
this._http = http;
var albumId = parseInt(routeParam.params['albumId']);
http.get('/api/albums/' + albumId).subscribe(result => {
var json = result.json();
this.originalAlbum = json;
(<Control>this.form.controls['Title']).updateValue(json.Title);
(<Control>this.form.controls['Price']).updateValue(json.Price);
(<Control>this.form.controls['ArtistId']).updateValue(json.ArtistId);
(<Control>this.form.controls['GenreId']).updateValue(json.GenreId);
(<Control>this.form.controls['AlbumArtUrl']).updateValue(json.AlbumArtUrl);
});
http.get('/api/artists/lookup').subscribe(result => {
this.artists = result.json();
});
http.get('/api/genres/genre-lookup').subscribe(result => {
this.genres = result.json();
});
this.form = fb.group(<any>{
AlbumId: fb.control(albumId),
ArtistId: fb.control(0, Validators.required),
GenreId: fb.control(0, Validators.required),
Title: fb.control('', Validators.required),
Price: fb.control('', Validators.compose([Validators.required, AlbumEdit._validatePrice])),
AlbumArtUrl: fb.control('', Validators.required)
});
this.form.valueChanges.subscribe(() => {
this.changesSaved = false;
});
}
public onSubmitModelBased() {
// Force all fields to show any validation errors even if they haven't been touched
Object.keys(this.form.controls).forEach(controlName => {
this.form.controls[controlName].markAsTouched();
});
if (this.form.valid) {
var controls = this.form.controls;
var albumId = this.originalAlbum.AlbumId;
this._putJson(`/api/albums/${ albumId }/update`, this.form.value).subscribe(successResponse => {
this.changesSaved = true;
}, errorResponse => {
AspNet.Validation.showValidationErrors(errorResponse, this.form);
});
}
}
private static _validatePrice(control: Control): { [key: string]: boolean } {
return /^\d+\.\d+$/.test(control.value) ? null : { Price: true };
}
// Need feedback on whether this really is the easiest way to PUT some JSON
private _putJson(url: string, body: any): Observable<Response> {
return this._http.put(url, JSON.stringify(body), {
headers: new Headers({ 'Content-Type': 'application/json' })
});
}
private ngDoCheck() {
this.formErrors = this.form.dirty ? Object.keys(this.form.errors || {}) : [];
}
}

View File

@@ -1,44 +0,0 @@
<h2>Albums</h2>
<album-delete-prompt #deleteprompt></album-delete-prompt>
<table class="table">
<thead>
<tr>
<th><a>Genre</a></th>
<th><a>Artist</a></th>
<th><a (click)="sortBy('Title')">Title</a></th>
<th><a (click)="sortBy('Price')">Price</a></th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="#row of rows">
<td>{{ row.Genre.Name }}</td>
<td>{{ row.Artist.Name }}</td>
<td>{{ row.Title }}</td>
<td>{{ row.Price | currency:'USD':true }}</td>
<td>
<div class="btn-group btn-group-xs">
<a class="btn btn-default" [routerLink]="['/Admin/AlbumDetails', { albumId: row.AlbumId }]">Details</a>
<a class="btn btn-default" [routerLink]="['/Admin/AlbumEdit', { albumId: row.AlbumId }]">Edit</a>
<a class="btn btn-default" (click)="deleteprompt.show(row)">Delete</a>
</div>
</td>
</tr>
</tbody>
</table>
<div class="btn-group">
<button class="btn btn-default" [disabled]="!canGoBack" (click)="goToPage(1)">First</button>
<button class="btn btn-default" [disabled]="!canGoBack" (click)="goToPage(pageIndex - 1)">Previous</button>
<button class="btn" *ngFor="#page of pageLinks"
[ngClass]="{ 'btn-info': page.isCurrent, 'btn-default': !page.isCurrent }"
(click)="goToPage(page.index)">
{{ page.text }}
</button>
<button class="btn btn-default" [disabled]="!canGoForward" (click)="goToPage(pageIndex + 1)">Next</button>
<button class="btn btn-default" [disabled]="!canGoForward" (click)="goToLast()">Last</button>
</div>
<p>{{ totalCount }} total albums</p>

View File

@@ -1,68 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import { Http, HTTP_BINDINGS } from 'angular2/http';
import * as models from '../../../models/models';
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
@ng.Component({
selector: 'albums-list',
templateUrl: './ng-app/components/admin/albums-list/albums-list.html',
directives: [router.ROUTER_DIRECTIVES, AlbumDeletePrompt]
})
export class AlbumsList {
public rows: models.Album[];
public canGoBack: boolean;
public canGoForward: boolean;
public pageLinks: any[];
public totalCount: number;
public get pageIndex() {
return this._pageIndex;
}
private _http: Http;
private _pageIndex = 1;
private _sortBy = "Title";
private _sortByDesc = false;
constructor(http: Http) {
this._http = http;
this.refreshData();
}
public sortBy(col: string) {
this._sortByDesc = col === this._sortBy ? !this._sortByDesc : false;
this._sortBy = col;
this.refreshData();
}
public goToPage(pageIndex: number) {
this._pageIndex = pageIndex;
this.refreshData();
}
public goToLast() {
this.goToPage(this.pageLinks[this.pageLinks.length - 1].index);
}
refreshData() {
var sortBy = this._sortBy + (this._sortByDesc ? ' DESC' : '');
this._http.get(`/api/albums?page=${ this._pageIndex }&pageSize=50&sortBy=${ sortBy }`).subscribe(result => {
var json = result.json();
this.rows = json.Data;
var numPages = Math.ceil(json.TotalCount / json.PageSize);
this.pageLinks = [];
for (var i = 1; i <= numPages; i++) {
this.pageLinks.push({
index: i,
text: i.toString(),
isCurrent: i === json.Page
});
}
this.canGoBack = this.pageLinks.length && !this.pageLinks[0].isCurrent;
this.canGoForward = this.pageLinks.length && !this.pageLinks[this.pageLinks.length - 1].isCurrent;
this.totalCount = json.TotalCount;
});
}
}

View File

@@ -1,9 +0,0 @@
<div class="form-group" [class.has-error]="errorMessages.length">
<label class="col-md-2 control-label">{{ label }}</label>
<div class="col-md-5">
<ng-content></ng-content>
<div class="alert alert-danger" role="alert" *ngIf="errorMessages.length">
<p *ngFor="#message of errorMessages">{{ message }}</p>
</div>
</div>
</div>

View File

@@ -1,19 +0,0 @@
import * as ng from 'angular2/core';
import { AbstractControl } from 'angular2/common';
@ng.Component({
selector: 'form-field',
properties: ['label', 'validate'],
templateUrl: './ng-app/components/admin/form-field/form-field.html'
})
export class FormField {
public errorMessages: string[] = [];
private validate: AbstractControl;
private ngDoCheck() {
var errors = (this.validate && this.validate.dirty && this.validate.errors) || {};
this.errorMessages = Object.keys(errors).map(key => {
return 'Error: ' + key;
});
}
}

View File

@@ -1,30 +0,0 @@
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" [routerLink]="['/Home']">Music Store</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a [routerLink]="['/Home']">Home</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown">Store <b class="caret"></b></a>
<ul class="dropdown-menu">
<li *ngFor="#genre of genres">
<a title="{{ genre.Description }}" [routerLink]="['/Genre', { genreId: genre.GenreId }]">
{{ genre.Name }}
</a>
</li>
<li class="divider"></li>
<li>
<a [routerLink]="['/GenresList']">More…</a>
</li>
</ul>
</li>
<li><a [routerLink]="['/Admin/Albums']">Admin</a></li>
</ul>
</div>
</div>
</nav>
<div class="container body-content">
<router-outlet></router-outlet>
</div>

View File

@@ -1,33 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import { Http, HTTP_BINDINGS } from 'angular2/http';
import { Home } from '../public/home/home';
import { AlbumDetails } from '../public/album-details/album-details';
import { GenreContents } from '../public/genre-contents/genre-contents';
import { GenresList } from '../public/genres-list/genres-list';
import { AdminHome } from '../admin/admin-home/admin-home';
import * as models from '../../models/models';
@ng.Component({
selector: 'app',
templateUrl: './ng-app/components/app/app.html',
styleUrls: ['./ng-app/components/app/app.css'],
directives: [router.ROUTER_DIRECTIVES]
})
@router.RouteConfig([
{ path: '/', component: Home, name: 'Home' },
{ path: '/album/:albumId', component: AlbumDetails, name: 'Album' },
{ path: '/genre/:genreId', component: GenreContents, name: 'Genre' },
{ path: '/genres', component: GenresList, name: 'GenresList' },
{ path: '/admin/...', component: AdminHome, name: 'Admin' }
])
export class App {
public genres: models.Genre[];
constructor(http: Http) {
http.get('/api/genres/menu').subscribe(result => {
this.genres = result.json();
});
}
}

View File

@@ -1,26 +0,0 @@
<div *ngIf="albumData">
<h2>{{ albumData.Title }}</h2>
<p>
<img alt="{{ albumData.Title }}" src="{{ albumData.AlbumArtUrl }}">
</p>
<div id="album-details">
<p>
<em>Genre:</em>
{{ albumData.Genre.Name }}
</p>
<p>
<em>Artist:</em>
{{ albumData.Artist.Name }}
</p>
<p>
<em>Price:</em>
{{ albumData.Price | currency:'USD':true }}
</p>
<p class="button">
<!-- TODO: Shopping cart functionality -->
Add to cart
</p>
</div>
</div>

View File

@@ -1,18 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import { Http } from 'angular2/http';
import * as models from '../../../models/models';
@ng.Component({
selector: 'album-details',
templateUrl: './ng-app/components/public/album-details/album-details.html'
})
export class AlbumDetails {
public albumData: models.Album;
constructor(http: Http, routeParam: router.RouteParams) {
http.get('/api/albums/' + routeParam.params['albumId']).subscribe(result => {
this.albumData = result.json();
});
}
}

View File

@@ -1,4 +0,0 @@
<a [routerLink]="['/Album', { albumId: albumData.AlbumId }]">
<img alt="{{ albumData.Title }}" src="{{ albumData.AlbumArtUrl }}">
<h4>{{ albumData.Title }}</h4>
</a>

View File

@@ -1,12 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import * as models from '../../../models/models';
@ng.Component({
selector: 'album-tile',
properties: ['albumData'],
templateUrl: './ng-app/components/public/album-tile/album-tile.html',
directives: [router.ROUTER_DIRECTIVES]
})
export class AlbumTile {
}

View File

@@ -1,7 +0,0 @@
<h3>Albums</h3>
<ul class="list-unstyled">
<li *ngFor="#album of albums" class="col-lg-2 col-md-2 col-sm-2 col-xs-4 container">
<album-tile [albumData]="album"></album-tile>
</li>
</ul>

View File

@@ -1,20 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import { Http } from 'angular2/http';
import * as models from '../../../models/models';
import { AlbumTile } from '../album-tile/album-tile';
@ng.Component({
selector: 'genre-contents',
templateUrl: './ng-app/components/public/genre-contents/genre-contents.html',
directives: [AlbumTile]
})
export class GenreContents {
public albums: models.Album[];
constructor(http: Http, routeParam: router.RouteParams) {
http.get(`/api/genres/${ routeParam.params['genreId'] }/albums`).subscribe(result => {
this.albums = result.json();
});
}
}

View File

@@ -1,13 +0,0 @@
<h3>Browse Genres</h3>
<p *ngIf="genres">
Select from {{ genres.length }} genres:
</p>
<ul class="list-group">
<li *ngFor="#genre of genres" class="list-group-item">
<a title="{{genre.Description}}" [routerLink]="['/Genre', { genreId: genre.GenreId }]">
{{ genre.Name }}
</a>
</li>
</ul>

View File

@@ -1,19 +0,0 @@
import * as ng from 'angular2/core';
import * as router from 'angular2/router';
import { Http } from 'angular2/http';
import * as models from '../../../models/models';
@ng.Component({
selector: 'genres-list',
templateUrl: './ng-app/components/public/genres-list/genres-list.html',
directives: [router.ROUTER_DIRECTIVES]
})
export class GenresList {
public genres: models.Genre[];
constructor(http: Http) {
http.get('/api/genres').subscribe(result => {
this.genres = result.json();
});
}
}

View File

@@ -1,10 +0,0 @@
<div class="jumbotron">
<h1>MVC Music Store</h1>
<img src="/Images/home-showcase.png">
</div>
<ul class="row list-unstyled" id="album-list">
<li *ngFor="#album of mostPopular" class="col-lg-2 col-md-2 col-sm-2 col-xs-4 container">
<album-tile [albumData]="album"></album-tile>
</li>
</ul>

View File

@@ -1,19 +0,0 @@
import * as ng from 'angular2/core';
import { Http } from 'angular2/http';
import { AlbumTile } from '../album-tile/album-tile';
import * as models from '../../../models/models';
@ng.Component({
selector: 'home',
templateUrl: './ng-app/components/public/home/home.html',
directives: [AlbumTile]
})
export class Home {
public mostPopular: models.Album[];
constructor(http: Http) {
http.get('/api/albums/mostPopular').subscribe(result => {
this.mostPopular = result.json();
});
}
}

View File

@@ -1,16 +0,0 @@
export interface Album {
AlbumId: number;
Title: string;
AlbumArtUrl: string;
}
export interface Genre {
GenreId: number;
Name: string;
Description: string;
}
export interface Artist {
ArtistId: number;
Name: string;
}

View File

@@ -1,3 +0,0 @@
System.config({
defaultJSExtensions: true
});

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
</handlers>
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false" forwardWindowsAuthToken="false" startupTimeLimit="3600" />
</system.webServer>
</configuration>

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\build\dependencies.props" />
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" />
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.NodeServices.Sockets\Microsoft.AspNetCore.NodeServices.Sockets.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(AspNetCoreVersion)" />
</ItemGroup>
</Project>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0.25123" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25123</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a64af9d9-72aa-4433-be1d-dc2524b6808a</ProjectGuid>
<RootNamespace>LatencyTest</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -3,6 +3,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.NodeServices; using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.NodeServices.Sockets;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApplication namespace ConsoleApplication
@@ -16,7 +17,10 @@ namespace ConsoleApplication
// Set up the DI system // Set up the DI system
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddNodeServices(options => { services.AddNodeServices(options => {
options.HostingModel = NodeServicesOptions.DefaultNodeHostingModel; // To compare with Socket hosting, uncomment the following line
// Since .NET Core 1.1, the HTTP hosting model has become basically as fast as the Socket hosting model
//options.UseSocketHosting();
options.ProjectPath = Directory.GetCurrentDirectory(); options.ProjectPath = Directory.GetCurrentDirectory();
options.WatchFileExtensions = new string[] {}; // Don't watch anything options.WatchFileExtensions = new string[] {}; // Don't watch anything
}); });

View File

@@ -1,19 +0,0 @@
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
},
"Microsoft.AspNetCore.NodeServices": "1.0.0-*",
"Microsoft.Extensions.DependencyInjection": "1.0.0"
},
"frameworks": {
"netcoreapp1.0": {
"imports": "dnxcore50"
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.NodeServices;
namespace NodeServicesExamples.Controllers namespace NodeServicesExamples.Controllers
{ {
@@ -15,8 +16,21 @@ namespace NodeServicesExamples.Controllers
return View(); return View();
} }
public IActionResult ImageResizing() public async Task<IActionResult> Chart([FromServices] INodeServices nodeServices)
{ {
var options = new { width = 400, height = 200, showArea = true, showPoint = true, fullWidth = true };
var data = new
{
labels = new[] { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
series = new[] {
new[] { 1, 5, 2, 5, 4, 3 },
new[] { 2, 3, 4, 8, 1, 2 },
new[] { 5, 4, 3, 2, 1, 0 }
}
};
ViewData["ChartMarkup"] = await nodeServices.InvokeAsync<string>("./Node/renderChart", "line", options, data);
return View(); return View();
} }

View File

@@ -1,64 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.StaticFiles;
namespace NodeServicesExamples.Controllers
{
public class ResizeImageController : Controller
{
private const int MaxDimension = 1000;
private static string[] AllowedMimeTypes = new[] { "image/jpeg", "image/png", "image/gif" };
private IHostingEnvironment _environment;
private INodeServices _nodeServices;
public ResizeImageController(IHostingEnvironment environment, INodeServices nodeServices)
{
_environment = environment;
_nodeServices = nodeServices;
}
[Route("resize/{*imagePath}")]
public async Task<IActionResult> Index(string imagePath, int maxWidth, int maxHeight)
{
// Validate incoming params
if (maxWidth < 0 || maxHeight < 0 || maxWidth > MaxDimension || maxHeight > MaxDimension
|| (maxWidth + maxHeight) == 0)
{
return BadRequest("Invalid dimensions");
}
var mimeType = GetContentType(imagePath);
if (Array.IndexOf(AllowedMimeTypes, mimeType) < 0)
{
return BadRequest("Disallowed image format");
}
// Locate source image on disk
var fileInfo = _environment.WebRootFileProvider.GetFileInfo(imagePath);
if (!fileInfo.Exists)
{
return NotFound();
}
// Invoke Node and pipe the result to the response
var imageStream = await _nodeServices.InvokeAsync<Stream>(
"./Node/resizeImage",
fileInfo.PhysicalPath,
mimeType,
maxWidth,
maxHeight);
return File(imageStream, mimeType);
}
private string GetContentType(string path)
{
string result;
return new FileExtensionContentTypeProvider().TryGetContentType(path, out result) ? result : null;
}
}
}

View File

@@ -0,0 +1,8 @@
var generate = require('node-chartist');
module.exports = function (callback, type, options, data) {
generate(type, options, data).then(
result => callback(null, result), // Success case
error => callback(error) // Error case
);
};

View File

@@ -1,8 +0,0 @@
var sharp = require('sharp');
module.exports = function(result, physicalPath, mimeType, maxWidth, maxHeight) {
// Invoke the 'sharp' NPM module, and have it pipe the resulting image data back to .NET
sharp(physicalPath)
.resize(maxWidth || null, maxHeight || null)
.pipe(result.stream);
}

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\build\dependencies.props" />
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(AspNetCoreVersion)" />
</ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
<Exec Command="npm install" />
</Target>
</Project>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>6d4bcdd6-7951-449b-be55-cb7f014b7430</ProjectGuid>
<RootNamespace>NodeServicesExamples</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\JavaScriptServices.sln\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>2018</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -41,7 +41,6 @@ namespace NodeServicesExamples
}); });
app.UseStaticFiles(); app.UseStaticFiles();
loggerFactory.AddConsole();
app.UseMvc(routes => app.UseMvc(routes =>
{ {
routes.MapRoute( routes.MapRoute(
@@ -53,6 +52,11 @@ namespace NodeServicesExamples
public static void Main(string[] args) public static void Main(string[] args)
{ {
var host = new WebHostBuilder() var host = new WebHostBuilder()
.ConfigureLogging(factory =>
{
factory.AddConsole();
factory.AddDebug();
})
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseKestrel() .UseKestrel()

View File

@@ -0,0 +1,12 @@
<h1>Server-rendered chart</h1>
<p>
This sample demonstrates how arbitrary NPM modules can be invoked from .NET code.
</p>
<p>
In this case, we use <code>node-chartist</code> to render the following chart on the server. The output is
identical to what you'd get if you used <a href='https://gionkunz.github.io/chartist-js/'>chartist.js</a>
on the client, except that in this example, we're not executing any client-side code at all.
</p>
@Html.Raw(ViewData["ChartMarkup"])

View File

@@ -1,34 +0,0 @@
<h1>Image Resizing</h1>
<p>
This sample shows how the NPM module <a href="https://www.npmjs.com/package/sharp"><code>sharp</code></a>
can be used for dynamic image resizing from within an ASP.NET Core application. There is one copy of the
following image on disk, but we can set up an MVC action method that returns it resized to fit within an
arbitrary width and height.
</p>
<p>
<strong>Dependencies:</strong> On Windows and Linux, there are no native dependencies. Just running
<code>npm install</code> is enough. On OS X, however, you need to have <code>libvips</code> installed,
which you can get through <a href="http://brew.sh/">Homebrew</a> by running
<code>brew install homebrew/science/vips</code>.
</p>
<h3>100px wide [<a href="/resize/images/parrot.jpg?maxWidth=100">open</a>]</h3>
<img src="/resize/images/parrot.jpg?maxWidth=100" />
<h3>200px wide [<a href="/resize/images/parrot.jpg?maxWidth=200">open</a>]</h3>
<img src="/resize/images/parrot.jpg?maxWidth=200" />
<h3>400px wide [<a href="/resize/images/parrot.jpg?maxWidth=400">open</a>]</h3>
<img src="/resize/images/parrot.jpg?maxWidth=400" />
<h3>800px wide [<a href="/resize/images/parrot.jpg?maxWidth=800">open</a>]</h3>
<img src="/resize/images/parrot.jpg?maxWidth=800" />
<p>
<strong>Credit:</strong>
<em><a href="https://www.flickr.com/photos/dcoetzee/3572948635">Parrot</a>
by <a href="https://www.flickr.com/photos/dcoetzee/">D Coetzee</a>
is dedicated to the <a href="http://creativecommons.org/publicdomain/zero/1.0/">public domain (CC0)</a></em>
</p>

View File

@@ -7,7 +7,6 @@
</p> </p>
<ul> <ul>
<li><a asp-action="ES2015Transpilation">ES2015 transpilation</a> <li><a asp-action="ES2015Transpilation">ES2015 transpilation</a></li>
<li><a asp-action="ImageResizing">Image resizing</a> <li><a asp-action="Chart">Server-side chart rendering</a></li>
</li> </ul>

View File

@@ -1,8 +1,9 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>NodeServices Examples</title> <title>NodeServices Examples</title>
<link rel="stylesheet" href="~/css/chartist.min.css" />
</head> </head>
<body> <body>
@RenderBody() @RenderBody()

Some files were not shown because too many files have changed in this diff Show More