...
 
Commits (15)
......@@ -27,7 +27,9 @@ bld/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
#wwwroot
Source/Web/MvcCoreTemplate.Web/wwwroot/lib/
# MSTest test Results
[Tt]est[Rr]esult*/
......
namespace MvcCoreTemplate.Data.Common
{
using System;
public interface IDbQueryRunner : IDisposable
{
void RunQuery(string query, params object[] parameters);
}
}
namespace MvcCoreTemplate.Data.Common.Models
{
using System;
public abstract class BaseDeletableModel<TKey> : BaseModel<TKey>, IDeletableEntity
{
public bool IsDeleted { get; set; }
public DateTime? DeletedOn { get; set; }
}
}
namespace MvcCoreTemplate.Data.Common.Models
{
using System;
using System.ComponentModel.DataAnnotations;
public abstract class BaseModel<TKey> : IAuditInfo
{
[Key]
public TKey Id { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
}
}
namespace MvcCoreTemplate.Data.Common.Models
{
using System;
public interface IAuditInfo
{
DateTime CreatedOn { get; set; }
DateTime? ModifiedOn { get; set; }
}
}
namespace MvcCoreTemplate.Data.Common.Models
{
using System;
public interface IDeletableEntity
{
bool IsDeleted { get; set; }
DateTime? DeletedOn { get; set; }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta006" PrivateAssets="All" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.1" />
</ItemGroup>
</Project>
\ No newline at end of file
namespace MvcCoreTemplate.Data.Common.Repositories
{
using System.Linq;
using System.Threading.Tasks;
using MvcCoreTemplate.Data.Common.Models;
public interface IDeletableEntityRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IDeletableEntity
{
IQueryable<TEntity> AllWithDeleted();
IQueryable<TEntity> AllAsNoTrackingWithDeleted();
Task<TEntity> GetByIdWithDeletedAsync(params object[] id);
void HardDelete(TEntity entity);
void Undelete(TEntity entity);
}
}
namespace MvcCoreTemplate.Data.Common.Repositories
{
using System;
using System.Linq;
using System.Threading.Tasks;
public interface IRepository<TEntity> : IDisposable
where TEntity : class
{
IQueryable<TEntity> All();
IQueryable<TEntity> AllAsNoTracking();
Task<TEntity> GetByIdAsync(params object[] id);
void Add(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
Task<int> SaveChangesAsync();
}
}
// ReSharper disable VirtualMemberCallInConstructor
namespace MvcCoreTemplate.Data.Models
{
using System;
using MvcCoreTemplate.Data.Common.Models;
using Microsoft.AspNetCore.Identity;
public class ApplicationRole : IdentityRole, IAuditInfo, IDeletableEntity
{
public ApplicationRole()
: this(null)
{
}
public ApplicationRole(string name)
: base(name)
{
this.Id = Guid.NewGuid().ToString();
}
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletedOn { get; set; }
}
}
// ReSharper disable VirtualMemberCallInConstructor
namespace MvcCoreTemplate.Data.Models
{
using System;
using System.Collections.Generic;
using MvcCoreTemplate.Data.Common.Models;
using Microsoft.AspNetCore.Identity;
public class ApplicationUser : IdentityUser, IAuditInfo, IDeletableEntity
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
this.Roles = new HashSet<IdentityUserRole<string>>();
this.Claims = new HashSet<IdentityUserClaim<string>>();
this.Logins = new HashSet<IdentityUserLogin<string>>();
}
// Audit info
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
// Deletable entity
public bool IsDeleted { get; set; }
public DateTime? DeletedOn { get; set; }
public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta006" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MvcCoreTemplate.Data.Common\MvcCoreTemplate.Data.Common.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
namespace MvcCoreTemplate.Data.Models
{
using MvcCoreTemplate.Data.Common.Models;
public class Setting : BaseDeletableModel<int>
{
public string Name { get; set; }
public string Value { get; set; }
}
}
namespace MvcCoreTemplate.Data
{
using System;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using MvcCoreTemplate.Data.Common.Models;
using MvcCoreTemplate.Data.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
private static readonly MethodInfo SetIsDeletedQueryFilterMethod =
typeof(ApplicationDbContext).GetMethod(
nameof(SetIsDeletedQueryFilter),
BindingFlags.NonPublic | BindingFlags.Static);
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Setting> Settings { get; set; }
public override int SaveChanges() => this.SaveChanges(true);
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
this.ApplyAuditInfoRules();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) =>
this.SaveChangesAsync(true, cancellationToken);
public override Task<int> SaveChangesAsync(
bool acceptAllChangesOnSuccess,
CancellationToken cancellationToken = default)
{
this.ApplyAuditInfoRules();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
protected override void OnModelCreating(ModelBuilder builder)
{
// Needed for Identity models configuration
base.OnModelCreating(builder);
ConfigureUserIdentityRelations(builder);
EntityIndexesConfiguration.Configure(builder);
var entityTypes = builder.Model.GetEntityTypes().ToList();
// Set global query filter for not deleted entities only
var deletableEntityTypes = entityTypes
.Where(et => et.ClrType != null && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType));
foreach (var deletableEntityType in deletableEntityTypes)
{
var method = SetIsDeletedQueryFilterMethod.MakeGenericMethod(deletableEntityType.ClrType);
method.Invoke(null, new object[] { builder });
}
// Disable cascade delete
var foreignKeys = entityTypes
.SelectMany(e => e.GetForeignKeys().Where(f => f.DeleteBehavior == DeleteBehavior.Cascade));
foreach (var foreignKey in foreignKeys)
{
foreignKey.DeleteBehavior = DeleteBehavior.Restrict;
}
}
private static void ConfigureUserIdentityRelations(ModelBuilder builder)
{
builder.Entity<ApplicationUser>()
.HasMany(e => e.Claims)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Logins)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ApplicationUser>()
.HasMany(e => e.Roles)
.WithOne()
.HasForeignKey(e => e.UserId)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
}
private static void SetIsDeletedQueryFilter<T>(ModelBuilder builder)
where T : class, IDeletableEntity
{
builder.Entity<T>().HasQueryFilter(e => !e.IsDeleted);
}
private void ApplyAuditInfoRules()
{
var changedEntries = this.ChangeTracker
.Entries()
.Where(e =>
e.Entity is IAuditInfo &&
(e.State == EntityState.Added || e.State == EntityState.Modified));
foreach (var entry in changedEntries)
{
var entity = (IAuditInfo)entry.Entity;
if (entry.State == EntityState.Added && entity.CreatedOn == default)
{
entity.CreatedOn = DateTime.UtcNow;
}
else
{
entity.ModifiedOn = DateTime.UtcNow;
}
}
}
}
}
namespace MvcCoreTemplate.Data
{
using System.Security.Claims;
using MvcCoreTemplate.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
public class ApplicationRoleStore : RoleStore<
ApplicationRole,
ApplicationDbContext,
string,
IdentityUserRole<string>,
IdentityRoleClaim<string>>
{
public ApplicationRoleStore(ApplicationDbContext context, IdentityErrorDescriber describer = null)
: base(context, describer)
{
}
protected override IdentityRoleClaim<string> CreateRoleClaim(ApplicationRole role, Claim claim) =>
new IdentityRoleClaim<string>
{
RoleId = role.Id,
ClaimType = claim.Type,
ClaimValue = claim.Value
};
}
}
namespace MvcCoreTemplate.Data
{
using System.Security.Claims;
using MvcCoreTemplate.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
public class ApplicationUserStore : UserStore<
ApplicationUser,
ApplicationRole,
ApplicationDbContext,
string,
IdentityUserClaim<string>,
IdentityUserRole<string>,
IdentityUserLogin<string>,
IdentityUserToken<string>,
IdentityRoleClaim<string>>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null)
: base(context, describer)
{
}
protected override IdentityUserRole<string> CreateUserRole(ApplicationUser user, ApplicationRole role)
{
return new IdentityUserRole<string> { RoleId = role.Id, UserId = user.Id };
}
protected override IdentityUserClaim<string> CreateUserClaim(ApplicationUser user, Claim claim)
{
var identityUserClaim = new IdentityUserClaim<string> { UserId = user.Id };
identityUserClaim.InitializeFromClaim(claim);
return identityUserClaim;
}
protected override IdentityUserLogin<string> CreateUserLogin(ApplicationUser user, UserLoginInfo login) =>
new IdentityUserLogin<string>
{
UserId = user.Id,
ProviderKey = login.ProviderKey,
LoginProvider = login.LoginProvider,
ProviderDisplayName = login.ProviderDisplayName
};
protected override IdentityUserToken<string> CreateUserToken(
ApplicationUser user,
string loginProvider,
string name,
string value)
{
var token = new IdentityUserToken<string>
{
UserId = user.Id,
LoginProvider = loginProvider,
Name = name,
Value = value
};
return token;
}
}
}
namespace MvcCoreTemplate.Data
{
using System;
using MvcCoreTemplate.Data.Common;
using Microsoft.EntityFrameworkCore;
public class DbQueryRunner : IDbQueryRunner
{
public DbQueryRunner(ApplicationDbContext context)
{
this.Context = context ?? throw new ArgumentNullException(nameof(context));
}
public ApplicationDbContext Context { get; set; }
public void RunQuery(string query, params object[] parameters)
{
this.Context.Database.ExecuteSqlCommand(query, parameters);
}
public void Dispose()
{
this.Context?.Dispose();
}
}
}
namespace MvcCoreTemplate.Data
{
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
// Stop client query evaluation
builder.ConfigureWarnings(w => w.Throw(RelationalEventId.QueryClientEvaluationWarning));
return new ApplicationDbContext(builder.Options);
}
}
}
namespace MvcCoreTemplate.Data
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage;
internal static class EfExpressionHelper
{
private static readonly Type StringType = typeof(string);
private static readonly MethodInfo ValueBufferGetValueMethod =
typeof(ValueBuffer).GetRuntimeProperties().Single(p => p.GetIndexParameters().Any()).GetMethod;
private static readonly MethodInfo EfPropertyMethod =
typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(Property));
public static Expression<Func<TEntity, bool>> BuildByIdPredicate<TEntity>(
DbContext dbContext,
object[] id)
where TEntity : class
{
if (id == null)
{
throw new ArgumentNullException(nameof(id));
}
var entityType = typeof(TEntity);
var entityParameter = Expression.Parameter(entityType, "e");
var keyProperties = dbContext.Model.FindEntityType(entityType).FindPrimaryKey().Properties;
var predicate = BuildPredicate(keyProperties, new ValueBuffer(id), entityParameter);
return Expression.Lambda<Func<TEntity, bool>>(predicate, entityParameter);
}
private static BinaryExpression BuildPredicate(
IReadOnlyList<IProperty> keyProperties,
ValueBuffer keyValues,
ParameterExpression entityParameter)
{
var keyValuesConstant = Expression.Constant(keyValues);
BinaryExpression predicate = null;
for (var i = 0; i < keyProperties.Count; i++)
{
var property = keyProperties[i];
var equalsExpression =
Expression.Equal(
Expression.Call(
EfPropertyMethod.MakeGenericMethod(property.ClrType),
entityParameter,
Expression.Constant(property.Name, StringType)),
Expression.Convert(
Expression.Call(
keyValuesConstant,
ValueBufferGetValueMethod,
Expression.Constant(i)),
property.ClrType));
predicate = predicate == null ? equalsExpression : Expression.AndAlso(predicate, equalsExpression);
}
return predicate;
}
}
}
namespace MvcCoreTemplate.Data
{
using System.Linq;
using MvcCoreTemplate.Data.Common.Models;
using Microsoft.EntityFrameworkCore;
internal static class EntityIndexesConfiguration
{
public static void Configure(ModelBuilder modelBuilder)
{
// IDeletableEntity.IsDeleted index
var deletableEntityTypes = modelBuilder.Model
.GetEntityTypes()
.Where(et => et.ClrType != null && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType));
foreach (var deletableEntityType in deletableEntityTypes)
{
modelBuilder.Entity(deletableEntityType.ClrType).HasIndex(nameof(IDeletableEntity.IsDeleted));
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta006" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MvcCoreTemplate.Common\MvcCoreTemplate.Common.csproj" />
<ProjectReference Include="..\MvcCoreTemplate.Data.Common\MvcCoreTemplate.Data.Common.csproj" />
<ProjectReference Include="..\MvcCoreTemplate.Data.Models\MvcCoreTemplate.Data.Models.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
namespace MvcCoreTemplate.Data.Repositories
{
using System;
using System.Linq;
using System.Threading.Tasks;
using MvcCoreTemplate.Data.Common.Models;
using MvcCoreTemplate.Data.Common.Repositories;
using Microsoft.EntityFrameworkCore;
public class EfDeletableEntityRepository<TEntity> : EfRepository<TEntity>, IDeletableEntityRepository<TEntity>
where TEntity : class, IDeletableEntity
{
public EfDeletableEntityRepository(ApplicationDbContext context)
: base(context)
{
}
public override IQueryable<TEntity> All() => base.All().Where(x => !x.IsDeleted);
public override IQueryable<TEntity> AllAsNoTracking() => base.AllAsNoTracking().Where(x => !x.IsDeleted);
public IQueryable<TEntity> AllWithDeleted() => base.All().IgnoreQueryFilters();
public IQueryable<TEntity> AllAsNoTrackingWithDeleted() => base.AllAsNoTracking().IgnoreQueryFilters();
public override async Task<TEntity> GetByIdAsync(params object[] id)
{
var entity = await base.GetByIdAsync(id);
if (entity?.IsDeleted ?? false)
{
entity = null;
}
return entity;
}
public Task<TEntity> GetByIdWithDeletedAsync(params object[] id)
{
var byIdPredicate = EfExpressionHelper.BuildByIdPredicate<TEntity>(this.Context, id);
return this.AllWithDeleted().FirstOrDefaultAsync(byIdPredicate);
}
public void HardDelete(TEntity entity)
{
base.Delete(entity);
}
public void Undelete(TEntity entity)
{
entity.IsDeleted = false;
entity.DeletedOn = null;
this.Update(entity);
}
public override void Delete(TEntity entity)
{
entity.IsDeleted = true;
entity.DeletedOn = DateTime.UtcNow;
this.Update(entity);
}
}
}
namespace MvcCoreTemplate.Data.Repositories
{
using System;
using System.Linq;
using System.Threading.Tasks;
using MvcCoreTemplate.Data.Common.Repositories;
using Microsoft.EntityFrameworkCore;
public class EfRepository<TEntity> : IRepository<TEntity>
where TEntity : class
{
public EfRepository(ApplicationDbContext context)
{
this.Context = context ?? throw new ArgumentNullException(nameof(context));
this.DbSet = this.Context.Set<TEntity>();
}
protected DbSet<TEntity> DbSet { get; set; }
protected ApplicationDbContext Context { get; set; }
public virtual IQueryable<TEntity> All() => this.DbSet;
public virtual IQueryable<TEntity> AllAsNoTracking() => this.DbSet.AsNoTracking();
public virtual Task<TEntity> GetByIdAsync(params object[] id) => this.DbSet.FindAsync(id);
public virtual void Add(TEntity entity)
{
this.DbSet.Add(entity);
}
public virtual void Update(TEntity entity)
{
var entry = this.Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
this.DbSet.Attach(entity);
}
entry.State = EntityState.Modified;
}
public virtual void Delete(TEntity entity)
{
this.DbSet.Remove(entity);
}
public Task<int> SaveChangesAsync() => this.Context.SaveChangesAsync();
public void Dispose() => this.Context.Dispose();
}
}
namespace MvcCoreTemplate.Data.Seeding
{
using System;
using System.Linq;
using MvcCoreTemplate.Common;
using MvcCoreTemplate.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
public static class ApplicationDbContextSeeder
{
public static void Seed(ApplicationDbContext dbContext, IServiceProvider serviceProvider)
{
if (dbContext == null)
{
throw new ArgumentNullException(nameof(dbContext));
}
if (serviceProvider == null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}
var roleManager = serviceProvider.GetRequiredService<RoleManager<ApplicationRole>>();
Seed(dbContext, roleManager);
}
public static void Seed(ApplicationDbContext dbContext, RoleManager<ApplicationRole> roleManager)
{
if (dbContext == null)
{
throw new ArgumentNullException(nameof(dbContext));
}
if (roleManager == null)
{
throw new ArgumentNullException(nameof(roleManager));
}
SeedRoles(roleManager);
}
private static void SeedRoles(RoleManager<ApplicationRole> roleManager)
{
SeedRole(GlobalConstants.AdministratorRoleName, roleManager);
}
private static void SeedRole(string roleName, RoleManager<ApplicationRole> roleManager)
{
var role = roleManager.FindByNameAsync(roleName).GetAwaiter().GetResult();
if (role == null)
{
var result = roleManager.CreateAsync(new ApplicationRole(roleName)).GetAwaiter().GetResult();
if (!result.Succeeded)
{
throw new Exception(string.Join(Environment.NewLine, result.Errors.Select(e => e.Description)));
}
}
}
}
}
{
"ConnectionStrings": {
"DefaultConnection": "Server=.;Database=MvcCoreTemplate;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
using System;
namespace MvcCoreTemplate.Common
namespace MvcCoreTemplate.Common
{
public class GlobalConstants
public static class GlobalConstants
{
public const string AdministratorRoleName = "Administrator";
public const string AppName = "MvcCoreTemplate";
}
}
namespace MvcCoreTemplate.Common.Mapping
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AutoMapper;
public static class AutoMapperConfig
{
public static void RegisterMappings(params Assembly[] assemblies)
{
var types = assemblies.SelectMany(a => a.GetExportedTypes()).ToList();
Mapper.Initialize(configuration =>
{
RegisterStandardFromMappings(configuration, types);
RegisterStandardToMappings(configuration, types);
RegisterCustomMaps(configuration, types);
});
}
private static void RegisterStandardFromMappings(IProfileExpression configuration, IEnumerable<Type> types)
{
var maps = GetFromMaps(types);
CreateMappings(configuration, maps);
}
private static void RegisterStandardToMappings(IProfileExpression configuration, IEnumerable<Type> types)
{
var maps = GetToMaps(types);
CreateMappings(configuration, maps);
}
private static void RegisterCustomMaps(IMapperConfigurationExpression configuration, IEnumerable<Type> types)
{
var maps = GetCustomMappings(types);
CreateMappings(configuration, maps);
}
private static IEnumerable<IHaveCustomMappings> GetCustomMappings(IEnumerable<Type> types)
{
var customMaps = from t in types
from i in t.GetTypeInfo().GetInterfaces()
where typeof(IHaveCustomMappings).GetTypeInfo().IsAssignableFrom(t) &&
!t.GetTypeInfo().IsAbstract &&
!t.GetTypeInfo().IsInterface
select (IHaveCustomMappings)Activator.CreateInstance(t);
return customMaps;
}
private static IEnumerable<TypesMap> GetFromMaps(IEnumerable<Type> types)
{
var fromMaps = from t in types
from i in t.GetTypeInfo().GetInterfaces()
where i.GetTypeInfo().IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IMapFrom<>) &&
!t.GetTypeInfo().IsAbstract &&
!t.GetTypeInfo().IsInterface
select new TypesMap
{
Source = i.GetTypeInfo().GetGenericArguments()[0],
Destination = t
};
return fromMaps;
}
private static IEnumerable<TypesMap> GetToMaps(IEnumerable<Type> types)
{
var toMaps = from t in types
from i in t.GetTypeInfo().GetInterfaces()
where i.GetTypeInfo().IsGenericType &&
i.GetTypeInfo().GetGenericTypeDefinition() == typeof(IMapTo<>) &&
!t.GetTypeInfo().IsAbstract &&
!t.GetTypeInfo().IsInterface
select new TypesMap
{
Source = t,
Destination = i.GetTypeInfo().GetGenericArguments()[0]
};
return toMaps;
}
private static void CreateMappings(IProfileExpression configuration, IEnumerable<TypesMap> maps)
{
foreach (var map in maps)
{
configuration.CreateMap(map.Source, map.Destination);
}
}
private static void CreateMappings(IMapperConfigurationExpression configuration, IEnumerable<IHaveCustomMappings> maps)
{
foreach (var map in maps)
{
map.CreateMappings(configuration);
}
}
}
}
namespace MvcCoreTemplate.Common.Mapping
{
using AutoMapper;
public interface IHaveCustomMappings
{
void CreateMappings(IMapperConfigurationExpression configuration);
}
}
namespace MvcCoreTemplate.Common.Mapping
{
// ReSharper disable once UnusedTypeParameter
public interface IMapFrom<T>
{
}
}
namespace MvcCoreTemplate.Common.Mapping
{
// ReSharper disable once UnusedTypeParameter
public interface IMapTo<T>
{
}
}
namespace MvcCoreTemplate.Common.Mapping
{
using System;
using System.Linq;
using System.Linq.Expressions;
using AutoMapper.QueryableExtensions;
public static class QueryableMappingExtensions
{
public static IQueryable<TDestination> To<TDestination>(
this IQueryable source,
params Expression<Func<TDestination, object>>[] membersToExpand)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
return source.ProjectTo(membersToExpand);
}
public static IQueryable<TDestination> To<TDestination>(
this IQueryable source,
object parameters)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
return source.ProjectTo<TDestination>(parameters);
}
}
}
namespace MvcCoreTemplate.Common.Mapping
{
using System;
internal class TypesMap
{
public Type Source { get; set; }
public Type Destination { get; set; }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="6.2.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta006" PrivateAssets="All" />
</ItemGroup>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
\ No newline at end of file
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using System;
namespace MvcCoreTemplate.Data.Models
{
public class ApplicationUser : IdentityUser
{
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.1" />
</ItemGroup>
</Project>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using MvcCoreTemplate.Data.Models;
namespace MvcCoreTemplate.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MvcCoreTemplate.Data.Models\MvcCoreTemplate.Data.Models.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
namespace MvcCoreTemplate.Services.Data
{
public class CommonService : ICommonService
{
public string TestService()
{
return "TestService";
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace MvcCoreTemplate.Services.Data
{
public interface ICommonService
{
string TestService();
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
\ No newline at end of file
{
"directory": "wwwroot/lib"
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MvcCoreTemplate.Services.Data;
namespace MvcCoreTemplate.Web.Controllers
{
[Produces("application/json")]
[Route("api/BaseApi")]
public class BaseApiController : Controller
{
private ICommonService commonService;
public BaseApiController(ICommonService commonService)
{
this.commonService = commonService;
}
public string TestService()
{
return this.commonService.TestService();
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcCoreTemplate.Services.Data;
namespace MvcCoreTemplate.Web.Controllers
{
public class BaseController : Controller
{
protected ICommonService commonService;
public BaseController(ICommonService commonService)
{
this.commonService = commonService;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcCoreTemplate.Services.Data;
namespace MvcCoreTemplate.Web.Controllers
{
public class HomeController : BaseController
{