這篇文章主要紀錄目前自己將網站的分類方式與檔案內容做簡單紀錄並分享
採用MVC並將業務邏輯層(Service)與資料層(Repository)抽象
網站本體以"WebSite"為基底 名稱
有切分為幾個專案
網站主體有幾個重要檔案
appsetting.json
放置參數檔、資料庫連線字串
Program.cs
註冊各種網站啟動時的內容
資料庫連線註冊
builder.Services.AddDbContext<WebSiteContext>(options =>
{
var Connect = builder.Configuration["ConnectionStrings:WebSiteConnectString"];
options.UseSqlServer(builder.Configuration["ConnectionStrings:WebSiteConnectString"], sqlServerOptionsAction: sqlOptions => {
sqlOptions.EnableRetryOnFailure();
});
});
builder.Services.AddQuartz(quartz =>
{
quartz.UseMicrosoftDependencyInjectionJobFactory();
var SyncWebSiteJobKey = new JobKey("WebSiteJob", "SyncWebSiteGroup");
quartz.AddJob<SyncWebSiteJob>(opts =>
{
opts.WithIdentity(SyncWebSiteJobKey);
opts.StoreDurably();
});
//觸發器
quartz.AddTrigger(opts =>
{
opts.ForJob(SyncWebSiteJobKey);
opts.WithIdentity("SyncWebSiteTrigger", "SyncWebSiteGroup");
//opts.WithCronSchedule("0 0 1 * * ?").
opts.WithCronSchedule("0 55 0 * * ?").
WithDescription("SyncWebSite 凌晨0點55分");
});
builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
}
//DI註冊
//WebSite Entity
builder.Services.AddScoped<IWebSiteService, WebSiteService>();
builder.Services.AddScoped<IWebSiteRepository, WebSiteRepository>();
builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly);
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(2);
options.Cookie.HttpOnly = false;
options.Cookie.IsEssential = true;
});
ErrorHandler.RegistEenumError<CommonError>();
/// <summary>
/// 註冊錯誤代碼
/// </summary>
/// <typeparam name="TEnum"></typeparam>
public static void RegistEenumError<TEnum>() where TEnum : struct, Enum
{
var enums = Enum.GetValues(typeof(TEnum));
var Type = typeof(TEnum);
foreach (int item in enums)
{
CustomError customError = new CustomError();
customError.ErrorCode = item;
string? name = Enum.GetName(Type, item);
var displayAttribute = Type.GetMember(name!)
.First()
.GetCustomAttribute<DisplayAttribute>();
if (displayAttribute is not null)
{
customError.Message = displayAttribute!.Name ?? "未定義錯誤顯示名稱,請洽網站管理員";
}
if (CustomErrors.Exists(x => x.ErrorCode == customError.ErrorCode && x.Message == customError.Message))
{
continue;
}
CustomErrors.Add(customError);
}
}
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseSession();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Login}/{action=Login}/{id?}");
//API Route
app.MapControllerRoute(
name: "api",
pattern: "api/{controller=LineIndex}/{action=Login}/{id?}");
app.Run();
就是拿來放DTO檔案的,通常會參照資料庫格式複製同名DTO以便AutoMapper做轉換
會在Program.cs註冊MappingProfile
在DTO專案底下放置註冊檔
屬性名稱轉換 如果屬性名稱相同,AutoMapper會嘗試直接轉換
public class MappingProfile : Profile
{
public MappingProfile()
{
//WebSite
CreateMap<WebSite, WebSiteDTO>();
CreateMap<WebSiteDTO, WebSite>();
}
}
CreateMap<WebSite, SelectListItem>().
ForMember(x => x.Text, y => y.MapFrom(o => o.Name)).
ForMember(x => x.Value, y => y.MapFrom(o => o.DepartmentId));
CreateMap<WebSiteRow, WebSiteDTO>()
.ForMember(x => x.CustomInternalID, opt => opt.MapFrom<WebSiteIDResolver>());
public class WebSiteResolver : IValueResolver<WebSiteRow, WebSiteDTO, int>
{
public int Resolve(WebSiteRow source, WebSiteDTO destination, int destMember, ResolutionContext context)
{
return source.Basic.Entity?.SearchValue?.InternalId != null ? int.Parse(source.Basic.Entity.SearchValue.InternalId) : 0;
}
}
因為我是用Code First
所以會有Entitys與Migration資料夾
放置兩個資料夾Services、Repositorys
處理各個Service與Repository的介面定義放置
定義各個table的資料倉儲以及定義unitOfWork
基本上共通Function會有一個BaseRepository去處理
沒有太特殊的資料取得通常不太會需要特別增加Reposiotory取資料的方法
public class WebSiteRepository:BaseRepository<WebSite>, IWebSiteRepository
{
private readonly WebSiteContext _db;
public WebSiteRepository(WebSiteContext db) : base(db)
{
_db = db;
}
}
public abstract class BaseRepository<T> : IBaseRepository<T> where T : class
{
private JSContext _db;
protected BaseRepository(JSContext db)
{
_db = db;
}
public void Create(T entity)
{
_db.Set<T>().Add(entity);
}
public IEnumerable<T> GetByPageList(Expression<Func<T, bool>> filter, PageList pageList, string[] includes =null,string[] orderbyDesc = null)
{
List<T> result;
IQueryable<T> query = _db.Set<T>();
if (includes!=null)
{
foreach (string include in includes)
{
query = (IQueryable<T>)query.Include(include);
}
}
if (filter != null)
{
query = query.Where(filter);
}
if (orderbyDesc != null && orderbyDesc.Length > 0)
{
string orderByClause = string.Join(", ", orderbyDesc.Select(p => p + " descending"));
query = query.OrderBy(orderByClause);
}
var TotalObject = query.ToList();
result = PickPageList(pageList, TotalObject);
return result;
}
public void SaveChanges()
{
_db.SaveChanges();
}
}
放置業務邏輯層
通常建構子會注入需要用到的Repository 如果多個table操作寫入則使用UnitOfWork
讀取則使includes 去關聯相關資料
供給View資料顯示的Model
請注意ViewModel一定不會有Entity的引用,必定都是轉換為DTO才傳遞
以上為基本.NetCore 6 專案基本架構紀錄分享 如有更詳細或是更好的方式請分享留言於底下