• IDE为VS 2022社区版(英文版),DotNet Core版本为.NET 8

创建项目


  • 打开VS,依次点击工具栏文件按钮->新建->项目,搜索框输入"ASP.NET Core",语言选择"C#",在筛选结果中选择"ASP.NET Core Web App (Model-View-Controller)",点击"Next"按钮。

  • 填写自己的Project Name项目名称,如:DotNet.NewLife.Cube,点击Next下一步

选择".NET 8.0"版本,模板选择`Web 应用程序`,最后点击Create创建。

  • 创建完之后,项目结构如下。

安装并应用NewLife.Cube.Core


  • 右键点击项目中依赖项->点击管理NuGet程序包->搜索框输入"NewLife.Cube.Core",选中搜索结果,点击安装。

  • Program.cs文件修改如下。注意从.NET 6开始Startup.cs默认没有了。
using NewLife.Cube;
//using NewLife.Cube.WebMiddleware;
//using NewLife.Cube.AdminLTE;
//using NewLife.Cube.ElementUI;
//using NewLife.Cube.LayuiAdmin;
//using NewLife.Cube.Metronic;
//using NewLife.Cube.Tabler;
//using NewLife.Cube.ElementUI;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// 引入星尘,设置监控中间件
//var star = builder.Services.AddStardust(null);
//TracerMiddleware.Tracer = star?.Tracer;

// 启用接口响应压缩
builder.Services.AddResponseCompression();
builder.Services.AddControllersWithViews();
// 启用魔方
builder.Services.AddCube();

var app = builder.Build();

// 加载魔方UI
//app.UseAdminLTE(app.Environment);
//app.UseTabler(app.Environment);
//app.UseMetronic(app.Environment);
//app.UseElementUI(app.Environment);
//app.UseMetronic8(app.Environment);
//app.UseLayuiAdmin(app.Environment);

// 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.UseStaticFiles();

// 使用魔方
app.UseCube(app.Environment);
// 使用魔方首页
app.UseCubeHome();
app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");


app.Run();
  • 编译项目,项目上点击鼠标右键 > View查看,在浏览器中查看,运行魔方平台。

  • 这时候会出现这个SSL证书的提示,选择Yes就对了

  • 一切顺利的话,就能看到魔方的登录页面,默认账号密码都是`admin`,登录进去,可看到内置的控制器。注意:初始化运行需要下载静态资源和驱动,生成菜单,所以出现异常或者页面样式不对,都是正常现象,稍等或重新运行项目即可。

  • 预览的时候可能的报错信息

那么可以看到控制台的报错:


那么也可以看日志或者按F5,会看到具体的报错。

System.DllNotFoundException: 'Unable to load DLL 'SQLite.Interop.dll' or one of its dependencies: 找不到指定的模块。 (0x8007007E)'

需要到bin目录下找到Plugins删除,重新F5会自动重新下载。

添加自己的页面

新建区域

  • 首先新建区域School,手动创建文件夹Areas/School,在此文件夹创建区域类SchoolArea,填入以下内容。
using NewLife.Cube;
using NewLife;
using System.ComponentModel;

namespace DotNet.NewLife.Cube.Areas.School
{
    [DisplayName("教务系统")]
    public class SchoolArea : AreaBase
    {
        public SchoolArea() : base(nameof(SchoolArea).TrimEnd("Area")) { }

        static SchoolArea() => RegisterArea<SchoolArea>();
    }
}

新建实体

  • 新建数据库实体类,参考数据中间件NewLife.XCode教程。新建文件夹Areas/School/Models,将实体类放在此文件夹,具体内容参考这里
  • 我使用了XCodeTool.exe + Model.xml (具体用法参考这里),没用码神工具。

新建控制器

  • 新建文件夹Areas/School/Controllers,新建ClassController、StudentController两个控制器,分别填入以下内容。
using Microsoft.AspNetCore.Mvc;
using NewLife.Cube;
using NewLife.Log;
using NewLife.School.Entity;
using NewLife.Web;
using System.ComponentModel;
using System.Reflection;
using System.Security.Claims;
using XCode.Membership;

namespace DotNet.NewLife.Cube.Areas.School.Controllers
{
    [SchoolArea]
    [DisplayName("班级")]
    public class ClassController : EntityController<Class, ClassModel>
    {
        private readonly ITracer _tracer;

        public ClassController(IServiceProvider provider)
        {

            PageSetting.EnableTableDoubleClick = true;

            _tracer = provider?.GetService<ITracer>();
        }

        protected override IEnumerable<Class> Search(Pager p)
        {
            using var span = _tracer?.NewSpan(nameof(Search), p);

            var id = p["Id"].ToInt(-1);
            if (id > 0)
            {
                var entity = Class.FindById(id);
                return entity == null ? new List<Class>() : new List<Class> { entity };
            }

            var start = p["dtStart"].ToDateTime();
            var end = p["dtEnd"].ToDateTime();

            return Class.Search(start, end, p["Q"], p);
        }
    }
}
using Microsoft.AspNetCore.Mvc;
using NewLife.Cube.ViewModels;
using NewLife.Cube;
using System.ComponentModel;
using System.Reflection;
using XCode.Membership;
using NewLife.School.Entity;
using NewLife.Web;

namespace DotNet.NewLife.Cube.Areas.School.Controllers
{
    [SchoolArea]
    [DisplayName("学生")]
    public class StudentController : EntityController<Student, StudentModel>
    {
        static StudentController()
        {
            ListFields.RemoveField("CreateUserID");
            ListFields.RemoveField("UpdateUserID");
            //FormFields
        }

        protected override Student Find(Object key)
        {
            return base.Find(key);
        }

        protected override IEnumerable<Student> Search(Pager p)
        {
            return base.Search(p);
            //var classid = p["classid"].ToInt();
            //return Student.Search(null,p);
        }

        public override ActionResult Index(Pager p = null)
        {
            return base.Index(p);
        }
    }
}

页面修改

  • 此时运行,一切正常,可看到如下页面。

  • 如果要修改页面,那么直接在对应位置建立对应视图文件即可自动覆盖。详细可覆盖文件参考这里
  • 比如修改学生主页,则新建文件Areas/School/Views/Student/,新建文件_List_Data.cshtml。填入以下内容。
@using NewLife;
@using NewLife.Cube
@using NewLife.Web;
@using XCode;
@using XCode.Configuration;
@using XCode.Membership
@{
    var fact = ViewBag.Factory as IEntityFactory;
    var page = ViewBag.Page as Pager;
    var fields = ViewBag.Fields as List<FieldItem>;
}
<table class="table table-bordered table-hover table-striped table-condensed">
    <thead>
        <tr>
            <th class="text-center hidden-md hidden-sm hidden-xs"><a href="@Html.Raw(page.GetSortUrl("ID"))">编号</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("ClassID"))">班级2</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("Name"))">名称3</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("Sex"))">性别4</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("Age"))">年龄5</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("Mobile"))">手机</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("Address"))">地址</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("CreateTime"))">创建时间</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("CreateIP"))">创建地址</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("UpdateTime"))">更新时间</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("UpdateIP"))">更新地址</a></th>
            <th class="text-center"><a href="@Html.Raw(page.GetSortUrl("Remark"))">备注</a></th>
            @if (this.Has(PermissionFlags.Detail, PermissionFlags.Update, PermissionFlags.Delete))
            {
                <th class="text-center">操作</th>
            }
        </tr>
    </thead>
    <tbody>
        @foreach (var entity in Model)
        {
            <tr>
                <td class="text-center hidden-md hidden-sm hidden-xs">@entity.ID</td>
                <td>@entity.ClassName</td>
                <td>@entity.Name</td>
                <td class="text-center">@entity.Sex</td>
                <td class="text-right">@entity.Age.ToString("n0")</td>
                <td>@entity.Mobile</td>
                <td>@entity.Address</td>
                <td>@Utility.ToFullString(entity.CreateTime, "")</td>
                <td>@entity.CreateIP</td>
                <td>@Utility.ToFullString(entity.UpdateTime, "")</td>
                <td>@entity.UpdateIP</td>
                <td>@entity.Remark</td>
                @if (this.Has(PermissionFlags.Detail, PermissionFlags.Update, PermissionFlags.Delete))
                {
                    <td class="text-center">
                        @await Html.PartialAsync("_List_Data_Action", (Object)entity)
                    </td>
                }
            </tr>
        }
    </tbody>
</table>
  • 修改后页面如下。其他页面均可按照此方式进行覆盖。