目录
1.创建演示项目
1.1 DemoSqlForms.App
1.2 Platz.SqlForms NuGet包
1.3数据库项目
2.设置演示数据库
3. SqlForms动态页面
3.1 CourseEditForm和CourseEdit.razor页面
3.2 CourseListForm和CourseList.razor页面
3.3 StudentListForm和StudentList.razor页面
3.4 StudentEditForm和StudentEdit.razor页面
4. Platz.ObjectBuilder
4.1代码生成
4.2 EnrollmentListForm和EnrollmentList.razor页面
4.3登记编辑和删除
5.总结
5.1下一步
附录
设置演示数据库
SchoolContext
DbInitializer
连接字符串
Program.cs
Startup.cs
当您需要为客户构建有效的原型时,或者您的公司没有用于企业发展的预算时,您别无选择,并且需要使用一些捷径和生活技巧,通常是低代码或无代码方法。在这篇文章中,我提出了一种有趣的方法,它关于如何使用开源库Platz.SqlForms快速开发Blazor UI。SqlForms将提供SPA用户体验,并且无需您进行任何编码即可与数据库进行通信,您所要做的就是定义UI表单,为要作为UI控件显示的EF实体提供流畅的符号定义。
让我们首先使用Visual Studio 2019“创建新项目”链接创建Blazor Server App .NET 5.0项目DemoSqlForms.App。
然后找到“Blazor App”模板,选择它,然后单击“下一步”按钮。
在下一个屏幕上,指定项目名称:DemoSqlForms.App和解决方案名称:DemoSqlForms,然后单击“创建”按钮。
现在选择“.NET 5.0 ”和“Blazor Server App”模板,然后单击“创建”按钮。
Visual Studio将使用项目创建解决方案。
我想花一些时间删除示例页面(Counter和FetchData)及其相关代码,但这不是必需的。
现在,我们需要添加Platz.SqlForms NuGet软件包,右键单击解决方案项目,然后单击“Manage NuGet Packages…”菜单,然后在“浏览”选项卡中键入“Platz”搜索模式,您将看到Platz软件包。选择Platz.SqlForms并单击“安装”按钮。
安装后,您将看到一个带有简单说明的readme.txt文件,请遵循它们。
重要的步骤是在ConfigureServices 方法中添加Platz.SqlForms初始化逻辑:
services.AddPlatzSqlForms();
为了演示如何使用Platz.SqlForms,我们将需要创建一个数据库项目。
右键单击“DemoSqlForms”解决方案(解决方案资源管理器中的第一行),单击“添加”,然后单击“新建项目…”。
在“添加新项目”向导中,找到“类库(.NET Core)”模板,将其选中并单击“下一步”。
在项目名称中键入“DemoSqlForms.Database”,然后单击“创建”。
Visual Studio将创建一个新的类库项目并将其添加到解决方案中。
我们需要确保目标框架是“.NET 5.0”,右键单击项目“DemoSqlForms.Database”,然后单击“Properties”。
选择目标框架“.NET 5.0”和
您可以在本文的附录中看到如何设置演示数据库——它与我们演示的方法无关,并且许多人都知道如何使用Entity Framework,因此,我不想在此花费您的时间。
我只应该说,对于此演示,我们需要SchoolContext数据库上下文以及以下带有一些测试数据的实体:
SqlForms的主要思想是为开发人员提供一个工具,使他们能够以C#类型安全的方式定义UI。具有Entity Framework实体或您自己的POCO对象意味着您可以定义要显示的特定属性,要使用的UI控件,使其成为强制性或可选性来提交,以及附加业务规则以验证输入。
让我们从[ Course]实体开始,向“DemoSqlForms.App”项目添加一个新文件夹“Forms” ,然后创建一个CourseEditForm类。
using DemoSqlForms.Database.Model;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App.Forms
{
public class CourseEditForm : DynamicEditFormBase
{
protected override void Define(DynamicFormBuilder builder)
{
builder.Entity(e =>
{
e.Property(p => p.CourseID).IsPrimaryKey().IsUnique();
e.Property(p => p.Title).IsRequired();
e.Property(p => p.Credits).IsRequired();
e.DialogButton(ButtonActionTypes.Cancel).DialogButton
(ButtonActionTypes.Submit);
e.DialogButtonNavigation("CourseList", ButtonActionTypes.Cancel,
ButtonActionTypes.Delete, ButtonActionTypes.Submit);
});
}
}
}
您可以看到[CourseEditForm]继承自具有类型参数[SchoolContext]的[DynamicEditFormBase
我们重写[ Define]方法并在其中提供表单定义。
代码[ builder.Entity
现在我们需要指定如何显示每个属性:
e.Property(p => p.CourseID).IsPrimaryKey().IsUnique();
这意味着CourseID是一个主键,并且具有唯一性约束。IsRequired()表示如果此属性的值为空,则不会提交表单。
方法DialogButton用于指定要显示的按钮。
方法DialogButtonNavigation用于将导航操作分配给一组按钮。因此,下一行...
e.DialogButtonNavigation("CourseList", ButtonActionTypes.Cancel,
ButtonActionTypes.Delete, ButtonActionTypes.Submit);
表示单击“取消”、“删除”或“提交”按钮时,应用程序将重定向到/CourseList链接。
可以在项目Wiki页面上找到Form Definition的完整规范:
现在,在定义表单后,我们可以将新的razor页面添加到Pages文件夹CourseEdit.razor中。
@page "/CourseEdit/{CourseId:int}"
@page "/CourseEdit"
Course Edit
@code {
[Parameter]
public int CourseId { get; set; }
}
现在,如果您运行该应用程序并将其/CourseEdit添加到浏览器路径,您将看到从定义呈现的编辑页面。因为我们没有提供Id值,所以它将在数据库中创建一个新Course记录。
如果你点击“提交”,你会看到,验证了CourseID和Title*失败。
因为CourseID是主键,但不是自动递增的,所以您可以指定任何整数值,除了0和已经使用的整数值外,对于自动递增的主键,输入始终是只读的。
如果使用值(100, C#, 4) 填充表单,然后单击Submit,则表单将在数据库中创建新记录并重定向到/CourseList,该记录尚未实现。
列表形式的定义有些不同,但是我们使用类似的方法BTW,这是我们在Entity Framework实体定义中发现的,请查看SchoolContext.cs的这段代码:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(entity =>
{
entity.HasOne(d => d.Course)
.WithMany(p => p.Enrollments)
.HasForeignKey(d => d.CourseID)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Enrollment_Course");
entity.HasOne(d => d.Student)
.WithMany(p => p.Enrollments)
.HasForeignKey(d => d.StudentID)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Enrollment_Student");
});
}
因此,“课程列表”表单将如下所示:
using DemoSqlForms.Database.Model;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App.Forms
{
public class CourseListForm : DataServiceBase
{
protected override void Define(DataServiceFormBuilder builder)
{
builder.Entity(e =>
{
e.ExcludeAll();
e.Property(p => p.CourseID).IsPrimaryKey();
e.Property(p => p.Title);
e.Property(p => p.Credits);
// Parameter {0} is always PrimaryKey, parameters {1} and above - Filter Keys
// {0} = AddressId {1} = CustomerId
e.ContextButton("Edit", "CourseEdit/{0}").ContextButton
("Delete", "CourseDelete/{0}");
e.DialogButton("CourseEdit/0", ButtonActionTypes.Add);
});
builder.SetListMethod(GetCourseList);
}
public List GetCourseList(params object[] parameters)
{
using (var db = GetDbContext())
{
var query =
from s in db.Course
select new Course
{
CourseID = s.CourseID,
Title = s.Title,
Credits = s.Credits
};
var result = query.ToList();
return result;
}
}
}
}
现在类CourseListForm开始从DataServiceBase
首先,我们用e.ExcludeAll();从定义中删除所有属性,我们在不想显示所有内容时执行此操作。
其次,我们指定要按看到顺序显示的所有列。
接下来,我们在一行中定义上下文菜单:
e.ContextButton("Edit", "CourseEdit/{0}").ContextButton("Delete", "CourseDelete/{0}");
我们在此处提供按钮的文本和导航链接。链接部分“{0}”是记录主键的占位符,当用户在某行上单击此按钮时,主键值将从行中提取并放置到占位符,例如对于主键值17,我们将获得产生的导航链接“CourseEdit/17 ”。
然后,我们使用DialogButton来显示带有链接“CourseEdit/0”的“添加”按钮,并且“0”表示执行了编辑页面以创建新记录。
最后,我们需要指定返回数据以显示在页面上的方法(“SetListMethod”)。GetCourseList使用LINQ从数据库返回所有课程。
定义就绪后,我们可以添加razor页面:
@page "/CourseList"
Courses
@code {
}
我们使用FormDataServiceListComponent并将我们的定义设置为TForm参数。
我们还需要在Shared文件夹中进行修改NavMenu.razor,并将CourseList页面包含在左侧菜单中,另外,我还包含了指向StudentList页面的链接,我们将在下一步中实现它。
Student List
Course List
如果立即运行该应用程序,您将看到:
如果单击“课程列表”,您将看到:
您可以使用“添加”按钮将更多课程添加到数据库中,也可以使用Actions上下文菜单来编辑记录。
如果添加CourseDelete.razor页面,我们也可以删除课程记录。
@page "/CourseDelete/{CourseId:int}"
Delete Course
@code {
[Parameter]
public int CourseId { get; set; }
}
该页面具有路由[ @page "/CourseDelete/{CourseId:int}“,并且重复使用了CourseEditForm我们提供的ForDelete="true",并且此参数告诉SqlForms表单应该是只读的,并包含一个“ Delete”按钮。
如您所见,所有insert、update和delete操作都是由SqlForms完成的,我们只需要创建查询以选择课程记录即可。
学生名单的Form定义与CourseList非常相似。
using DemoSqlForms.Database.Model;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App.Forms
{
public class StudentListForm : DataServiceBase
{
protected override void Define(DataServiceFormBuilder builder)
{
builder.Entity(e =>
{
e.ExcludeAll();
e.Property(p => p.ID).IsPrimaryKey();
e.Property(p => p.FirstMidName);
e.Property(p => p.LastName);
e.Property(p => p.EnrollmentDate).Format("dd-MMM-yyyy");
e.Property(p => p.EnrollmentCount);
// Parameter {0} is always PrimaryKey, parameters {1} and above - Filter Keys
// {0} = AddressId {1} = CustomerId
e.ContextButton("Edit", "StudentEdit/{0}").ContextButton
("Delete", "StudentDelete/{0}").ContextButton
("Enrollments", "EnrollmentList/{0}");
e.DialogButton("StudentEdit/0", ButtonActionTypes.Add);
});
builder.SetListMethod(GetStudentList);
}
public class StudentDetails : Student
{
public int EnrollmentCount { get; set; }
}
public List GetStudentList(params object[] parameters)
{
using (var db = GetDbContext())
{
var query =
from s in db.Student
select new StudentDetails
{
ID = s.ID,
FirstMidName = s.FirstMidName,
LastName = s.LastName,
EnrollmentDate = s.EnrollmentDate,
EnrollmentCount = (db.Enrollment.Where
(e => e.StudentID == s.ID).Count())
};
var result = query.ToList();
return result;
}
}
}
}
请注意Format("dd-MMM-yyyy")指定如何显示EnrollmentDate属性的格式。
另外,有时您需要显示比实体更多的列,然后我们需要创建一个业务对象——一个将包含所有必需属性的类。我创建了一个继承自Student的所有属性的StudentDetails类,并且还添加了该EnrollmentCount属性。
GetStudentList返回所有学生数据,并计算每个student的入学人数。
razor页面如下所示:
@page "/StudentList"
Students
@code {
}
如果运行该应用程序并单击“学生列表”菜单项,则将看到:
为了使添加、编辑、删除工作,我们需要添加StudentEditForm。
StudentEditForm定义非常相似于CourseEditForm,但是我添加了业务规则,以便在输入或编辑新student规则时进行其他验证。
using DemoSqlForms.Database.Model;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App.Forms
{
public class StudentEditForm : DynamicEditFormBase
{
protected override void Define(DynamicFormBuilder builder)
{
builder.Entity(e =>
{
e.Property(p => p.ID).IsReadOnly();
e.Property(p => p.FirstMidName).IsRequired();
e.Property(p => p.LastName).IsRequired();
e.Property(p => p.EnrollmentDate).Rule
(DefaultDate, FormRuleTriggers.Create).Rule(CheckDate);
e.DialogButton(ButtonActionTypes.Cancel).DialogButton
(ButtonActionTypes.Validate).DialogButton(ButtonActionTypes.Submit);
e.DialogButtonNavigation("StudentList", ButtonActionTypes.Cancel,
ButtonActionTypes.Delete, ButtonActionTypes.Submit);
});
}
public FormRuleResult DefaultDate(Student model)
{
model.EnrollmentDate = new DateTime(DateTime.Now.Year, 9, 1);
return null;
}
public FormRuleResult CheckDate(Student model)
{
if (model.EnrollmentDate < new DateTime(2015, 1, 1))
{
return new FormRuleResult("EnrollmentDate is incorrect");
}
return null;
}
}
}
规则Rule(DefaultDate, FormRuleTriggers.Create)表明,当创建新的学生记录时,将执行该DefaultDate方法,该方法设置EnrollmentDate为当年的01-Sep。
EnrollmentDate属性更改或提交表单后,将执行规则CheckDate。当输入的值早于2015年1月1日时,此规则将触发验证错误。
StudentEdit.razor 页面像往常一样非常简单:
@page "/StudentEdit/{Id:int}"
@page "/StudentEdit"
Student Edit
@code {
[Parameter]
public int Id { get; set; }
}
如果现在运行该应用程序,请选择“学生列表”页面,然后单击“添加”按钮。您可以使用默认规则和验证规则。
对于删除功能,我们需要添加StudentDelete.razor页面。
@page "/StudentDelete/{Id:int}"
Delete Student
@code {
[Parameter]
public int Id { get; set; }
}
运行应用程序时,删除页面将如下所示:
现在我们需要创建“注册”页面,我想演示如何简化列表表单的创建。
Platz.ObjectBuilder 可用于可视化地构建具有联接、子查询、条件的复杂LINQ查询,并为查询和查询返回的业务对象生成C#代码。
为了展示如何使用Platz.ObjectBuilder,我们需要创建另一个目标框架为.NET 5.0的Blazor Server应用程序,并将其命名为“DemoSqlForms.ObjectBuilder.App”。
然后,我们需要安装Platz.ObjectBuilder NuGet软件包,并按照readm.txt文件中的说明进行操作。
要使用SchoolContext,我们需要向项目添加一个项目引用DemoSqlForms.Database,并将连接字符串添加到“appsettings.json”文件中。
现在让我们修改Index.razor页面。
@page "/"
@using Platz.ObjectBuilder
右键单击“DemoSqlForms.ObjectBuilder.App”项目,然后选择“调试”,然后选择“启动新实例”。
您将看到该应用程序,它使我们可以直观地构建查询。
选择注册实体,然后选择Course实体。您将看到两个对象已添加到“From”面板。现在,在“选择”面板中,为“e.StudentID”列输入“@p1”到Filter。您应该看到一个查询窗口,如下所示:
现在,在“设置”面板上单击“…”,并在“查询返回类型名称”控件中输入 “EnrollmentDetails” ,然后单击“保存”,然后关闭应用程序。
我们创建了查询定义,该定义保存为json文件,位于文件夹“DemoSqlForms.ObjectBuilder.App\StoreData”中。我们可以使用t4模板从此json定义生成代码。
让我们回到“DemoSqlForms.App”项目。如果打开项目文件夹“Platz.Config.Link”,则将看到“CopyMe.PlatzDataService.tt.txt”文件。双击此文件,选择所有代码(
现在,在“Forms”文件夹中,创建一个子文件夹“DataServices”。
在“DataServices”文件夹中,创建一个名为“SchoolDataService.tt”的文件,然后粘贴剪贴板中的内容(
您需要更改第12行,以指向“DemoSqlForms.ObjectBuilder.App”项目中保存了查询的“StoreData”文件夹:
<# var JsonStorePath = @"DemoSqlForms.ObjectBuilder.App\StoreData"; #>
现在,当您保存文件时,Visual Studio将为您生成代码并将其放置在“SchoolDataService.cs”中。
// ******************************************************************************************
// This code is auto generated by Platz.ObjectBuilder template,
// any changes made to this code will be lost
// ******************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Platz.SqlForms;
using DemoSqlForms.Database.Model;
namespace Default
{
#region Interface
public partial interface IMyDataService
{
List GetEnrollmentDetailsList(params object[] parameters);
}
#endregion
#region Data Service
public partial class MyDataService : DataServiceBase, IMyDataService
{
public List GetEnrollmentDetailsList(params object[] parameters)
{
var p1 = (Int32)parameters[0];
using (var db = GetDbContext())
{
var query =
from c in db.Course
join e in db.Enrollment on c.CourseID equals e.CourseID
where e.StudentID == p1
select new EnrollmentDetails
{
EnrollmentID = e.EnrollmentID,
CourseID = e.CourseID,
Grade = e.Grade,
StudentID = e.StudentID,
Credits = c.Credits,
Title = c.Title,
};
var result = query.ToList();
return result;
}
}
}
#endregion
#region Entities
public partial class EnrollmentDetails
{
public Int32 EnrollmentID { get; set; }
public Int32 CourseID { get; set; }
public Grade? Grade { get; set; }
public Int32 StudentID { get; set; }
public Int32 Credits { get; set; }
public String Title { get; set; }
}
#endregion
}
生成的文件包含EnrollmentDetails业务对象类和MyDataService:: GetEnrollmentDetailsList方法,该类和方法返回Enrollment和Course实体的联接数据。它还接受参数p1,并且该StudentID字段将过滤数据。
现在我们添加EnrollmentListForm代码:
using Default;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App.Forms
{
public class EnrollmentListForm : MyDataService
{
protected override void Define(DataServiceFormBuilder builder)
{
builder.Entity(e =>
{
e.ExcludeAll();
e.Property(p => p.EnrollmentID).IsPrimaryKey();
e.Property(p => p.StudentID).IsFilter().IsReadOnly();
e.Property(p => p.CourseID);
e.Property(p => p.Grade);
e.Property(p => p.Title);
e.Property(p => p.Credits);
// Parameter {0} is always PrimaryKey, parameters {1} and above - Filter Keys
// {0} = EnrollmentID {1} = StudentID
e.ContextButton("Edit", "EnrollmentEdit/{0}/{1}").ContextButton
("Delete", "EnrollmentDelete/{0}/{1}");
e.DialogButton("StudentList", ButtonActionTypes.Custom, "Back");
e.DialogButton("EnrollmentEdit/0/{1}", ButtonActionTypes.Add);
});
builder.SetListMethod(GetEnrollmentDetailsList);
}
}
}
我们从生成的MyDataService继承类EnrollmentListForm,并使用SetListMethod指定生成的GetEnrollmentDetailsList。
我们照常定义了属性,但是导航链接现在具有两个占位符:“EnrollmentEdit/{0}/{1}”和“EnrollmentDelete/{0}/{1}”。
原因是EnrollmentListForm是StudentListForm的附属形式。当我们选择学生,点击“Enrollments”上下文菜单按钮,我们需要向EnrollmentListForm提供StudentID主键,这个StudentID将作为“{1}”占位符被传播到EnrollmentEditForm,但“{0}”是保留给EnrollmentEditForm主键- EnrollmentID的。
EnrollmentList.razor 页面将如下所示:
@page "/EnrollmentList/{StudentId:int}"
Student Enrollments
@code {
[Parameter]
public int StudentId { get; set; }
}
页面路由现在接受StudentId参数,我们使用ServiceParameters提供StudentId给FormDataServiceListComponent。该引擎将使用ServiceParameters生成导航链接,以填充占位符“{1}”及以上的占位符。
我们还添加了FormDynamicEditComponent以显示所有字段都设置为只读的字段的StudentHeaderForm。
using DemoSqlForms.Database.Model;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App.Forms
{
public class StudentHeaderForm : DynamicEditFormBase
{
protected override void Define(DynamicFormBuilder builder)
{
builder.Entity(e =>
{
e.ExcludeAll();
e.Property(p => p.ID).IsReadOnly();
e.Property(p => p.FirstMidName).IsReadOnly();
e.Property(p => p.LastName).IsReadOnly();
});
}
}
}
如果我们现在运行该应用程序,然后在“学生列表”中选择一个学生,然后单击“注册”上下文菜单按钮,我们将看到:
您可以在下面的header和enrollments表中看到学生只读详细信息。
如果单击“返回”按钮,我们将返回到“学生列表”页面。
最后一步是创建EnrollmentEditForm定义,就像我们之前所做的一样简单。
using DemoSqlForms.Database.Model;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App.Forms
{
public class EnrollmentEditForm : DynamicEditFormBase
{
protected override void Define(DynamicFormBuilder builder)
{
builder.Entity(e =>
{
e.Property(p => p.EnrollmentID).IsPrimaryKey().IsReadOnly();
e.Property(p => p.StudentID).IsFilter().IsHidden();
e.Property(p => p.CourseID).IsRequired().Dropdown().Set
(c => c.CourseID, c => c.Title);
e.Property(p => p.Grade).IsRequired().Rule
(DefaultGrade, FormRuleTriggers.Create).Dropdown().Set(g => g, g => g);
e.DialogButton(ButtonActionTypes.Cancel).DialogButton
(ButtonActionTypes.Submit);
// {0} always reserved for Primary Key (EnrollmentID in this case)
// but EnrollmentList accepts StudentId as parameter
e.DialogButtonNavigation("EnrollmentList/{1}",
ButtonActionTypes.Cancel, ButtonActionTypes.Delete, ButtonActionTypes.Submit);
});
}
public FormRuleResult DefaultGrade(Enrollment model)
{
model.Grade = Grade.A;
return null;
}
}
}
在这里,我们使用了Dropdown定义。对于CourseID属性,我们使用Course实体,并指定[value]将是Course.CourseID和[name]将是Course.Title。对于“Grade”属性,我们指定“Grade” enum,并且下拉列表中的[value]和[name]将具有“Grade”枚举项(A,B,C等)
然后,我们需要为Edit添加razor页面。
@page "/EnrollmentEdit/{EnrollmentId:int}/{StudentId:int}"
Student Enrollment Edit
@code {
[Parameter]
public int EnrollmentId { get; set; }
[Parameter]
public int StudentId { get; set; }
}
而对于Delete。
@page "/EnrollmentDelete/{EnrollmentId:int}/{StudentId:int}"
Student Enrollment Delete
@code {
[Parameter]
public int EnrollmentId { get; set; }
[Parameter]
public int StudentId { get; set; }
}
在这两个页面中,我们都显示StudentHeaderForm为header,并在ServiceParameters中提供StudentId。
现在,该应用程序可以进行测试了,单击“学生注册”的“编辑”操作,您将看到:
如果单击“删除”操作,将显示此页面:
SqlForms引擎将使用我们提供的表单定义来执行Insert、Update和Delete的所有数据库操作。
在本文中,我们演示了一种使用C#中的类型安全定义来构建Blazor UI应用程序的方法。对于使用Platz.SqlForms进行原型或低预算应用程序的开发人员而言,该技术可以节省大量时间。
这种方法有几个优点:
但是,有一些缺点:
我们还考虑了可以节省大量时间来定义业务对象并将其映射到LINQ查询结果的Platz.ObjectBuilder工具。尽管对象生成器目前不支持复杂的查询,但我们演示了一个概念,该概念涉及t4模板如何使用可视化工具输出来生成不需要维护的代码:任何时候您需要更改内容时,只需进行修改即可查询并重新生成代码。
该项目Platz.SqlForms是开源的,由Pro Coders团队开发。
您可以在Github上找到所有详细信息
要提交错误或功能请求,请使用以下链接:
Issues · ProCodersPtyLtd/MasterDetailsDataEntry (github.com)
我的下一篇文章将介绍SqlForms内联编辑。
您可以在此处详细了解如何设置实体框架模型优先数据库:
教程:在ASP.NET MVC Web应用程序中开始使用EF Core 微软文档。
我们在“DemoSqlForms.Database”项目中创建一个文件夹“Model ”,并添加SchoolContext.cs文件。
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Configuration;
using Microsoft.Extensions.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DemoSqlForms.Database.Model
{
public class SchoolContext : DbContext
{
public SchoolContext()
{
}
public SchoolContext(DbContextOptions options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
IConfigurationRoot configuration =
new ConfigurationBuilder().AddJsonFile
("appsettings.json", optional: false).Build();
optionsBuilder.UseSqlServer
(configuration.GetConnectionString("DefaultConnection"));
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(entity =>
{
entity.HasOne(d => d.Course)
.WithMany(p => p.Enrollments)
.HasForeignKey(d => d.CourseID)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Enrollment_Course");
entity.HasOne(d => d.Student)
.WithMany(p => p.Enrollments)
.HasForeignKey(d => d.StudentID)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Enrollment_Student");
});
}
public DbSet Course { get; set; }
public DbSet Enrollment { get; set; }
public DbSet Student { get; set; }
}
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection Enrollments { get; set; }
}
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection Enrollments { get; set; }
}
}
我将简要提及该文件包含我们的演示数据库的实体框架DbContext和实体。SchoolContext从“appsettings.json”中读取连接字符串,我们将其添加到“DemoSqlForms.App”项目中。
实体看起来像:
要使用测试数据初始化数据库,我们添加DbInitializer.cs文件。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DemoSqlForms.Database.Model
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
context.Database.EnsureCreated();
// Look for any students.
if (context.Student.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",
EnrollmentDate=DateTime.Parse("2005-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",
EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",
EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",
EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Yan",LastName="Li",
EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",
EnrollmentDate=DateTime.Parse("2001-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",
EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",
EnrollmentDate=DateTime.Parse("2005-09-01")}
};
foreach (Student s in students)
{
context.Student.Add(s);
}
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
foreach (Course c in courses)
{
context.Course.Add(c);
}
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
foreach (Enrollment e in enrollments)
{
context.Enrollment.Add(e);
}
context.SaveChanges();
}
}
}
现在我们需要对“DemoSqlForms.App”项目进行更改。
将连接字符串添加到“appsettings.json”,文件将如下所示:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;
Database=DemoSqlForms1;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
连接字符串指定SQL Server LocalDB。LocalDB是SQL Server Express数据库引擎的轻量级版本,旨在用于应用程序开发,而不用于生产。LocalDB按需启动并以用户模式运行,因此没有复杂的配置。默认情况下,在C:/Users/
在文件Program.cs中,我们删除以下行:
CreateHostBuilder(args).Build().Run();
并添加创建数据库逻辑,代码将如下所示:
using DemoSqlForms.Database.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App
{
public class Program
{
public static void Main(string[] args)
{
//CreateHostBuilder(args).Build().Run();
var host = CreateHostBuilder(args).Build();
CreateDbIfNotExists(host);
host.Run();
}
private static void CreateDbIfNotExists(IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService();
DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService>();
logger.LogError(ex, "An error occurred creating the DB.");
}
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
});
}
}
方法CreateDbIfNotExists只需执行DbInitializer ,即可在首次运行时创建数据库并填充测试数据。
在ConfigureServices方法中,我们需要添加DbContext初始化逻辑
services.AddDbContext
(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
我们已经添加了Platz.SqlForms初始化逻辑:
services.AddPlatzSqlForms();
该代码将如下所示:
using DemoSqlForms.Database.Model;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Platz.SqlForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DemoSqlForms.App
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime.
// Use this method to add services to the container.
// For more information on how to configure your application,
// visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddDbContext(options => options.UseSqlServer
(Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddPlatzSqlForms();
}
// This method gets called by the runtime. Use this method to configure
// the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/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.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
https://www.codeproject.com/Articles/5291832/Microsoft-Blazor-Rapid-Development-with-SQL-Forms