MusicStore.Spa changes:
- Added buddy validation provider Created SimpleMapper in MusicStore.Spa - Fixed EF mapping config - Made SimpleMapper to map DTOs to EF objects (and vice-versa) - Admin screens now return Artist/Genre names when showing Album details - Unhook grunt/npm/bower from project.json due to long-path issue in KRE build - Remove unneeded AngularHtmlHelper<T> class
This commit is contained in:
parent
1c3c7a0eef
commit
0352ef8a79
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.22112.0
|
VisualStudioVersion = 14.0.22230.1
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{44621553-AA7D-4893-8834-79582A7D8348}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{44621553-AA7D-4893-8834-79582A7D8348}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
using System.Linq;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using MusicStore.Infrastructure;
|
using MusicStore.Infrastructure;
|
||||||
using MusicStore.Models;
|
using MusicStore.Models;
|
||||||
|
using MusicStore.Spa.Infrastructure;
|
||||||
|
|
||||||
namespace MusicStore.Apis
|
namespace MusicStore.Apis
|
||||||
{
|
{
|
||||||
|
|
@ -19,10 +21,16 @@ namespace MusicStore.Apis
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult> Paged(int page = 1, int pageSize = 50, string sortBy = null)
|
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
|
var albums = await _storeContext.Albums
|
||||||
//.Include(a => a.Genre)
|
//.Include(a => a.Genre)
|
||||||
//.Include(a => a.Artist)
|
//.Include(a => a.Artist)
|
||||||
.ToPagedListAsync(page, pageSize, sortBy, a => a.Title);
|
.ToPagedListAsync(page, pageSize, sortBy,
|
||||||
|
a => a.Title, // sortExpression
|
||||||
|
SortDirection.Ascending, // defaultSortDirection
|
||||||
|
a => SimpleMapper.Map(a, new AlbumResultDto())); // selector
|
||||||
|
|
||||||
return Json(albums);
|
return Json(albums);
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +44,7 @@ namespace MusicStore.Apis
|
||||||
.OrderBy(a => a.Title)
|
.OrderBy(a => a.Title)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Json(albums);
|
return Json(albums.Select(a => SimpleMapper.Map(a, new AlbumResultDto())));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("mostPopular")]
|
[HttpGet("mostPopular")]
|
||||||
|
|
@ -48,60 +56,70 @@ namespace MusicStore.Apis
|
||||||
.Take(count)
|
.Take(count)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Json(albums);
|
// TODO: Move the .Select() to end of albums query when EF supports it
|
||||||
|
return Json(albums.Select(a => SimpleMapper.Map(a, new AlbumResultDto())));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{albumId:int}")]
|
[HttpGet("{albumId:int}")]
|
||||||
public async Task<ActionResult> Details(int albumId)
|
public async Task<ActionResult> Details(int albumId)
|
||||||
{
|
{
|
||||||
// TODO: Remove this when EF supports related entity loading
|
await _storeContext.Genres.LoadAsync();
|
||||||
await _storeContext.Artists.ToListAsync();
|
await _storeContext.Artists.LoadAsync();
|
||||||
await _storeContext.Genres.ToListAsync();
|
|
||||||
|
|
||||||
// TODO: Make async when EF supports SingleOrDefaultAsync
|
var album = await _storeContext.Albums
|
||||||
var album = _storeContext.Albums
|
|
||||||
//.Include(a => a.Artist)
|
//.Include(a => a.Artist)
|
||||||
//.Include(a => a.Genre)
|
//.Include(a => a.Genre)
|
||||||
.SingleOrDefault(a => a.AlbumId == albumId);
|
.Where(a => a.AlbumId == albumId)
|
||||||
|
.SingleOrDefaultAsync();
|
||||||
|
|
||||||
|
var albumResult = SimpleMapper.Map(album, new AlbumResultDto());
|
||||||
|
|
||||||
|
// TODO: Get these from the related entities when EF supports that again, i.e. when .Include() works
|
||||||
|
//album.Artist.Name = (await _storeContext.Artists.SingleOrDefaultAsync(a => a.ArtistId == album.ArtistId)).Name;
|
||||||
|
//album.Genre.Name = (await _storeContext.Genres.SingleOrDefaultAsync(g => g.GenreId == album.GenreId)).Name;
|
||||||
|
|
||||||
// TODO: Add null checking and return 404 in that case
|
// TODO: Add null checking and return 404 in that case
|
||||||
|
|
||||||
return Json(album);
|
return Json(albumResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize("app-ManageStore", "Allowed")]
|
[Authorize("app-ManageStore", "Allowed")]
|
||||||
public async Task<ActionResult> CreateAlbum()
|
public async Task<ActionResult> CreateAlbum([FromBody]AlbumChangeDto album)
|
||||||
{
|
{
|
||||||
var album = new Album();
|
if (!ModelState.IsValid)
|
||||||
|
|
||||||
//if (!await TryUpdateModelAsync(album, excludeProperties: new[] { "Genre", "Artist", "OrderDetails" }))
|
|
||||||
if (!await TryUpdateModelAsync(album))
|
|
||||||
{
|
{
|
||||||
// Return the model errors
|
// Return the model errors
|
||||||
return new ApiResult(ModelState);
|
return new ApiResult(ModelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the changes to the DB
|
// Save the changes to the DB
|
||||||
await _storeContext.Albums.AddAsync(album);
|
var dbAlbum = new Album();
|
||||||
|
await _storeContext.Albums.AddAsync(SimpleMapper.Map(album, dbAlbum));
|
||||||
await _storeContext.SaveChangesAsync();
|
await _storeContext.SaveChangesAsync();
|
||||||
|
|
||||||
// TODO: Handle missing record, key violations, concurrency issues, etc.
|
// TODO: Handle missing record, key violations, concurrency issues, etc.
|
||||||
|
|
||||||
return new ApiResult
|
return new ApiResult
|
||||||
{
|
{
|
||||||
Data = album.AlbumId,
|
Data = dbAlbum.AlbumId,
|
||||||
Message = "Album created successfully."
|
Message = "Album created successfully."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{albumId:int}/update")]
|
[HttpPut("{albumId:int}/update")]
|
||||||
[Authorize("app-ManageStore", "Allowed")]
|
[Authorize("app-ManageStore", "Allowed")]
|
||||||
public async Task<ActionResult> UpdateAlbum(int albumId)
|
public async Task<ActionResult> UpdateAlbum(int albumId, [FromBody]AlbumChangeDto album)
|
||||||
{
|
{
|
||||||
var album = _storeContext.Albums.SingleOrDefault(a => a.AlbumId == albumId);
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
// Return the model errors
|
||||||
|
return new ApiResult(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
if (album == null)
|
var dbAlbum = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
|
||||||
|
|
||||||
|
if (dbAlbum == null)
|
||||||
{
|
{
|
||||||
return new ApiResult
|
return new ApiResult
|
||||||
{
|
{
|
||||||
|
|
@ -110,14 +128,8 @@ namespace MusicStore.Apis
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (!TryUpdateModel(album, prefix: null, includeProperties: null, excludeProperties: new[] { "Genre", "Artist", "OrderDetails" }))
|
|
||||||
if (!await TryUpdateModelAsync(album))
|
|
||||||
{
|
|
||||||
// Return the model errors
|
|
||||||
return new ApiResult(ModelState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the changes to the DB
|
// Save the changes to the DB
|
||||||
|
SimpleMapper.Map(album, dbAlbum);
|
||||||
await _storeContext.SaveChangesAsync();
|
await _storeContext.SaveChangesAsync();
|
||||||
|
|
||||||
// TODO: Handle missing record, key violations, concurrency issues, etc.
|
// TODO: Handle missing record, key violations, concurrency issues, etc.
|
||||||
|
|
@ -132,8 +144,8 @@ namespace MusicStore.Apis
|
||||||
[Authorize("app-ManageStore", "Allowed")]
|
[Authorize("app-ManageStore", "Allowed")]
|
||||||
public async Task<ActionResult> DeleteAlbum(int albumId)
|
public async Task<ActionResult> DeleteAlbum(int albumId)
|
||||||
{
|
{
|
||||||
//var album = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
|
var album = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
|
||||||
var album = _storeContext.Albums.SingleOrDefault(a => a.AlbumId == albumId);
|
//var album = _storeContext.Albums.SingleOrDefault(a => a.AlbumId == albumId);
|
||||||
|
|
||||||
if (album != null)
|
if (album != null)
|
||||||
{
|
{
|
||||||
|
|
@ -150,5 +162,44 @@ namespace MusicStore.Apis
|
||||||
Message = "Album deleted successfully."
|
Message = "Album deleted successfully."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BuddyType(typeof(Album))]
|
||||||
|
public class AlbumChangeDto
|
||||||
|
{
|
||||||
|
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 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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
|
|
||||||
public static HtmlString ngTextBoxFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
|
public static HtmlString ngTextBoxFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
|
||||||
{
|
{
|
||||||
var helper = html as AngularHtmlHelper<TModel>;
|
|
||||||
if (helper == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("You need to configure the services container to return AngularHtmlHelper<T> for IHtmlHelper<T>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
||||||
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, helper.ViewData, helper.ModelMetadataProvider);
|
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
|
||||||
var ngAttributes = new Dictionary<string, object>();
|
var ngAttributes = new Dictionary<string, object>();
|
||||||
|
|
||||||
ngAttributes["type"] = "text";
|
ngAttributes["type"] = "text";
|
||||||
|
|
@ -92,9 +86,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add attributes for Angular validation
|
// Add attributes for Angular validation
|
||||||
//var clientValidators = metadata.GetValidators(html.ViewContext.Controller.ControllerContext)
|
var clientValidators = html.GetClientValidationRules(metadata, null);
|
||||||
// .SelectMany(v => v.GetClientValidationRules());
|
|
||||||
var clientValidators = helper.GetClientValidators(null, metadata);
|
|
||||||
|
|
||||||
foreach (var validator in clientValidators)
|
foreach (var validator in clientValidators)
|
||||||
{
|
{
|
||||||
|
|
@ -222,15 +214,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
|
|
||||||
public static HtmlString ngDropDownListFor<TModel, TProperty, TDisplayProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, Expression<Func<TModel, TDisplayProperty>> displayExpression, string source, string nullOption, IDictionary<string, object> htmlAttributes)
|
public static HtmlString ngDropDownListFor<TModel, TProperty, TDisplayProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, Expression<Func<TModel, TDisplayProperty>> displayExpression, string source, string nullOption, IDictionary<string, object> htmlAttributes)
|
||||||
{
|
{
|
||||||
var helper = html as AngularHtmlHelper<TModel>;
|
|
||||||
if (helper == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("You need to configure the services container to return AngularHtmlHelper<T> for IHtmlHelper<T>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var propertyExpressionText = ExpressionHelper.GetExpressionText(propertyExpression);
|
var propertyExpressionText = ExpressionHelper.GetExpressionText(propertyExpression);
|
||||||
var displayExpressionText = ExpressionHelper.GetExpressionText(displayExpression);
|
var displayExpressionText = ExpressionHelper.GetExpressionText(displayExpression);
|
||||||
var metadata = ExpressionMetadataProvider.FromLambdaExpression(propertyExpression, helper.ViewData, helper.ModelMetadataProvider);
|
var metadata = ExpressionMetadataProvider.FromLambdaExpression(propertyExpression, html.ViewData, html.MetadataProvider);
|
||||||
var tag = new TagBuilder("select");
|
var tag = new TagBuilder("select");
|
||||||
|
|
||||||
var valueFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyExpressionText);
|
var valueFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyExpressionText);
|
||||||
|
|
@ -255,7 +241,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
tag.InnerHtml = nullOptionTag.ToString();
|
tag.InnerHtml = nullOptionTag.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientValidators = helper.GetClientValidators(null, metadata);
|
var clientValidators = html.GetClientValidationRules(metadata, null);
|
||||||
var isRequired = clientValidators.SingleOrDefault(cv => string.Equals(cv.ValidationType, "required", StringComparison.OrdinalIgnoreCase)) != null;
|
var isRequired = clientValidators.SingleOrDefault(cv => string.Equals(cv.ValidationType, "required", StringComparison.OrdinalIgnoreCase)) != null;
|
||||||
if (isRequired)
|
if (isRequired)
|
||||||
{
|
{
|
||||||
|
|
@ -279,19 +265,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
|
|
||||||
public static HtmlString ngValidationMessageFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, string formName, IDictionary<string, object> htmlAttributes)
|
public static HtmlString ngValidationMessageFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, string formName, IDictionary<string, object> htmlAttributes)
|
||||||
{
|
{
|
||||||
var helper = html as AngularHtmlHelper<TModel>;
|
|
||||||
if (helper == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("You need to configure the services container to return AngularHtmlHelper<T> for IHtmlHelper<T>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
||||||
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, helper.ModelMetadataProvider);
|
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
|
||||||
var modelName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
|
var modelName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
|
||||||
|
|
||||||
//var clientValidators = metadata.GetValidators(html.ViewContext.Controller.ControllerContext)
|
//var clientValidators = metadata.GetValidators(html.ViewContext.Controller.ControllerContext)
|
||||||
// .SelectMany(v => v.GetClientValidationRules());
|
// .SelectMany(v => v.GetClientValidationRules());
|
||||||
var clientValidators = helper.GetClientValidators(null, metadata);
|
var clientValidators = html.GetClientValidationRules(metadata, null);
|
||||||
var tags = new List<TagBuilder>();
|
var tags = new List<TagBuilder>();
|
||||||
|
|
||||||
// Get validation messages from data type
|
// Get validation messages from data type
|
||||||
|
|
@ -334,14 +314,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
|
|
||||||
public static string ngValidationClassFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, string formName, string className)
|
public static string ngValidationClassFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, string formName, string className)
|
||||||
{
|
{
|
||||||
var helper = html as AngularHtmlHelper<TModel>;
|
|
||||||
if (helper == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("You need to configure the services container to return AngularHtmlHelper<T> for IHtmlHelper<T>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
||||||
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, helper.ModelMetadataProvider);
|
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
|
||||||
var modelName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
|
var modelName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
|
||||||
var ngClassFormat = "{{ '{0}' : ({1}.submitAttempted || {1}.{2}.$dirty || {1}.{2}.visited) && {1}.{2}.$invalid }}";
|
var ngClassFormat = "{{ '{0}' : ({1}.submitAttempted || {1}.{2}.$dirty || {1}.{2}.visited) && {1}.{2}.$invalid }}";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Rendering
|
|
||||||
{
|
|
||||||
public class AngularHtmlHelper<TModel> : HtmlHelper<TModel>
|
|
||||||
{
|
|
||||||
public AngularHtmlHelper(
|
|
||||||
IHtmlGenerator generator,
|
|
||||||
ICompositeViewEngine viewEngine,
|
|
||||||
IModelMetadataProvider metadataProvider)
|
|
||||||
: base(generator, viewEngine, metadataProvider)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: These members are required to give helper extensions access to required protected members
|
|
||||||
|
|
||||||
public IModelMetadataProvider ModelMetadataProvider
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return MetadataProvider;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidators(string name, ModelMetadata metadata)
|
|
||||||
{
|
|
||||||
return GetClientValidationRules(metadata, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace MusicStore.Spa.Infrastructure
|
||||||
|
{
|
||||||
|
public class BuddyModelMetadataProvider : DataAnnotationsModelMetadataProvider
|
||||||
|
{
|
||||||
|
protected override CachedDataAnnotationsModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes, Type containerType, Type modelType, string propertyName)
|
||||||
|
{
|
||||||
|
var realTypeMetadata = base.CreateMetadataPrototype(attributes, containerType, modelType, propertyName);
|
||||||
|
var buddyType = BuddyTypeAttribute.GetBuddyType(modelType);
|
||||||
|
|
||||||
|
if (buddyType != null)
|
||||||
|
{
|
||||||
|
var buddyMetadata = base.CreateMetadataPrototype(attributes, containerType, buddyType, propertyName);
|
||||||
|
foreach (var realProperty in realTypeMetadata.Properties)
|
||||||
|
{
|
||||||
|
var buddyProperty = buddyMetadata.Properties.SingleOrDefault(bp => string.Equals(bp.PropertyName, realProperty.PropertyName, StringComparison.Ordinal));
|
||||||
|
if (buddyProperty != null)
|
||||||
|
{
|
||||||
|
// TODO: Only overwrite if the real type doesn't explicitly set it
|
||||||
|
realProperty.IsReadOnly = buddyProperty.IsReadOnly;
|
||||||
|
realProperty.IsRequired = buddyProperty.IsRequired;
|
||||||
|
realProperty.DisplayName = buddyProperty.DisplayName;
|
||||||
|
realProperty.DisplayFormatString = buddyProperty.DisplayFormatString;
|
||||||
|
realProperty.SimpleDisplayText = buddyProperty.SimpleDisplayText;
|
||||||
|
realProperty.DataTypeName = buddyProperty.DataTypeName;
|
||||||
|
realProperty.Description = buddyProperty.Description;
|
||||||
|
realProperty.EditFormatString = buddyProperty.EditFormatString;
|
||||||
|
realProperty.NullDisplayText = buddyProperty.NullDisplayText;
|
||||||
|
realProperty.ShowForDisplay = buddyProperty.ShowForDisplay;
|
||||||
|
realProperty.ShowForEdit = buddyProperty.ShowForEdit;
|
||||||
|
realProperty.TemplateHint = buddyProperty.TemplateHint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return realTypeMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace MusicStore.Spa.Infrastructure
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||||
|
public sealed class BuddyTypeAttribute : Attribute
|
||||||
|
{
|
||||||
|
private readonly Type _metadataBuddyType;
|
||||||
|
private readonly Type _validatorBuddyType;
|
||||||
|
|
||||||
|
public BuddyTypeAttribute(Type buddyType)
|
||||||
|
{
|
||||||
|
_metadataBuddyType = buddyType;
|
||||||
|
_validatorBuddyType = buddyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type MetadataBuddyType
|
||||||
|
{
|
||||||
|
get { return _metadataBuddyType; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type ValidatorBuddyType
|
||||||
|
{
|
||||||
|
get { return _validatorBuddyType; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type GetBuddyType(Type type)
|
||||||
|
{
|
||||||
|
var attribute = type.GetTypeInfo().GetCustomAttribute<BuddyTypeAttribute>();
|
||||||
|
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
return attribute.MetadataBuddyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace MusicStore.Spa.Infrastructure
|
||||||
|
{
|
||||||
|
public class BuddyValidatorProvider : DataAnnotationsModelValidatorProvider
|
||||||
|
{
|
||||||
|
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<Attribute> attributes)
|
||||||
|
{
|
||||||
|
var buddyType = BuddyTypeAttribute.GetBuddyType(metadata.ContainerType ?? metadata.ModelType);
|
||||||
|
|
||||||
|
if (buddyType != null)
|
||||||
|
{
|
||||||
|
var buddyProperty = buddyType.GetTypeInfo().GetDeclaredProperty(metadata.PropertyName);
|
||||||
|
if (buddyProperty != null)
|
||||||
|
{
|
||||||
|
var buddyTypeAttributes = buddyProperty.GetCustomAttributes();
|
||||||
|
// TODO: De-dupe?
|
||||||
|
attributes = attributes.Concat(buddyTypeAttributes);
|
||||||
|
return base.GetValidators(metadata, attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<IModelValidator>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MusicStore.Spa.Infrastructure
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
||||||
|
internal sealed class NotNullAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -65,8 +65,15 @@ namespace MusicStore.Infrastructure
|
||||||
return new PagedList<T>(data, pagingConfig.Page, pagingConfig.PageSize, query.Count());
|
return new PagedList<T>(data, pagingConfig.Page, pagingConfig.PageSize, query.Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async 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)
|
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
|
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)
|
if (query == null)
|
||||||
{
|
{
|
||||||
|
|
@ -99,7 +106,11 @@ namespace MusicStore.Infrastructure
|
||||||
|
|
||||||
var count = await query.CountAsync();
|
var count = await query.CountAsync();
|
||||||
|
|
||||||
return new PagedList<TModel>(data, pagingConfig.Page, pagingConfig.PageSize, count);
|
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)
|
private static int ValidatePagePropertiesAndGetSkipCount(PagingConfig pagingConfig)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace MusicStore.Spa.Infrastructure
|
||||||
|
{
|
||||||
|
public class SimpleMapper
|
||||||
|
{
|
||||||
|
private static readonly Expression _emptyExp = Expression.Empty();
|
||||||
|
private static ConcurrentDictionary<Tuple<Type, Type>, Delegate> _mapCache = new ConcurrentDictionary<Tuple<Type, Type>, Delegate>();
|
||||||
|
|
||||||
|
public static TDest Map<TSource, TDest>(TSource source, TDest dest)
|
||||||
|
{
|
||||||
|
var map = (Func<TSource, TDest, TDest>)_mapCache.GetOrAdd(Tuple.Create(typeof(TSource), typeof(TDest)), _ => MakeMapMethod<TSource, TDest>());
|
||||||
|
return map(source, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Func<TSource, TDest, TDest> MakeMapMethod<TSource, TDest>()
|
||||||
|
{
|
||||||
|
// TODO: Support convention-based mapping, e.g. AlbumTitle <- Album.Title
|
||||||
|
// TODO: Support mapping to/from fields
|
||||||
|
|
||||||
|
var sourceProps = typeof(TSource).GetRuntimeProperties().ToDictionary(p => p.Name);
|
||||||
|
var destProps = typeof(TDest).GetRuntimeProperties().ToDictionary(p => p.Name);
|
||||||
|
|
||||||
|
var destArg = Expression.Parameter(typeof(TDest), "dest");
|
||||||
|
var srcArg = Expression.Parameter(typeof(TSource), "src");
|
||||||
|
|
||||||
|
var assignments = MakeAssignments(typeof(TSource), typeof(TDest), srcArg, destArg);
|
||||||
|
|
||||||
|
if (!assignments.Any())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("No matching properties were found between the types {0} and {1}", typeof(TSource), typeof(TDest)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var assignmentsBlock = Expression.Block(assignments);
|
||||||
|
var blockExp = Expression.Block(typeof(TDest), assignmentsBlock, destArg);
|
||||||
|
var map = Expression.Lambda<Func<TSource, TDest, TDest>>(blockExp, srcArg, destArg);
|
||||||
|
|
||||||
|
return map.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Expression> MakeAssignments(Type sourceType, Type destType, Expression sourcePropertyExp, Expression destPropertyExp)
|
||||||
|
{
|
||||||
|
var sourceProps = sourceType.GetRuntimeProperties().ToDictionary(p => p.Name);
|
||||||
|
var destProps = destType.GetRuntimeProperties().ToDictionary(p => p.Name);
|
||||||
|
var assignments = new List<Expression>();
|
||||||
|
|
||||||
|
foreach (var srcProp in sourceProps)
|
||||||
|
{
|
||||||
|
if (!srcProp.Value.GetMethod.IsPublic) continue;
|
||||||
|
|
||||||
|
var destProp = destProps.ContainsKey(srcProp.Key) ? destProps[srcProp.Key] : null;
|
||||||
|
if (destProp != null && destProp.SetMethod != null)
|
||||||
|
{
|
||||||
|
var destPropType = destProp.PropertyType;
|
||||||
|
var srcPropType = srcProp.Value.PropertyType;
|
||||||
|
|
||||||
|
var srcPropExp = Expression.Property(sourcePropertyExp, srcProp.Value);
|
||||||
|
var destPropExp = Expression.Property(destPropertyExp, destProp);
|
||||||
|
|
||||||
|
if (destPropType.GetTypeInfo().IsAssignableFrom(srcPropType.GetTypeInfo()) && destProp.SetMethod.IsPublic)
|
||||||
|
{
|
||||||
|
var assignmentExp = Expression.Assign(destPropExp, srcPropExp);
|
||||||
|
// dest.Prop = src.Prop;
|
||||||
|
assignments.Add(assignmentExp);
|
||||||
|
}
|
||||||
|
else if (destProp.GetMethod.IsPublic)
|
||||||
|
{
|
||||||
|
// The properties aren't assignable but they may have members that are
|
||||||
|
var deepAssignmentExp = MakeAssignments(srcPropType, destPropType, srcPropExp, destPropExp);
|
||||||
|
if (deepAssignmentExp.Any())
|
||||||
|
{
|
||||||
|
// Check if dest is null and if so skip for now
|
||||||
|
// - if (dest.Foo != null && src.Foo != null) {
|
||||||
|
// - dest.Foo.Bar = src.Foo.Bar;
|
||||||
|
// - }
|
||||||
|
var nullCheckExp = Expression.And(Expression.NotEqual(destPropExp, Expression.Constant(null)),
|
||||||
|
Expression.NotEqual(srcPropExp, Expression.Constant(null)));
|
||||||
|
var nullCheckExpBlock = Expression.IfThen(nullCheckExp, Expression.Block(deepAssignmentExp));
|
||||||
|
assignments.Add(nullCheckExpBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return assignments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ namespace MusicStore.Models
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(160, MinimumLength = 2)]
|
[StringLength(160, MinimumLength = 2)]
|
||||||
|
[ForcedModelError("fail")]
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,7 @@ namespace MusicStore.Models
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
// TODO: All this configuration needs to be done manually right now.
|
// Configure pluralization
|
||||||
// We can remove this once EF supports the conventions again.
|
|
||||||
builder.Entity<Album>().ForRelational().Table("Albums");
|
builder.Entity<Album>().ForRelational().Table("Albums");
|
||||||
builder.Entity<Artist>().ForRelational().Table("Artists");
|
builder.Entity<Artist>().ForRelational().Table("Artists");
|
||||||
builder.Entity<Order>().ForRelational().Table("Orders");
|
builder.Entity<Order>().ForRelational().Table("Orders");
|
||||||
|
|
@ -35,46 +34,16 @@ namespace MusicStore.Models
|
||||||
builder.Entity<CartItem>().ForRelational().Table("CartItems");
|
builder.Entity<CartItem>().ForRelational().Table("CartItems");
|
||||||
builder.Entity<OrderDetail>().ForRelational().Table("OrderDetails");
|
builder.Entity<OrderDetail>().ForRelational().Table("OrderDetails");
|
||||||
|
|
||||||
builder.Entity<Album>().Key(a => a.AlbumId);
|
|
||||||
builder.Entity<Artist>().Key(a => a.ArtistId);
|
|
||||||
|
|
||||||
builder.Entity<Order>(b =>
|
|
||||||
{
|
|
||||||
b.Key(o => o.OrderId);
|
|
||||||
b.Property(o => o.OrderId)
|
|
||||||
.ForRelational()
|
|
||||||
.Column("[Order]");
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Entity<Genre>().Key(g => g.GenreId);
|
|
||||||
builder.Entity<CartItem>().Key(ci => ci.CartItemId);
|
|
||||||
builder.Entity<OrderDetail>().Key(od => od.OrderDetailId);
|
|
||||||
|
|
||||||
// TODO: Remove this when we start using auto generated values
|
// TODO: Remove this when we start using auto generated values
|
||||||
builder.Entity<Artist>().Property(a => a.ArtistId).GenerateValueOnAdd(generateValue: false);
|
builder.Entity<Artist>().Property(a => a.ArtistId).GenerateValueOnAdd(generateValue: false);
|
||||||
builder.Entity<Album>().Property(a => a.ArtistId).GenerateValueOnAdd(generateValue: false);
|
builder.Entity<Album>().Property(a => a.ArtistId).GenerateValueOnAdd(generateValue: false);
|
||||||
builder.Entity<Genre>().Property(g => g.GenreId).GenerateValueOnAdd(generateValue: false);
|
builder.Entity<Genre>().Property(g => g.GenreId).GenerateValueOnAdd(generateValue: false);
|
||||||
|
|
||||||
builder.Entity<Album>(b =>
|
// TODO: Remove this once convention-based relations work again
|
||||||
{
|
builder.Entity<Album>().ManyToOne(a => a.Artist);
|
||||||
b.ForeignKey<Genre>(a => a.GenreId);
|
builder.Entity<Album>().ManyToOne(a => a.Genre, g => g.Albums);
|
||||||
b.ForeignKey<Artist>(a => a.ArtistId);
|
builder.Entity<Order>().OneToMany(o => o.OrderDetails);
|
||||||
});
|
builder.Entity<Album>().OneToMany(a => a.OrderDetails, od => od.Album);
|
||||||
|
|
||||||
builder.Entity<OrderDetail>(b =>
|
|
||||||
{
|
|
||||||
b.ForeignKey<Album>(a => a.AlbumId);
|
|
||||||
b.ForeignKey<Order>(a => a.OrderId);
|
|
||||||
});
|
|
||||||
|
|
||||||
var genre = builder.Model.GetEntityType(typeof(Genre));
|
|
||||||
var album = builder.Model.GetEntityType(typeof(Album));
|
|
||||||
var artist = builder.Model.GetEntityType(typeof(Artist));
|
|
||||||
var orderDetail = builder.Model.GetEntityType(typeof(OrderDetail));
|
|
||||||
genre.AddNavigation("Albums", album.ForeignKeys.Single(k => k.ReferencedEntityType == genre), pointsToPrincipal: false);
|
|
||||||
album.AddNavigation("OrderDetails", orderDetail.ForeignKeys.Single(k => k.ReferencedEntityType == album), pointsToPrincipal: false);
|
|
||||||
album.AddNavigation("Genre", album.ForeignKeys.Single(k => k.ReferencedEntityType == genre), pointsToPrincipal: true);
|
|
||||||
album.AddNavigation("Artist", album.ForeignKeys.Single(k => k.ReferencedEntityType == artist), pointsToPrincipal: true);
|
|
||||||
|
|
||||||
base.OnModelCreating(builder);
|
base.OnModelCreating(builder);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,12 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<AssemblyName>MusicStore.Spa</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<RootNamespace>MusicStore.Spa</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
<DevelopmentServerPort>5101</DevelopmentServerPort>
|
<DevelopmentServerPort>5101</DevelopmentServerPort>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.FileSystems;
|
using Microsoft.AspNet.FileSystems;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.AspNet.Security.Cookies;
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
|
@ -11,6 +13,7 @@ using Microsoft.Data.Entity;
|
||||||
using Microsoft.Framework.ConfigurationModel;
|
using Microsoft.Framework.ConfigurationModel;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using MusicStore.Models;
|
using MusicStore.Models;
|
||||||
|
using MusicStore.Spa.Infrastructure;
|
||||||
|
|
||||||
namespace MusicStore.Spa
|
namespace MusicStore.Spa
|
||||||
{
|
{
|
||||||
|
|
@ -36,6 +39,11 @@ namespace MusicStore.Spa
|
||||||
// Add MVC services to the service container
|
// Add MVC services to the service container
|
||||||
services.AddMvc();
|
services.AddMvc();
|
||||||
|
|
||||||
|
services.Configure<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ModelValidatorProviders.Add(typeof(BuddyValidatorProvider));
|
||||||
|
});
|
||||||
|
|
||||||
// Add EF services to the service container
|
// Add EF services to the service container
|
||||||
services.AddEntityFramework()
|
services.AddEntityFramework()
|
||||||
.AddSqlServer()
|
.AddSqlServer()
|
||||||
|
|
@ -48,7 +56,7 @@ namespace MusicStore.Spa
|
||||||
services.AddDefaultIdentity<MusicStoreContext, ApplicationUser, IdentityRole>(Configuration);
|
services.AddDefaultIdentity<MusicStoreContext, ApplicationUser, IdentityRole>(Configuration);
|
||||||
|
|
||||||
// Add application services to the service container
|
// Add application services to the service container
|
||||||
services.AddTransient(typeof(IHtmlHelper<>), typeof(AngularHtmlHelper<>));
|
//services.AddTransient<IModelMetadataProvider, BuddyModelMetadataProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,8 @@ module MusicStore.Admin.Catalog {
|
||||||
|
|
||||||
// Reload the view with the new album ID
|
// Reload the view with the new album ID
|
||||||
this._location.path("/albums/" + albumId + "/edit").replace();
|
this._location.path("/albums/" + albumId + "/edit").replace();
|
||||||
|
|
||||||
|
// TODO: Should we reload the data from the server?
|
||||||
} else {
|
} else {
|
||||||
this.alert = alert;
|
this.alert = alert;
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,52 @@
|
||||||
{
|
{
|
||||||
"webroot": "wwwroot",
|
"webroot": "wwwroot",
|
||||||
"exclude": [ "wwwroot/**/*.*", "bower_components/**/*.*", "node_modules/**/*.*", "grunt/**/*.*" ],
|
"exclude": [
|
||||||
"packExclude": [ "bower.json", "package.json", "gruntfile.js", "bower_components/**/*.*", "node_modules/**/*.*", "grunt/**/*.*" ],
|
"wwwroot",
|
||||||
"authors": [
|
"bower_components",
|
||||||
"Microsoft"
|
"node_modules",
|
||||||
],
|
"grunt"
|
||||||
"description": "Music store application on K as a SPA",
|
],
|
||||||
"compilationOptions": { "define": [ "DEBUG" ] },
|
"packExclude": [
|
||||||
"dependencies": {
|
"bower.json",
|
||||||
"Kestrel": "1.0.0-*",
|
"package.json",
|
||||||
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
"gruntfile.js",
|
||||||
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
"bower_components",
|
||||||
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
"node_modules",
|
||||||
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
"grunt"
|
||||||
"EntityFramework.InMemory": "7.0.0-*",
|
],
|
||||||
"EntityFramework.SqlServer": "7.0.0-*",
|
"authors": [
|
||||||
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
|
"Microsoft"
|
||||||
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-*",
|
],
|
||||||
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
"description": "Music store application on K as a SPA",
|
||||||
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*"
|
"compilationOptions": {
|
||||||
},
|
"define": [
|
||||||
"commands": {
|
"DEBUG"
|
||||||
"WebListener": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5102",
|
]
|
||||||
"Kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5104",
|
},
|
||||||
"run": "run server.urls=http://localhost:5103"
|
"dependencies": {
|
||||||
},
|
"Kestrel": "1.0.0-*",
|
||||||
"scripts": {
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
"postrestore": [ "npm install" ],
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
"prepare": [ "grunt bower:install" ]
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
},
|
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
||||||
"frameworks": {
|
"EntityFramework.InMemory": "7.0.0-*",
|
||||||
"aspnet50": { },
|
"EntityFramework.SqlServer": "7.0.0-*",
|
||||||
"aspnetcore50": { }
|
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
|
||||||
}
|
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-*",
|
||||||
}
|
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
||||||
|
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"WebListener": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5102",
|
||||||
|
"Kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5104",
|
||||||
|
"run": "run server.urls=http://localhost:5103"
|
||||||
|
},
|
||||||
|
/*"scripts": {
|
||||||
|
"postrestore": [ "npm install" ],
|
||||||
|
"prepare": [ "grunt bower:install" ]
|
||||||
|
},*/
|
||||||
|
"frameworks": {
|
||||||
|
"aspnet50": {},
|
||||||
|
"aspnetcore50": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1 +1,8 @@
|
||||||
/// <autosync enabled="true" />
|
/// <autosync enabled="true" />
|
||||||
|
/// <reference path="../wwwroot/Scripts/bootstrap.js" />
|
||||||
|
/// <reference path="../wwwroot/Scripts/jquery-2.0.3.js" />
|
||||||
|
/// <reference path="../wwwroot/Scripts/jquery.signalR-2.0.1.js" />
|
||||||
|
/// <reference path="../wwwroot/Scripts/jquery.validate.js" />
|
||||||
|
/// <reference path="../wwwroot/Scripts/jquery.validate.unobtrusive.js" />
|
||||||
|
/// <reference path="../wwwroot/Scripts/modernizr-2.6.2.js" />
|
||||||
|
/// <reference path="../wwwroot/Scripts/respond.js" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue