├── .gitignore ├── Common └── MasterDataGenerator.cs ├── Controllers ├── AccountController.cs ├── BookController.cs ├── MaterController.cs └── RestErrorFilter.cs ├── LICENSE ├── Logger ├── Log4NetLogger.cs ├── Log4NetProvider.cs └── Log4netExtensions.cs ├── Models ├── Account.cs ├── AccountRole.cs ├── Book.cs ├── Category.cs ├── Dtos │ └── BookDto.cs └── Format.cs ├── Program.cs ├── README.md ├── Repositories ├── AbstractRepository.cs ├── BookRepository.cs └── PagedList.cs ├── Services ├── AccountService.cs ├── BookService.cs └── MasterService.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.Docker.json ├── appsettings.json ├── aspdotnet_managesys.csproj ├── log4net.config └── wwwroot └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode/ 2 | /bin/ 3 | /logs/ 4 | /obj/ 5 | Sample.db 6 | -------------------------------------------------------------------------------- /Common/MasterDataGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using aspdotnet_managesys.Models; 4 | using aspdotnet_managesys.Repositories; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace aspdotnet_managesys.Common 9 | { 10 | public static class MasterDataGenerator 11 | { 12 | public static async void InitializeAsync(IServiceProvider serviceProvider) 13 | { 14 | using (var serviceScope = serviceProvider.GetRequiredService().CreateScope()) 15 | { 16 | BookRepository rep = serviceScope.ServiceProvider.GetService(); 17 | // データベースの初期化 18 | initializeDatabase(rep); 19 | // テストデータの生成 20 | await createTestData(serviceScope, rep); 21 | } 22 | } 23 | 24 | private static void initializeDatabase(BookRepository rep) 25 | { 26 | // データベースの初期化 27 | rep.Database.EnsureDeleted(); 28 | // テーブル生成 29 | rep.Database.EnsureCreated(); 30 | } 31 | 32 | private static async Task createTestData(IServiceScope serviceScope, BookRepository rep) 33 | { 34 | UserManager userManager = serviceScope.ServiceProvider.GetService>(); 35 | var user = new Account("test"); 36 | var password = "Pa$$w0rd"; 37 | var result = await userManager.CreateAsync(user, password); 38 | 39 | if (result.Succeeded) 40 | { 41 | // ダミーデータ挿入 42 | insertDummyData(rep); 43 | } 44 | } 45 | 46 | private static void insertDummyData(BookRepository rep) 47 | { 48 | Format f1 = new Format { Name = "書籍" }; 49 | f1.Save(rep); 50 | 51 | Format f2 = new Format { Name = "電子書籍" }; 52 | f2.Save(rep); 53 | 54 | Category c1 = new Category { Name = "技術書" }; 55 | c1.Save(rep); 56 | 57 | Category c2 = new Category { Name = "小説" }; 58 | c2.Save(rep); 59 | 60 | Category c3 = new Category { Name = "雑誌" }; 61 | c3.Save(rep); 62 | 63 | for (int i = 0; i < 7; i++) 64 | { 65 | Category c = Category.FindById(rep, (i + 1) % 3 + 1); 66 | Format f = Format.FindById(rep, (i + 1) % 2 + 1); 67 | Book b = new Book 68 | { 69 | Title = "Test_" + i, 70 | Isbn = "123-234-567-" + i, 71 | Category = c, 72 | Format = f 73 | }; 74 | b.Save(rep); 75 | } 76 | 77 | rep.SaveChanges(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using aspdotnet_managesys.Models; 3 | using aspdotnet_managesys.Services; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace aspdotnet_managesys.Controllers 9 | { 10 | [Authorize] 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class AccountController : Controller 14 | { 15 | private readonly AccountService _service; 16 | 17 | public AccountController(AccountService service) 18 | { 19 | _service = service; 20 | } 21 | 22 | //ログイン状態を確認します 23 | [HttpGet("loginStatus")] 24 | public IActionResult LoginStatus() 25 | { 26 | return this.Json(true); 27 | } 28 | 29 | [AllowAnonymous] 30 | [HttpPost("login")] 31 | public async Task LogIn([FromServices]SignInManager signInManager, [FromForm] string userName, [FromForm] string passWord) 32 | { 33 | if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(passWord)) 34 | { 35 | return Unauthorized(); 36 | } 37 | 38 | var result = await signInManager.PasswordSignInAsync(userName, passWord, false, false); 39 | 40 | if (result.Succeeded) 41 | { 42 | return Ok(); 43 | } 44 | else 45 | { 46 | return Unauthorized(); 47 | } 48 | } 49 | 50 | [AllowAnonymous] 51 | [HttpPost("logout")] 52 | public async Task LogOut([FromServices]SignInManager signInManager) 53 | { 54 | await signInManager.SignOutAsync(); 55 | return IdentityResult.Success; 56 | } 57 | 58 | [HttpGet("loginAccount")] 59 | public Account loginAccount([FromServices]SignInManager signInManager, [FromServices]UserManager userManager) 60 | { 61 | return signInManager.IsSignedIn(this.User) ? userManager.GetUserAsync(this.User).Result : null; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Controllers/BookController.cs: -------------------------------------------------------------------------------- 1 | using aspdotnet_managesys.Models; 2 | using aspdotnet_managesys.Models.Dtos; 3 | using aspdotnet_managesys.Repositories; 4 | using aspdotnet_managesys.Services; 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace aspdotnet_managesys.Controllers 9 | { 10 | [Authorize] 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class BookController : Controller 14 | { 15 | private readonly BookService service; 16 | 17 | public BookController(BookService service) 18 | { 19 | this.service = service; 20 | } 21 | 22 | [HttpGet("list")] 23 | public PagedList GetAll([FromQuery] PagedList list) 24 | { 25 | return service.FindAllBook(list.Page, list.Size); 26 | } 27 | 28 | [HttpGet("search")] 29 | public PagedList Search([FromQuery] string query, [FromQuery] PagedList list) 30 | { 31 | return service.FindBookByTitle(query, list.Page, list.Size); 32 | } 33 | 34 | [HttpPost("new")] 35 | public Book SaveBook([FromBody] RegBook book) 36 | { 37 | return service.SaveBook(book); 38 | } 39 | 40 | [HttpPost("edit")] 41 | public Book EditBook([FromBody] ChgBook book) 42 | { 43 | return service.UpdateBook(book); 44 | } 45 | 46 | [HttpPost("delete")] 47 | public Book DeleteBook([FromBody] ChgBook book) 48 | { 49 | return service.DeleteBook(book); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Controllers/MaterController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using aspdotnet_managesys.Models; 3 | using aspdotnet_managesys.Services; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace aspdotnet_managesys.Controllers 8 | { 9 | [Authorize] 10 | [Route("api/[controller]")] 11 | [ApiController] 12 | public class MasterController : Controller 13 | { 14 | private readonly MasterService service; 15 | 16 | public MasterController(MasterService service) 17 | { 18 | this.service = service; 19 | } 20 | 21 | [HttpGet("category")] 22 | public IEnumerable ListCategory() 23 | { 24 | return service.FindAllCategories(); 25 | } 26 | 27 | [HttpGet("format")] 28 | public IEnumerable ListFormat() 29 | { 30 | return service.FindAllFormats(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Controllers/RestErrorFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | using Microsoft.AspNetCore.Mvc.ModelBinding; 6 | 7 | namespace aspdotnet_managesys.Controllers 8 | { 9 | public class RestErrorFilter : ActionFilterAttribute 10 | { 11 | // @see https://stackoverflow.com/questions/15296069/how-to-figure-out-which-key-of-modelstate-has-error 12 | public override void OnActionExecuting(ActionExecutingContext context) 13 | { 14 | if (context.ModelState.IsValid == false) 15 | { 16 | IDictionary errors = new Dictionary(); 17 | 18 | foreach (var key in context.ModelState.Keys) 19 | { 20 | foreach (ModelError error in context.ModelState[key].Errors) 21 | { 22 | errors.Add(key, error.ErrorMessage); 23 | } 24 | } 25 | 26 | JsonResult result = new JsonResult(errors); 27 | result.StatusCode = (int)HttpStatusCode.BadRequest; 28 | context.Result = result; 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ybkuroki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Logger/Log4NetLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Xml; 4 | using log4net; 5 | using log4net.Core; 6 | using log4net.Repository; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace aspdotnet_managesys.Logger 10 | { 11 | public class Log4NetLogger : Microsoft.Extensions.Logging.ILogger 12 | { 13 | private readonly string _name; 14 | private readonly XmlElement _xmlElement; 15 | private readonly ILog _log; 16 | private ILoggerRepository _loggerRepository; 17 | public Log4NetLogger(string name, XmlElement xmlElement) 18 | { 19 | _name = name; 20 | _xmlElement = xmlElement; 21 | _loggerRepository = log4net.LogManager.CreateRepository( 22 | Assembly.GetEntryAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy)); 23 | _log = LogManager.GetLogger(_loggerRepository.Name, name); 24 | log4net.Config.XmlConfigurator.Configure(_loggerRepository, xmlElement); 25 | } 26 | 27 | public string Name => throw new NotImplementedException(); 28 | 29 | public ILoggerRepository Repository => throw new NotImplementedException(); 30 | 31 | public IDisposable BeginScope(TState state) 32 | { 33 | return null; 34 | } 35 | 36 | public bool IsEnabled(LogLevel logLevel) 37 | { 38 | switch (logLevel) 39 | { 40 | case LogLevel.Critical: 41 | return _log.IsFatalEnabled; 42 | case LogLevel.Debug: 43 | case LogLevel.Trace: 44 | return _log.IsDebugEnabled; 45 | case LogLevel.Error: 46 | return _log.IsErrorEnabled; 47 | case LogLevel.Information: 48 | return _log.IsInfoEnabled; 49 | case LogLevel.Warning: 50 | return _log.IsWarnEnabled; 51 | default: 52 | throw new ArgumentOutOfRangeException(nameof(logLevel)); 53 | } 54 | } 55 | 56 | public bool IsEnabledFor(Level level) 57 | { 58 | throw new NotImplementedException(); 59 | } 60 | 61 | public void Log(LogLevel logLevel, EventId eventId, TState state, 62 | Exception exception, Func formatter) 63 | { 64 | if (!IsEnabled(logLevel)) 65 | { 66 | return; 67 | } 68 | 69 | if (formatter == null) 70 | { 71 | throw new ArgumentNullException(nameof(formatter)); 72 | } 73 | string message = null; 74 | if (null != formatter) 75 | { 76 | message = formatter(state, exception); 77 | } 78 | if (!string.IsNullOrEmpty(message) || exception != null) 79 | { 80 | switch (logLevel) 81 | { 82 | case LogLevel.Critical: 83 | _log.Fatal(message); 84 | break; 85 | case LogLevel.Debug: 86 | case LogLevel.Trace: 87 | _log.Debug(message); 88 | break; 89 | case LogLevel.Error: 90 | _log.Error(message); 91 | break; 92 | case LogLevel.Information: 93 | _log.Info(message); 94 | break; 95 | case LogLevel.Warning: 96 | _log.Warn(message); 97 | break; 98 | default: 99 | _log.Warn($"Encountered unknown log level {logLevel}, writing out as Info."); 100 | _log.Info(message, exception); 101 | break; 102 | } 103 | } 104 | } 105 | 106 | public void Log(Type callerStackBoundaryDeclaringType, Level level, object message, Exception exception) 107 | { 108 | throw new NotImplementedException(); 109 | } 110 | 111 | public void Log(LoggingEvent logEvent) 112 | { 113 | throw new NotImplementedException(); 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /Logger/Log4NetProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.IO; 3 | using System.Xml; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace aspdotnet_managesys.Logger 7 | { 8 | public class Log4NetProvider : ILoggerProvider 9 | { 10 | private readonly string _log4NetConfigFile; 11 | private readonly ConcurrentDictionary _loggers = 12 | new ConcurrentDictionary(); 13 | public Log4NetProvider(string log4NetConfigFile) 14 | { 15 | _log4NetConfigFile = log4NetConfigFile; 16 | } 17 | 18 | public ILogger CreateLogger(string categoryName) 19 | { 20 | return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation); 21 | } 22 | 23 | public void Dispose() 24 | { 25 | _loggers.Clear(); 26 | } 27 | private Log4NetLogger CreateLoggerImplementation(string name) 28 | { 29 | return new Log4NetLogger(name, Parselog4NetConfigFile(_log4NetConfigFile)); 30 | } 31 | 32 | private static XmlElement Parselog4NetConfigFile(string filename) 33 | { 34 | XmlDocument log4netConfig = new XmlDocument(); 35 | log4netConfig.Load(File.OpenRead(filename)); 36 | return log4netConfig["log4net"]; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Logger/Log4netExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace aspdotnet_managesys.Logger 4 | { 5 | public static class Log4netExtensions 6 | { 7 | public static ILoggerFactory AddLog4Net(this ILoggerFactory factory, string log4NetConfigFile) 8 | { 9 | factory.AddProvider(new Log4NetProvider(log4NetConfigFile)); 10 | return factory; 11 | } 12 | 13 | public static ILoggerFactory AddLog4Net(this ILoggerFactory factory) 14 | { 15 | factory.AddProvider(new Log4NetProvider("log4net.config")); 16 | return factory; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Models/Account.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using aspdotnet_managesys.Repositories; 4 | using Microsoft.AspNetCore.Identity; 5 | 6 | namespace aspdotnet_managesys.Models 7 | { 8 | [Table("ACCOUNT")] 9 | public class Account : IdentityUser 10 | { 11 | 12 | public string name { get; set; } 13 | 14 | public Account() : base() 15 | { 16 | } 17 | 18 | public Account(string userName) : base(userName) 19 | { 20 | this.name = userName; 21 | } 22 | 23 | public static Account FindById(AbstractRepository repo, Guid id) 24 | { 25 | return repo.FindOne(a => a.Id.Equals(id)); 26 | } 27 | 28 | public static Account FindByName(AbstractRepository repo, string name) 29 | { 30 | return repo.FindOne(a => a.NormalizedUserName.Contains(name)); 31 | } 32 | 33 | public void Save(AbstractRepository repo) 34 | { 35 | repo.Save(this); 36 | } 37 | 38 | public void Update(AbstractRepository repo) 39 | { 40 | repo.Change(this); 41 | } 42 | 43 | public void Delete(AbstractRepository repo) 44 | { 45 | repo.Delete(this); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Models/AccountRole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using aspdotnet_managesys.Repositories; 4 | using Microsoft.AspNetCore.Identity; 5 | 6 | namespace aspdotnet_managesys.Models 7 | { 8 | [Table("ACCOUNT_ROLE")] 9 | public class AccountRole : IdentityRole 10 | { 11 | public AccountRole() : base() 12 | { 13 | } 14 | 15 | public AccountRole(string roleName) : base(roleName) 16 | { 17 | } 18 | 19 | public static AccountRole FindById(AbstractRepository repo, Guid id) 20 | { 21 | return repo.FindOne(a => a.Id.Equals(id)); 22 | } 23 | 24 | public static AccountRole FindByName(AbstractRepository repo, string name) 25 | { 26 | return repo.FindOne(a => a.NormalizedName.Contains(name)); 27 | } 28 | 29 | public void Save(AbstractRepository repo) 30 | { 31 | repo.Save(this); 32 | } 33 | 34 | public void Update(AbstractRepository repo) 35 | { 36 | repo.Change(this); 37 | } 38 | 39 | public void Delete(AbstractRepository repo) 40 | { 41 | repo.Delete(this); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Models/Book.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using System.Linq; 5 | using aspdotnet_managesys.Repositories; 6 | 7 | namespace aspdotnet_managesys.Models 8 | { 9 | [Table("BOOK")] 10 | public class Book 11 | { 12 | [Key] 13 | public int Id { get; set; } 14 | 15 | [MaxLength(50, ErrorMessage = "書籍タイトルは、{1}文字以内で入力してください。")] 16 | [MinLength(3, ErrorMessage = "書籍タイトルは、{1}文字以上で入力してください。")] 17 | public string Title { get; set; } 18 | 19 | [MaxLength(20, ErrorMessage = "ISBNは、{1}文字以内で入力してください。")] 20 | [MinLength(10, ErrorMessage = "ISBNは、{1}文字以上で入力してください。")] 21 | public string Isbn { get; set; } 22 | 23 | [Required] 24 | public virtual Format Format { get; set; } 25 | 26 | [Required] 27 | public virtual Category Category { get; set; } 28 | 29 | public static Book FindById(AbstractRepository repo, int id) 30 | { 31 | return BookEagerFetch(repo).Where(b => b.Id == id).FirstOrDefault(); 32 | } 33 | 34 | public static PagedList FindByTitle(AbstractRepository repo, string title, int page, int pageSize) 35 | { 36 | return repo.Find(BookEagerFetch(repo).Where(b => b.Title.Contains(title)), page, pageSize); 37 | } 38 | 39 | public static List FindAll(AbstractRepository repo) 40 | { 41 | return BookEagerFetch(repo).ToList(); 42 | } 43 | 44 | public static PagedList FindAll(AbstractRepository repo, int page, int pageSize) 45 | { 46 | return repo.Find(BookEagerFetch(repo), page, pageSize); 47 | } 48 | 49 | private static IQueryable BookEagerFetch(AbstractRepository repo) 50 | { 51 | var books = repo.EntitySet(); 52 | var categories = repo.EntitySet(); 53 | var formats = repo.EntitySet(); 54 | 55 | var outerJoin = from b in books 56 | join c in categories on b.Category.Id equals c.Id into gs 57 | from g in gs.DefaultIfEmpty() 58 | join f in formats on b.Format.Id equals f.Id into gj 59 | from h in gj.DefaultIfEmpty() 60 | select new Book 61 | { 62 | Id = b.Id, 63 | Title = b.Title, 64 | Isbn = b.Isbn, 65 | Category = g, 66 | Format = h 67 | }; 68 | 69 | return outerJoin; 70 | } 71 | 72 | public void Save(AbstractRepository repo) 73 | { 74 | repo.Save(this); 75 | } 76 | 77 | public void Update(AbstractRepository repo) 78 | { 79 | repo.Change(this); 80 | } 81 | 82 | public void Delete(AbstractRepository repo) 83 | { 84 | repo.Delete(this); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Models/Category.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using aspdotnet_managesys.Repositories; 5 | 6 | namespace aspdotnet_managesys.Models 7 | { 8 | [Table("CATEGORY_MASTER")] 9 | public class Category 10 | { 11 | [Key] 12 | public int Id { get; set; } 13 | 14 | [MinLength(1)] 15 | public string Name { get; set; } 16 | 17 | public static Category FindById(AbstractRepository repo, int id) 18 | { 19 | return repo.FindOne(c => c.Id == id); 20 | } 21 | 22 | public static Category FindByName(AbstractRepository repo, string name) 23 | { 24 | return repo.FindOne(c => c.Name == name); 25 | } 26 | 27 | public static List FindAll(AbstractRepository repo) 28 | { 29 | return repo.FindAll(); 30 | } 31 | 32 | public void Save(AbstractRepository repo) 33 | { 34 | repo.Save(this); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Models/Dtos/BookDto.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace aspdotnet_managesys.Models.Dtos 4 | { 5 | public class RegBook 6 | { 7 | [MaxLength(50, ErrorMessage = "書籍タイトルは、{1}文字以内で入力してください。")] 8 | [MinLength(3, ErrorMessage = "書籍タイトルは、{1}文字以上で入力してください。")] 9 | public string Title { get; set; } 10 | [MaxLength(20, ErrorMessage = "ISBNは、{1}文字以内で入力してください。")] 11 | [MinLength(10, ErrorMessage = "ISBNは、{1}文字以上で入力してください。")] 12 | public string Isbn { get; set; } 13 | [Required] 14 | public int CategoryId { get; set; } 15 | [Required] 16 | public int FormatId { get; set; } 17 | 18 | public Book create() 19 | { 20 | Category c = new Category { Id = CategoryId, Name = "" }; 21 | Format f = new Format { Id = FormatId, Name = "" }; 22 | return new Book 23 | { 24 | Title = this.Title, 25 | Isbn = this.Isbn, 26 | Category = c, 27 | Format = f 28 | }; 29 | } 30 | } 31 | 32 | public class ChgBook 33 | { 34 | [Required] 35 | public int Id { get; set; } 36 | [MaxLength(50, ErrorMessage = "書籍タイトルは、{1}文字以内で入力してください。")] 37 | [MinLength(3, ErrorMessage = "書籍タイトルは、{1}文字以上で入力してください。")] 38 | public string Title { get; set; } 39 | [MaxLength(20, ErrorMessage = "ISBNは、{1}文字以内で入力してください。")] 40 | [MinLength(10, ErrorMessage = "ISBNは、{1}文字以上で入力してください。")] 41 | public string Isbn { get; set; } 42 | [Required] 43 | public int CategoryId { get; set; } 44 | [Required] 45 | public int FormatId { get; set; } 46 | } 47 | } -------------------------------------------------------------------------------- /Models/Format.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using aspdotnet_managesys.Repositories; 5 | 6 | namespace aspdotnet_managesys.Models 7 | { 8 | [Table("FORMAT_MASTER")] 9 | public class Format 10 | { 11 | [Key] 12 | public int Id { get; set; } 13 | 14 | [MinLength(1)] 15 | public string Name { get; set; } 16 | 17 | public static Format FindById(AbstractRepository repo, int id) 18 | { 19 | return repo.FindOne(c => c.Id == id); 20 | } 21 | 22 | public static Format FindByName(AbstractRepository repo, string name) 23 | { 24 | return repo.FindOne(c => c.Name == name); 25 | } 26 | 27 | public static List FindAll(AbstractRepository repo) 28 | { 29 | return repo.FindAll(); 30 | } 31 | 32 | public void Save(AbstractRepository repo) 33 | { 34 | repo.Save(this); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace aspdotnet_managesys 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateWebHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateWebHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseUrls("http://*:8080"); 18 | webBuilder.UseStartup(); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aspnetcore-webapp-sample 2 | 3 | ## Preface 4 | This sample project uses [ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/) and [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/). It provides only Web API. So, I recommend using a [vuejs-webapp-sample](https://github.com/ybkuroki/vuejs-webapp-sample) project as Web Front End. 5 | 6 | ## Install 7 | Perform the following steps: 8 | 9 | 1. Download and install [.NET Core SDK](https://www.microsoft.com/net/download). 10 | 1. Download and install [Visual Studio Code(VS Code)](https://code.visualstudio.com/). 11 | 1. Install [C# for Visual Studio Code (powered by OmniSharp)](https://github.com/OmniSharp/omnisharp-vscode) extension for VS Code. 12 | 1. Clone this repository. 13 | 1. Opening the project folder will download dependencies. 14 | 15 | ## Starting Server 16 | Perform the following steps: 17 | 18 | 1. Open this sample project. 19 | 1. Select **Debug > Start Debugging** in menu bar. 20 | 1. When startup is complete, the console shows the following message: 21 | ``` 22 | Hosting environment: Development 23 | Content root path: [Project Path] 24 | Now listening on: http://[::]:8080 25 | Application started. Press Ctrl+C to shut down. 26 | ``` 27 | 1. Access [http://localhost:8080/api/health](http://localhost:8080/api/health) in your browser and confirm that this application has started. 28 | 1. Log in with the following username and password. 29 | - username : ``test`` 30 | - password : ``Pa$$w0rd`` 31 | 32 | ## Creating a Production Build 33 | Perform the following command: 34 | 35 | ```bash 36 | $ dotnet publish -c Release -o out 37 | ``` 38 | 39 | ## Project Map 40 | The follwing figure is the map of this sample project. 41 | 42 | ``` 43 | - aspnetcore-webapp-sample 44 | + Common … Provide a common service of this system. 45 | + Controllers … Define controllers. 46 | + Models … Define models. 47 | + Repositories … Provide a service of database access. 48 | + Services … Provide a service of book management. 49 | + Logger … Provides logging using Log4Net 50 | - Startup.cs … Define configurations such as database connections, security, and swagger. 51 | - Program.cs … Entry Point. 52 | - log4net.config … The configuration file for Log4Net 53 | ``` 54 | 55 | ## Services 56 | This sample provides 3 services: book management, account management, and master management. Web APIs of this sample can be confirmed from [Swagger](http://localhost:8080/swagger). 57 | 58 | ### Book Management 59 | There are the following services in the book management. 60 | 61 | |Service Name|HTTP Method|URL|Parameter|Summary| 62 | |:---|:---:|:---|:---|:---| 63 | |List Service|GET|``/api/book/list``|Page|Get a list of books.| 64 | |Regist Service|POST|``/api/book/new``|Book|Regist a book data.| 65 | |Edit Service|POST|``/api/book/edit``|Book|Edit a book data.| 66 | |Delete Service|POST|``/api/book/delete``|Book|Delete a book data.| 67 | |Search Title Service|GET|``/api/book/search``|Keyword, Page|Search a title with the specified keyword.| 68 | |Report Service(No implementation)|GET|``/api/book/allListPdfReport``|Nothing|Output a list of books to the PDF file.| 69 | 70 | ### Account Management 71 | There are the following services in the Account management. 72 | 73 | |Service Name|HTTP Method|URL|Parameter|Summary| 74 | |:---|:---:|:---|:---|:---| 75 | |Login Service|POST|``/api/account/login``|Session ID, User Name, Password|Session authentication with username and password.| 76 | |Logout Service|POST|``/api/account/logout``|Session ID|Logout a user.| 77 | |Login Status Check Service|GET|``/api/account/loginStatus``|Session ID|Check if the user is logged in.| 78 | |Login Username Service|GET|``/api/account/loginAccount``|Session ID|Get the login user's username.| 79 | 80 | ### Master Management 81 | There are the following services in the Master management. 82 | 83 | |Service Name|HTTP Method|URL|Parameter|Summary| 84 | |:---|:---:|:---|:---|:---| 85 | |Category List Service|GET|``/api/master/category``|Nothing|Get a list of categories.| 86 | |Format List Service|GET|``/api/master/format``|Nothing|Get a list of formats.| 87 | 88 | ## Libraries 89 | This sample uses the following libraries. 90 | 91 | |Library Name|Version| 92 | |:---|:---:| 93 | |ASP.NET Core|3.1| 94 | |Entity Framework Core|3.1| 95 | |System.Linq|4.3.0| 96 | |Newtonsoft.Json|12.0.3| 97 | |log4net|2.0.8| 98 | |Swashbuckle.AspNetCore|5.0.0-rc5| 99 | 100 | ## License 101 | The License of this sample is *MIT License*. 102 | -------------------------------------------------------------------------------- /Repositories/AbstractRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | namespace aspdotnet_managesys.Repositories 9 | { 10 | public abstract class AbstractRepository : IdentityDbContext 11 | { 12 | public AbstractRepository(DbContextOptions options) : base(options) 13 | { 14 | } 15 | 16 | public DbSet EntitySet() where TEntity : class 17 | { 18 | return this.Set(); 19 | } 20 | 21 | public TEntity FindOne(Expression> predicate) where TEntity : class 22 | { 23 | return EntitySet().FirstOrDefault(predicate); 24 | } 25 | 26 | public List Find(Expression> predicate) where TEntity : class 27 | { 28 | return EntitySet().Where(predicate).ToList(); 29 | } 30 | 31 | public PagedList Find(IQueryable query, int page, int pageSize) where TEntity : class 32 | { 33 | var result = new PagedList(); 34 | result.Page = page; 35 | result.Size = pageSize; 36 | result.NumberOfElements = pageSize; 37 | 38 | //IQueryable query = EntitySet().Where(predicate); 39 | result.TotalElements = query.Count(); 40 | 41 | var pageCount = (double)result.TotalElements / pageSize; 42 | result.TotalPages = (int)Math.Ceiling(pageCount); 43 | 44 | var skip = page * pageSize; 45 | result.Content = query.Skip(skip).Take(pageSize).ToList(); 46 | 47 | return result; 48 | } 49 | 50 | public List FindAll() where TEntity : class 51 | { 52 | return EntitySet().ToList(); 53 | } 54 | 55 | public TEntity Save(TEntity entity) where TEntity : class 56 | { 57 | EntitySet().Add(entity); 58 | SaveChanges(); 59 | return entity; 60 | } 61 | 62 | public TEntity Change(TEntity entity) where TEntity : class 63 | { 64 | EntitySet().Update(entity); 65 | SaveChanges(); 66 | return entity; 67 | } 68 | 69 | public TEntity Delete(TEntity entity) where TEntity : class 70 | { 71 | EntitySet().Remove(entity); 72 | SaveChanges(); 73 | return entity; 74 | } 75 | 76 | public TResult Transaction(Func func) 77 | { 78 | using (var transaction = this.Database.BeginTransaction()) 79 | { 80 | try 81 | { 82 | var ret = func(); 83 | SaveChanges(); 84 | transaction.Commit(); 85 | return ret; 86 | } 87 | catch 88 | { 89 | transaction.Rollback(); 90 | return default(TResult); 91 | } 92 | } 93 | } 94 | 95 | public void Transaction(Action func) 96 | { 97 | Transaction(() => { func(); return true; }); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /Repositories/BookRepository.cs: -------------------------------------------------------------------------------- 1 | using aspdotnet_managesys.Models; 2 | using aspdotnet_managesys.Common; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Logging; 7 | using System.Collections.Generic; 8 | using aspdotnet_managesys.Logger; 9 | 10 | namespace aspdotnet_managesys.Repositories 11 | { 12 | public class BookRepository : AbstractRepository 13 | { 14 | public BookRepository(DbContextOptions options) : base(options) 15 | { 16 | // ロガーの設定 17 | var serviceProvider = this.GetInfrastructure(); 18 | var loggerFactory = serviceProvider.GetService(); 19 | loggerFactory.AddProvider(new Log4NetProvider("log4net.config")); 20 | } 21 | 22 | public DbSet Books { get; set; } 23 | public DbSet Formats { get; set; } 24 | public DbSet Categories { get; set; } 25 | 26 | public DbSet Accounts { get; set; } 27 | public DbSet AccountRoles { get; set; } 28 | } 29 | } -------------------------------------------------------------------------------- /Repositories/PagedList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace aspdotnet_managesys.Repositories 5 | { 6 | public class PagedList where T : class 7 | { 8 | public IList Content { get; set; } 9 | 10 | public Boolean Last { get; set; } 11 | 12 | public int TotalElements { get; set; } 13 | 14 | public int TotalPages { get; set; } 15 | 16 | public int Size { get; set; } 17 | 18 | public int Page { get; set; } 19 | 20 | public int NumberOfElements { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /Services/AccountService.cs: -------------------------------------------------------------------------------- 1 | 2 | using aspdotnet_managesys.Repositories; 3 | 4 | namespace aspdotnet_managesys.Services 5 | { 6 | public class AccountService 7 | { 8 | private readonly BookRepository repo; 9 | 10 | public AccountService(BookRepository rep) 11 | { 12 | repo = rep; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Services/BookService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using aspdotnet_managesys.Models; 4 | using aspdotnet_managesys.Models.Dtos; 5 | using aspdotnet_managesys.Repositories; 6 | 7 | namespace aspdotnet_managesys.Services 8 | { 9 | public class BookService 10 | { 11 | private readonly BookRepository repo; 12 | 13 | public BookService(BookRepository rep) 14 | { 15 | repo = rep; 16 | } 17 | 18 | public List FindAllBook() 19 | { 20 | return repo.Transaction(() => 21 | { 22 | return Book.FindAll(repo); 23 | }); 24 | } 25 | 26 | public Book FindById(int id) 27 | { 28 | return repo.Transaction(() => 29 | { 30 | return Book.FindById(repo, id); 31 | }); 32 | } 33 | 34 | public PagedList FindBookByTitle(String keyword, int number, int size) 35 | { 36 | return repo.Transaction(() => 37 | { 38 | return Book.FindByTitle(repo, keyword, number, size); 39 | }); 40 | } 41 | 42 | public PagedList FindAllBook(int number, int size) 43 | { 44 | return repo.Transaction(() => 45 | { 46 | return Book.FindAll(repo, number, size); 47 | }); 48 | } 49 | 50 | public Book SaveBook(RegBook book) 51 | { 52 | return repo.Transaction(() => 53 | { 54 | Category category = Category.FindById(repo, book.CategoryId); 55 | Format format = Format.FindById(repo, book.FormatId); 56 | 57 | Book entity = book.create(); 58 | if (category != null && format != null) 59 | { 60 | entity.Category = category; 61 | entity.Format = format; 62 | entity.Save(repo); 63 | return entity; 64 | } 65 | return null; 66 | }); 67 | } 68 | 69 | public Book UpdateBook(ChgBook book) 70 | { 71 | return repo.Transaction(() => 72 | { 73 | Book entity = Book.FindById(repo, book.Id); 74 | 75 | if (entity != null) 76 | { 77 | entity.Title = book.Title; 78 | entity.Isbn = book.Isbn; 79 | 80 | Category category = Category.FindById(repo, book.CategoryId); 81 | Format format = Format.FindById(repo, book.FormatId); 82 | 83 | if (category != null && format != null) 84 | { 85 | entity.Category = category; 86 | entity.Format = format; 87 | entity.Update(repo); 88 | return entity; 89 | } 90 | return null; 91 | } 92 | return null; 93 | }); 94 | } 95 | 96 | public Book DeleteBook(ChgBook book) 97 | { 98 | return repo.Transaction(() => 99 | { 100 | Book entity = Book.FindById(repo, book.Id); 101 | if (entity != null) 102 | { 103 | entity.Delete(repo); 104 | return entity; 105 | } 106 | return null; 107 | }); 108 | } 109 | 110 | } 111 | } -------------------------------------------------------------------------------- /Services/MasterService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using aspdotnet_managesys.Models; 4 | using aspdotnet_managesys.Repositories; 5 | 6 | namespace aspdotnet_managesys.Services 7 | { 8 | public class MasterService 9 | { 10 | private readonly BookRepository repo; 11 | 12 | public MasterService(BookRepository rep) 13 | { 14 | repo = rep; 15 | } 16 | 17 | public List FindAllCategories() 18 | { 19 | return repo.Transaction(() => Category.FindAll(repo)); 20 | } 21 | 22 | public void RegisterCategory(Category category) 23 | { 24 | if (!IsExistCategory(category)) 25 | { 26 | category.Save(repo); 27 | } 28 | } 29 | 30 | public Category GetCategory(int id) 31 | { 32 | return Category.FindById(repo, id); 33 | } 34 | 35 | private Boolean IsExistCategory(Category category) 36 | { 37 | Category c = Category.FindByName(repo, category.Name); 38 | return (c == null) ? false : true; 39 | } 40 | 41 | public List FindAllFormats() 42 | { 43 | return repo.Transaction(() => Format.FindAll(repo)); 44 | } 45 | 46 | public void RegisterCategory(Format format) 47 | { 48 | if (!IsExistFormat(format)) 49 | { 50 | format.Save(repo); 51 | } 52 | } 53 | 54 | public Format GetFormat(int id) 55 | { 56 | return Format.FindById(repo, id); 57 | } 58 | 59 | private Boolean IsExistFormat(Format format) 60 | { 61 | Format f = Format.FindByName(repo, format.Name); 62 | return (f == null) ? false : true; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Startup.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Threading.Tasks; 3 | using aspdotnet_managesys.Common; 4 | using aspdotnet_managesys.Controllers; 5 | using aspdotnet_managesys.Logger; 6 | using aspdotnet_managesys.Models; 7 | using aspdotnet_managesys.Repositories; 8 | using aspdotnet_managesys.Services; 9 | using Microsoft.AspNetCore.Builder; 10 | using Microsoft.AspNetCore.Hosting; 11 | using Microsoft.AspNetCore.Http; 12 | using Microsoft.AspNetCore.Identity; 13 | using Microsoft.AspNetCore.Mvc; 14 | using Microsoft.EntityFrameworkCore; 15 | using Microsoft.Extensions.Configuration; 16 | using Microsoft.Extensions.DependencyInjection; 17 | using Microsoft.Extensions.Logging; 18 | using Newtonsoft.Json.Converters; 19 | using Newtonsoft.Json.Serialization; 20 | using Microsoft.Extensions.Hosting; 21 | using Microsoft.OpenApi.Models; 22 | 23 | namespace aspdotnet_managesys 24 | { 25 | public class Startup 26 | { 27 | public Startup(IConfiguration configuration) 28 | { 29 | Configuration = configuration; 30 | } 31 | 32 | public IConfiguration Configuration { get; } 33 | 34 | // This method gets called by the runtime. Use this method to add services to the container. 35 | public void ConfigureServices(IServiceCollection services) 36 | { 37 | services.Configure(options => 38 | { 39 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 40 | options.CheckConsentNeeded = context => true; 41 | options.MinimumSameSitePolicy = SameSiteMode.None; 42 | }); 43 | 44 | // データベースのヘルスチェックを有効化する 45 | services.AddHealthChecks() 46 | .AddDbContextCheck(); 47 | 48 | // サービスの登録(DI) 49 | services.AddScoped(); 50 | services.AddScoped(); 51 | services.AddScoped(); 52 | 53 | if (string.IsNullOrEmpty(Configuration.GetConnectionString("NpgSqlConnection"))) 54 | { 55 | // EntityFrameworkCoreにリポジトリを登録 56 | services.AddDbContext(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); 57 | } 58 | else 59 | { 60 | // EntityFrameworkCoreにリポジトリを登録 61 | services.AddDbContext(options => options.UseNpgsql(Configuration.GetConnectionString("NpgSqlConnection"))); 62 | } 63 | 64 | // ASP.NET Core MVCの設定 65 | services 66 | .AddControllers() 67 | .SetCompatibilityVersion(CompatibilityVersion.Version_3_0) 68 | .AddMvcOptions(options => 69 | { 70 | // バリデーション例外処理 71 | options.Filters.Add(new RestErrorFilter()); 72 | }) 73 | .ConfigureApiBehaviorOptions(options => 74 | { 75 | // 自動的な 400 応答を無効にする 76 | // ref : https://docs.microsoft.com/ja-jp/aspnet/core/web-api/?view=aspnetcore-3.0#disable-automatic-400-response 77 | options.SuppressModelStateInvalidFilter = true; 78 | }) 79 | .AddNewtonsoftJson(options => 80 | { 81 | // JSONデータのカスタマイズ 82 | options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 83 | options.SerializerSettings.Converters.Add(new StringEnumConverter()); 84 | }); 85 | 86 | // ASP.NET Core Identityの認証設定 87 | services 88 | .AddIdentity() 89 | .AddEntityFrameworkStores() 90 | .AddDefaultTokenProviders(); 91 | 92 | // Cookie認証を利用する、認証失敗時は401を返す 93 | services.ConfigureApplicationCookie(options => 94 | { 95 | options.Events.OnRedirectToLogin = context => 96 | { 97 | context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; 98 | return Task.CompletedTask; 99 | }; 100 | }); 101 | 102 | // CORS設定 103 | services 104 | .AddCors(options => 105 | options.AddPolicy("AllowAll", p => p.WithOrigins("http://localhost:3000", "http://localhost").AllowAnyMethod().AllowAnyHeader().AllowCredentials())); 106 | 107 | // Register the Swagger generator, defining 1 or more Swagger documents 108 | services.AddSwaggerGen(c => 109 | { 110 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "ASP.NET Core Web App Sample API", Version = "v1" }); 111 | }); 112 | } 113 | 114 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 115 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 116 | { 117 | // Log4Netによるロギングを有効にする 118 | loggerFactory.AddLog4Net(); 119 | 120 | // Enable middleware to serve generated Swagger as a JSON endpoint. 121 | app.UseSwagger(); 122 | 123 | // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 124 | // specifying the Swagger JSON endpoint. 125 | app.UseSwaggerUI(c => 126 | { 127 | c.SwaggerEndpoint("/swagger/v1/swagger.json", "ASP.NET Core Web App Sample API V1"); 128 | }); 129 | 130 | // ヘルスチェックを有効にする 131 | app.UseHealthChecks("/api/health", port: 8080); 132 | app.UseRouting(); 133 | 134 | if (env.IsDevelopment()) 135 | { 136 | app.UseDeveloperExceptionPage(); 137 | app.UseCors("AllowAll"); 138 | MasterDataGenerator.InitializeAsync(app.ApplicationServices); 139 | } 140 | 141 | if (env.IsEnvironment("Docker")) 142 | { 143 | app.UseCors("AllowAll"); 144 | } 145 | 146 | app.UseCookiePolicy(); 147 | app.UseAuthentication(); 148 | app.UseAuthorization(); 149 | 150 | app.UseEndpoints(endpoints => 151 | { 152 | endpoints.MapControllers(); 153 | }); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /appsettings.Docker.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "NpgSqlConnection": "Host=postgres-db;Port=5432;Database=testdb;User ID=testusr;Password=testusr;" 4 | }, 5 | "Logging": { 6 | "IncludeScopes": false, 7 | "Debug": { 8 | "LogLevel": { 9 | "Default": "Warning" 10 | } 11 | }, 12 | "Console": { 13 | "LogLevel": { 14 | "Default": "Warning" 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Filename=./Sample.db" 4 | }, 5 | "Logging": { 6 | "IncludeScopes": false, 7 | "Debug": { 8 | "LogLevel": { 9 | "Default": "Warning" 10 | } 11 | }, 12 | "Console": { 13 | "LogLevel": { 14 | "Default": "Warning" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /aspdotnet_managesys.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp3.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /log4net.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /wwwroot/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/super-dev03/c-test-app/db08e4637a7491d99a73e79d6685721d6888e391/wwwroot/.gitkeep --------------------------------------------------------------------------------