项目开发中的一些注意事项以及技巧总结

 

1、jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View()、PartialView()等,只能返回json以及content等,但是一般我们在开发的时候也是使用json返回的,此时如果需要渲染界面或者是加载局部视图,我们可以在ajax的success的事件中使用$.html()来渲染后台给前端传的View()数据。一开始我遇到这个问题的时候还很纳闷,为什么ajax给后端穿了POST之后,不能加载局部页面,后来一想,是不是因为ajax是异步请求的原因,于是我把ajax的同步开关打开了,同时将后台传输的视图数据直接使用$html()就渲染到了前端页面。具体请参考:https://blog.csdn.net/m0_37302219/article/details/78272081

2 、后端MVC架构,一般前端都是使用Razor视图,使用@来加载后台传过来的数据比如ViewData,以及ViewBag等。此时,我建议在后端的C#中,我们给ViewBag赋值了之后,在前端的cshtml页面中这么做:
@{
List<T> xxx = ViewBag.xxx;
T xxx = ViewBag.xxx;
}
然后直接使用显式类型的变量来做处理。

3、EF Core中,尤其要注意多对多的关联属性的使用。如果使用不当就会加载不出来数据,直接会出一个null值,在这里请参考
https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-1-the-basics/
认真的跟着这个博客做一遍就可以理解了。

4、EF Core中,当我们使用code First建模方式生成数据库时,如果生成数据库有问题了(当然一般是没有问题的,但是我就遇到了,可能也是我操作不当)我强烈建议,不要删除这个生成有问题的数据库,千万不要觉得删除了之后,可以再生成一遍,会出问题的。具体请看
http://www.cjjjs.com/paper/lkkj/2018711212518513.html

5、EF Core下一对多的关联中,需要注意(我不怎么用过EF,也不知道在EF中是不是很正常,在这里仅仅说EF Core,可能两者在我要说的这点上是一致的)。我们以官方为例:https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db
一个博客(Blog)对应多个文章(Post),一篇文章对应一个博客(Blog)。此时,当我们使用上下文来获取数据库数据时,代码如下:
List<Blog> Blogs = _context.Blogs.ToList(); 
List<Post> Posts = _context.Posts.ToList(); // 如果不写这句代码,不从数据库里面加载处理关联属性Post的数据,那么Blogs里面的Post关联属性也将会是null值,这点是要特别注意的,只有写了这句代码,Blogs类型的关联属性Post才能有数据。

6、项目中一定要设计好数据库,一般来讲如果有级联关系的,就设置为两个表(这里的级联关系我是这么定义的:假设一个类别下有多条数据,这就是一个级联关系,就像是一个省份下面有多个城市)。在实体方面,我们可以认为是一对多关系,如果是一对多关系,我们就建两个表。设计好了数据表,对我们的开发有着莫大的好处,有时候多加一个字段和少一个字段完全不一样。

7、建议在列表数据中使用ForEach来遍历,也就是一般的List<T>变量,使用List<T>.ForEach来遍历数据处理逻辑

基于Repository模式设计项目架构—你可以参考的项目架构设计

 

关于Repository模式,直接百度查就可以了,其来源是《企业应用架构模式》。
我们新建一个Infrastructure文件夹,这里就是基础设施部分,EF Core的上下文类以及Repository层都放在这里面。
新建一个IReposotory的接口,其内容就是封装了基本的CRUD:

  1. public interface IRepository<TEntity> where TEntity : class
  2. {
  3. ///获取当前实体的查询数据集
  4. IQueryable<TEntity> Entities{get;}
  5.  
  6. ///获取当前实体的数据集
  7. DbSet<TEntity> DbEntities{get;}
  8.  
  9. /// <summary>
  10. /// Gets all objects from database
  11. /// </summary>
  12. /// <returns></returns>
  13. IQueryable<TEntity> All();
  14.  
  15. /// <summary>
  16. /// Gets objects from database by filter.
  17. /// </summary>
  18. /// <param name="predicate">Specified a filter</param>
  19. /// <returns></returns>
  20. IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate);
  21.  
  22. /// <summary>
  23. /// Gets objects from database with filting and paging.
  24. /// </summary>
  25. /// <param name="filter">Specified a filter</param>
  26. /// <param name="total">Returns the total records count of the filter.</param>
  27. /// <param name="index">Specified the page index.</param>
  28. /// <param name="size">Specified the page size</param>
  29. /// <returns></returns>
  30. IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 0, int size = 50);
  31.  
  32. /// <summary>
  33. /// Gets the object(s) is exists in database by specified filter.
  34. /// </summary>
  35. /// <param name="predicate">Specified the filter expression</param>
  36. /// <returns></returns>
  37. bool Contains(Expression<Func<TEntity, bool>> predicate);
  38.  
  39. /// <summary>
  40. /// Find object by keys.
  41. /// </summary>
  42. /// <param name="keys">Specified the search keys.</param>
  43. /// <returns></returns>
  44. TEntity Find(params object[] keys);
  45.  
  46. /// <summary>
  47. /// Find object by specified expression.
  48. /// </summary>
  49. /// <param name="predicate"></param>
  50. /// <returns></returns>
  51. TEntity Find(Expression<Func<TEntity, bool>> predicate);
  52.  
  53. /// <summary>
  54. /// Create a new object to database.
  55. /// </summary>
  56. /// <param name="t">Specified a new object to create.</param>
  57. /// <returns></returns>
  58. int Create(TEntity t);
  59.  
  60. /// <summary>
  61. /// Delete the object from database.
  62. /// </summary>
  63. /// <param name="t">Specified a existing object to delete.</param>
  64. void Delete(TEntity t);
  65.  
  66. /// <summary>
  67. /// Delete objects from database by specified filter expression.
  68. /// </summary>
  69. /// <param name="predicate"></param>
  70. /// <returns></returns>
  71. int Delete(Expression<Func<TEntity, bool>> predicate);
  72.  
  73. /// <summary>
  74. /// Update object changes and save to database.
  75. /// </summary>
  76. /// <param name="t">Specified the object to save.</param>
  77. /// <returns></returns>
  78. int Update(TEntity t);
  79.  
  80. /// <summary>
  81. /// Select Single Item by specified expression.
  82. /// </summary>
  83. /// <typeparam name="T"></typeparam>
  84. /// <param name="expression"></param>
  85. /// <returns></returns>
  86. TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression);
  87. }

创建一个基类,用来实现IRepository接口,同时作其余的Repository的基类

  1. public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
  2. {
  3. protected readonly DbContext Context;
  4.  
  5. public BaseRepository(DbContext context)
  6. {
  7. Context = context;
  8. }
  9.  
  10. /// 获取当前实体的查询数据集
  11. public IQueryable<TEntity> Entities
  12. {
  13. get { return Context.Set<TEntity>().AsQueryable(); }
  14. }
  15.  
  16. /// 获取当前实体
  17. public IQueryable<TEntity> Entities
  18. {
  19. get { return Context.Set<TEntity>(); }
  20. }
  21.  
  22. public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> expression)
  23. {
  24. return All().FirstOrDefault(expression);
  25. }
  26.  
  27. public IQueryable<TEntity> All()
  28. {
  29. return Context.Set<TEntity>().AsQueryable();
  30. }
  31.  
  32. public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate)
  33. {
  34. return Context.Set<TEntity>().Where<TEntity>(predicate).AsQueryable<TEntity>();
  35. }
  36.  
  37. public virtual IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> filter, out int total, int index = 0,
  38. int size = 50)
  39. {
  40. var skipCount = index * size;
  41. var resetSet = filter != null
  42. ? Context.Set<TEntity>().Where<TEntity>(filter).AsQueryable()
  43. : Context.Set<TEntity>().AsQueryable();
  44. resetSet = skipCount == 0 ? resetSet.Take(size) : resetSet.Skip(skipCount).Take(size);
  45. total = resetSet.Count();
  46. return resetSet.AsQueryable();
  47. }
  48.  
  49. public virtual int Create(TEntity TObject)
  50. {
  51. Entities.Add(TObject);
  52. Context.SaveChanges();
  53. }
  54.  
  55. public virtual int Delete(TEntity TObject)
  56. {
  57. Entities.Remove(TObject);.
  58. Context.SaveChanges();
  59. }
  60.  
  61. public virtual void Update(TEntity TObject)
  62. {
  63. try
  64. {
  65. var entry = Context.Entry(TObject);
  66. Context.Set<TEntity>().Attach(TObject);
  67. entry.State = EntityState.Modified;
  68. }
  69. catch (OptimisticConcurrencyException ex)
  70. {
  71. throw ex;
  72. }
  73. }
  74.  
  75. public virtual int Delete(Expression<Func<TEntity, bool>> predicate)
  76. {
  77. var objects = Filter(predicate);
  78. foreach (var obj in objects)
  79. Context.Set<TEntity>().Remove(obj);
  80. return Context.SaveChanges();
  81. }
  82.  
  83. public bool Contains(Expression<Func<TEntity, bool>> predicate)
  84. {
  85. return Context.Set<TEntity>().Any(predicate);
  86. }
  87.  
  88. public virtual TEntity Find(params object[] keys)
  89. {
  90. return Context.Set<TEntity>().Find(keys);
  91. }
  92.  
  93. public virtual TEntity Find(Expression<Func<TEntity, bool>> predicate)
  94. {
  95. return Context.Set<TEntity>().FirstOrDefault<TEntity>(predicate);
  96. }
  97. }

新建一个实体类的接口:

  1. public interface IStudentRepository : IRepository<Student>
  2. {
  3. int AddStudent(Student student);
  4. }

然后我们创建一个实体类的Repository:

  1. public class StudentRepository : BaseRepository<Student>, IStudentRepository
  2. {
  3. private readonly SchoolContext _context;
  4.  
  5. public StudentRepository(SchoolContext context)
  6. : base(context)
  7. {
  8. _context = context;
  9. }
  10.  
  11. int AddStudent(Student student)
  12. {
  13. _context.Create(student);
  14. }
  15. }

在这里就已经做好了我们要做的了。接下来的就是注入依赖、在控制器里面的使用了。

我们完全可以自己来定制自己的Repository模式下的项目。其实整个的架构没有什么,我们只是将所有的CRUD操作封装到了IRepository接口里面,然后在BaseRepository中实现了一遍,而且如果你细心的话,你会发现IRepository里面的CRUD操作都是基于已有的扩展方法里面的,就是linq扩展方法的Add等源码,同时我们在BaseRepository类中,提供了DbSet<TEntity>属性以及查询数据集IQueryable<DbSet<TEntity>>,这也是有必要的,可以省却我们很多不必要的代码,因为我们所有的CRUD都是基于这两个的。然后我们基于BaseRepository来实现实体类的Repository,同时继承按需增加的IEntityRepository接口。但是在这里要注意,我们将DbContext的子类都放在了Infrastructure文件夹里面,是因为,一般我们继承自DbContext的子类都是操作数据库的中间类,属于基础设施一块,所以将其放在Infrastructure文件夹比较合适。

参考资料:
MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext

分享基于Entity Framework的Repository模式设计(附源码)

《ASP.NET MVC框架揭秘》源码中的示例项目源码 S1402

EF Core中的多对多映射如何实现?

 

EF 6.X中的多对多映射是直接使用HasMany-HasMany来做的。但是到了EF Core中,不再直接支持这种方式了,可以是可以使用,但是不推荐,具体使用可以参考《你必须掌握的EntityFramework 6.X与Core 2.0》一文。在这里我就详细的说下如何在EF core下实现。
首先就是实体类的建立:

  1. public class Post
  2. {
  3. public int PostId { get; set; }
  4. public string Title { get; set; }
  5.  
  6. public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
  7. }
  8.  
  9. public class Tag
  10. {
  11. public int TagId { get; set; }
  12. public string Text { get; set; }
  13.  
  14. public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
  15. }
  16.  
  17. public class PostTag
  18. {
  19. public int PostId { get; set; }
  20. public Post Post { get; set; }
  21.  
  22. public int TagId { get; set; }
  23. public Tag Tag { get; set; }
  24. }

接下来就是映射了。派生自DbContext的上下文类:

  1. public class MyContext : DbContext
  2. {
  3. public DbSet<Post> Posts { get; set; }
  4. public DbSet<Tag> Tags { get; set; }
  5.  
  6. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  7. => optionsBuilder.UseSqlServer(
  8. @"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
  9.  
  10. protected override void OnModelCreating(ModelBuilder modelBuilder)
  11. {
  12. modelBuilder.Entity<PostTag>().ToTable("PostTags");
  13. modelBuilder.Entity<PostTag>()
  14. .HasKey(t => new { t.PostId, t.TagId });
  15. }
  16. }

这样就完成了我们的多对多映射了。我们只是通过多建立了一个表,将两个实体类的Id作为联合主键。

在Identity框架中,如果你细心点,你会发现有个userroles表,这个表是就是用来做Users表和Roles表的映射的。那么接下来我们只要新建一个实体类,随后在上下文类中映射到表:

  1. modelBuilder.Entity<UserRoles>.ToTable("userroles");

这样就可以了。然后我们就可以很方便的给用户添加角色了。

参考链接:https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-1-the-basics/

asp.net core下的如何给网站做安全设置

 

首先,我们来看下stack overflow网站的请求头文件:

可以看到一些我们熟悉或是陌生的HTTP头部文件字段。
在这里我们在对HTTP输入流的头部文件中,做一些基本的防护。首先要明确,既然我们是对HTTP头部做处理,那么就需要在Startup.cs类的 
Configuration方法中做处理,因为这里就是处理HTTP输入流的。

首先做一些基本的处理,比如中间件和基本的类:

1
2
3
4
5
6
7
8
public class SecurityHeadersPolicy 
{
    public IDictionary<stringstring> SetHeaders { get; }
         new Dictionary<stringstring>();
 
    public ISet<string> RemoveHeaders { get; }
        new HashSet<string>();
}

这里的头部信息是我们定义好的,用来增加或是删除头部信息,然后就是我们的中间件:

  1. public class SecurityHeadersMiddleware
  2. {
  3. private readonly RequestDelegate _next;
  4. private readonly SecurityHeadersPolicy _policy;
  5.  
  6. public SecurityHeadersMiddleware(RequestDelegate next, SecurityHeadersPolicy policy)
  7. {
  8. _next = next;
  9. _policy = policy;
  10. }
  11.  
  12. public async Task Invoke(HttpContext context)
  13. {
  14. IHeaderDictionary headers = context.Response.Headers;
  15.  
  16. foreach (var headerValuePair in _policy.SetHeaders)
  17. {
  18. headers[headerValuePair.Key] = headerValuePair.Value;
  19. }
  20.  
  21. foreach (var header in _policy.RemoveHeaders)
  22. {
  23. headers.Remove(header);
  24. }
  25.  
  26. await _next(context);
  27. }
  28. }

基于IApplicationBuilder接口做一个中间件的扩展方法:

  1. public static class MiddlewareExtensions
  2. {
  3. public static IApplicationBuilder UseSecurityHeadersMiddleware(this IApplicationBuilder app, SecurityHeadersBuilder builder)
  4. {
  5. SecurityHeaderPolicy policy = builder.Build();
  6. return app.UseMiddleware<SecurityHeadersMiddleware>(policy);
  7. }
  8. }

封装好相关的安全类:

  1. public class SecurityHeadersBuilder
  2. {
  3. private readonly SecurityHeadersPolicy _policy = new SecurityHeadersPolicy();
  4.  
  5. public SecurityHeadersBuilder AddDefaultSecurePolicy()
  6. {
  7. AddFrameOptionsDeny();
  8. AddXssProtectionBlock();
  9. AddContentTypeOptionsNoSniff();
  10. AddStrictTransportSecurityMaxAge();
  11. RemoveServerHeader();
  12.  
  13. return this;
  14. }
  15.  
  16. public SecurityHeadersBuilder AddFrameOptionsDeny()
  17. {
  18. _policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.Deny;
  19. return this;
  20. }
  21.  
  22. public SecurityHeadersBuilder AddFrameOptionsSameOrigin()
  23. {
  24. _policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.SameOrigin;
  25. return this;
  26. }
  27.  
  28. public SecurityHeadersBuilder AddFrameOptionsSameOrigin(string uri)
  29. {
  30. _policy.SetHeaders[FrameOptionsConstants.Header] = string.Format(FrameOptionsConstants.AllowFromUri, uri);
  31. return this;
  32. }
  33.  
  34. public SecurityHeadersBuilder RemoveServerHeader()
  35. {
  36. _policy.RemoveHeaders.Add(ServerConstants.Header);
  37. return this;
  38. }
  39.  
  40. public SecurityHeadersBuilder AddCustomHeader(string header, string value)
  41. {
  42. _policy.SetHeaders[header] = value;
  43. return this;
  44. }
  45.  
  46. public SecurityHeadersBuilder RemoveHeader(string header)
  47. {
  48. _policy.RemoveHeaders.Add(header);
  49. return this;
  50. }
  51.  
  52. public SecurityHeadersPolicy Build()
  53. {
  54. return _policy;
  55. }
  56. }

最后注入到HTTP的输入流中:

  1. app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder()
  2. .AddDefaultSecurePolicy()
  3. );

然后我们浏览一下网页,就可以在HTTP的头部信息中看到:

  1. HTTP/1.1 200 OK
  2. Content-Type: text/html; charset=utf-8
  3. X-Frame-Options: DENY
  4. X-XSS-Protection: 1; mode=block
  5. X-Content-Type-Options: nosniff
  6. Strict-Transport-Security: max-age=31536000
  7. X-Powered-By: ASP.NET

还有一个就是CSRF的防护,如果之前你用过ASP.NET MVC,在最基本的MVC模板中,可能你会留意到已有的cshtml页面中的form表单有这么一句:

  1. @Html.AntiForgeryToken()

这就是微软在MVC框架中为我们提供的防护CSRF的方法。我们在表单中直接使用上面那句代码就可以了,然后在表单提交的Action方法中:

  1. [ValidateAntiForgeryToken]
  2. [HttpPost]
  3. public IActionResult AntiForm(string message)
  4. {
  5. return Content(message);
  6. }

使用[ValidateAntiForgeryToken]属性,来验证CSRF。

参考链接:
How to add security headers in ASP.NET Core using custom middleware(如何使用自定义中间件在ASP.NET Core中添加安全标头)

初探CSRF在ASP.NET Core中的处理方式

代码地址:
https://github.com/RyanOvO/aspnetcore-fileup-demo

获取服务端https证书

最近开发一个需求,涉及获取服务端https证书。一般进行https调用我们都不太关心底层细节,直接使用WebClient或者HttpWebRequest来发送请求,这两种方法都无法获取证书信息,需要用到ServicePoint,这个类用于提供HTTP连接的管理。

写个Demo,拿新浪首页试一下:

  1. using System;
  2. using System.Net;
  3. using System.Security.Cryptography.X509Certificates;
  4.  
  5. namespace GetServerCertificateDemo
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. //用WebClient访问新浪首页
  12. var http = new WebClient();
  13. var uri = new Uri("https://www.sina.com.cn");
  14. http.DownloadString(uri);
  15.  
  16. //通过Uri获取ServicePoint
  17. var servicePoint = ServicePointManager.FindServicePoint(uri);
  18.  
  19. //取服务端证书,X509Certificate格式,转一下
  20. var serverCert = new X509Certificate2(servicePoint.Certificate);
  21. Console.WriteLine("颁发给:{0}", serverCert.Subject);
  22. Console.WriteLine("颁发者:{0}", serverCert.Issuer);
  23. Console.WriteLine("序列号:{0}", serverCert.SerialNumber);
  24. Console.WriteLine("指 纹:{0}", serverCert.Thumbprint);
  25. Console.WriteLine("起 始:{0}", serverCert.NotBefore);
  26. Console.WriteLine("过 期:{0}", serverCert.NotAfter);
  27. }
  28. }
  29. }

运行看效果:

上半部分是程序运行结果,下面是用Firefox查看的服务端证书信息,各项信息都能对应上。如果程序中涉及多个不同服务器的访问也没关系,关键在于根据Uri获取ServicePoint,然后取到的证书就是此服务器的了。

Js异常捕获

 
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <script>
  9. function demo(){
  10. try{
  11. alert(str);
  12. }catch(e){
  13. alert(e);
  14. }
  15. }
  16. demo();
  17. </script>
  18.  
  19. <form>
  20. <input id="txt" type="text"/>
  21. <input id="btn" type="button" onclick="demo1()" value="按钮" />
  22. </form>
  23. <script>
  24. function demo1(){
  25. try{
  26. var e=document.getElementById("txt").value;
  27. if(e==""){
  28. throw "请输入";
  29. }
  30. }catch(e){
  31. alert(e);
  32. }
  33.  
  34. }
  35. </script>
  36. </body>
  37. </html>

js事件

onLoad    网页加载事件

onUnload    关闭网页事件

反射:获取Class对象的三种方式

数据库_mysql多表操作

数据库_mysql多表操作

 

多表操作

       实际开发中,一个项目通常需要很多张表才能完成。例如:一个商城项目就需要分类表(category)、商品表(products)、订单表(orders)等多张表。且这些表的数据之间存在一定的关系。

1.1    表与表之间的关系

l  一对多关系:

n  常见实例:客户和订单,分类和商品,部门和员工.

n  一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键.

l  多对多关系:

n  常见实例:学生和课程、用户和角色

n  多对多关系建表原则:需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键.

l  一对一关系:(了解)

n  在实际的开发中应用不多.因为一对一可以创建成一张表.

n  两种建表原则:

u  外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一unique。

u  外键是主键:主表的主键和从表的主键,形成主外键关系。

1.2    外键约束

现在我们有两张表“分类表”和“商品表”,为了表明商品属于哪个分类,通常情况下,我们将在商品表上添加一列,用于存放分类cid的信息,此列称为:外键

此时“分类表category”称为:主表,“cid”我们称为主键。“商品表products”称为:从表,category_id称为外键。我们通过主表的主键和从表的外键来描述主外键关系,呈现就是一对多关系。

外键特点:

u  从表外键的值是对主表主键的引用。

u  从表外键类型,必须与主表主键类型一致。

l  声明外键约束     

语法:alter table 从表 add [constraint] [外键名称] foreign key (从表外键字段名) references 主表 (主表的主键);

[外键名称]用于删除外键约束的,一般建议“_fk”结尾

altertable 从表 drop foreignkey 外键名称

l  使用外键目的:

n  保证数据完整性

1.3    一对多操作

1.3.1    分析

  • category分类表,为一方,也就是主表,必须提供主键cid

  • products商品表,为多方,也就是从表,必须提供外键category_id

  • 1.3.2    实现:分类和商品

    ###创建分类表

    create table category(

    cidvarchar(32) PRIMARY KEY ,

    cnamevarchar(100)        #分类名称

    );

    # 商品表

    CREATE TABLE `products` (

    `pid`varchar(32) PRIMARY KEY  ,

    `name`VARCHAR(40) ,

    `price`DOUBLE

    );

    #添加外键字段

    alter table products add column category_id varchar(32);

    #添加约束

    alter table products add constraint product_fkforeign key (category_id) references category (cid);

    1.3.3    操作

    #1 向分类表中添加数据

    INSERT INTO category (cid ,cname) VALUES('c001','服装');

    #2 向商品表添加普通数据,没有外键数据,默认为null

    INSERT INTO products (pid,pname) VALUES('p001','商品名称');

    #3 向商品表添加普通数据,含有外键信息(数据存放在)

    INSERT INTO products (pid ,pname ,category_id)VALUES('p002','商品名称2','c001');

    #4 向商品表添加普通数据,含有外键信息(数据不存在) -- 不能异常

    INSERT INTO products (pid ,pname ,category_id)VALUES('p003','商品名称2','c999');

    #5 删除指定分类(分类被商品使用) -- 执行异常

    DELETE FROM category WHERE cid = 'c001';

    1.4    多对多

    1.4.1    分析

    • 商品和订单多对多关系,将拆分成两个一对多。

    • products商品表,为其中一个一对多的主表,需要提供主键pid

    • orders 订单表,为另一个一对多的主表,需要提供主键oid

    • orderitem中间表,为另外添加的第三张表,需要提供两个外键oid和pid

    1.4.2    实现:订单和商品

    ### 商品表[已存在]

    ### 订单表

    create table `orders`(

    `oid`varchar(32) PRIMARY KEY ,

    `totalprice` double   #总计

    );

    ### 订单项表

    create table orderitem(

    oidvarchar(50),-- 订单id

    pidvarchar(50)-- 商品id

    );

    ###---- 订单表和订单项表的主外键关系

    alter table `orderitem` add constraintorderitem_orders_fk foreign key (oid) references orders(oid);

    ###---- 商品表和订单项表的主外键关系

    alter table `orderitem` add constraintorderitem_product_fk foreign key (pid) references products(pid);

    ### 联合主键(可省略)

    alter table `orderitem` add primary key (oid,pid);

    1.4.3    操作

    #1 向商品表中添加数据

    INSERT INTO products (pid,pname) VALUES('p003','商品名称');

    #2 向订单表中添加数据

    INSERT INTO orders (oid ,totalprice)VALUES('x001','998');

    INSERT INTO orders (oid ,totalprice)VALUES('x002','100');

    #3向中间表添加数据(数据存在)

    INSERT INTO orderitem(pid,oid)VALUES('p001','x001');

    INSERT INTO orderitem(pid,oid)VALUES('p001','x002');

    INSERT INTO orderitem(pid,oid)VALUES('p002','x002');

    #4删除中间表的数据

    DELETE FROM orderitem WHERE pid='p002' AND oid ='x002';

    #5向中间表添加数据(数据不存在) -- 执行异常

    INSERT INTO orderitem(pid,oid)VALUES('p002','x003');

    #6删除商品表的数据 -- 执行异常

    DELETE FROM products WHERE pid = 'p001';

项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获的更多相关文章

  1. 基于Repository模式设计项目架构—你可以参考的项目架构设计

    关于Repository模式,直接百度查就可以了,其来源是<企业应用架构模式>.我们新建一个Infrastructure文件夹,这里就是基础设施部分,EF Core的上下文类以及Repos ...

  2. asp.net core下的如何给网站做安全设置

    首先,我们来看下stack overflow网站的请求头文件: 可以看到一些我们熟悉或是陌生的HTTP头部文件字段.在这里我们在对HTTP输入流的头部文件中,做一些基本的防护.首先要明确,既然我们是对 ...

  3. asp.net获取服务端和客户端信息

    asp.net获取服务端和客户端信息 获取服务器名:Page.Server.ManchineName获取用户信息:Page.User 获取客户端电脑名:Page.Request.UserHostNam ...

  4. 使用RSA加密在Python中逆向shell

    i春秋翻译小组-Neo(李皓伟) 使用RSA加密在Python中逆向shell 这是一个关于使用RSA加密编程逆向shell的python教程. 我想提一下,这篇文章更多的是关于理解shell中涉及的 ...

  5. 总结项目开发中用到的一些css\html技巧

    这篇就是用来总结记录的,会长期更新. 1,半透明背景效果(#ffffff颜色的半透明背景): font-style: italic;">#ffffff; filter:alpha(op ...

  6. php开发中怎么获取服务端MAC地址?

    MAC(Media Access Control或者Medium Access Control)地址,意译为媒体访问控制,或称为物理地址.硬件地址,用来定义网络设备的位置.在php中如何获取MAC(M ...

  7. 基于ASP.NET WPF技术及MVP模式实战太平人寿客户管理项目开发(Repository模式)

    亲爱的网友,我这里有套课程想和大家分享,假设对这个课程有兴趣的.能够加我的QQ2059055336和我联系.  课程背景 本课程是教授使用WPF.ADO.NET.MVVM技术来实现太平人寿保险有限公司 ...

  8. 使用Jquery+EasyUI 进行框架项目开发案例讲解之五 模块(菜单)管理源码分享

    http://www.cnblogs.com/huyong/p/3454012.html 使用Jquery+EasyUI 进行框架项目开发案例讲解之五  模块(菜单)管理源码分享    在上四篇文章 ...

  9. 使用Jquery+EasyUI 进行框架项目开发案例讲解之四 组织机构管理源码分享

    http://www.cnblogs.com/huyong/p/3404647.html 在上三篇文章  <使用Jquery+EasyUI进行框架项目开发案例讲解之一---员工管理源码分享> ...

随机推荐

  1. Linux Oracle DB log 清理

    sid_list=`/bin/ps -ef |/bin/grep smon |/bin/grep -v grep | /bin/cut -f3 -d_` host_name=`hostname` cd ...

  2. C#发送POST,GET,DELETE请求API,并接受返回值

    发送POST请求 /// <summary> /// API发送POST请求 /// </summary> /// <param name="url" ...

  3. AC日记——Sliding Window poj 2823

    2823 思路: 单调队列: 以前遇到都是用线段树水过: 现在为了优化dp不得不学习单调队列了: 代码: #include <cstdio> #include <cstring> ...

  4. HDU 6299.Balanced Sequence-贪心、前缀和排序 (2018 Multi-University Training Contest 1 1002)

    HDU6299.Balanced Sequence 这个题就是将括号处理一下,先把串里能匹配上的先计数去掉,然后统计左半边括号的前缀和以及右半边括号的前缀和,然后结构体排序,然后遍历一遍,贪心策略走一 ...

  5. (1)C#工具箱-公共控件1

    公共控件 InitializeComponent() 先说下InitializeComponent()这个方法,它在form1.cs里调用这个方法对控件进行初始化,控件的方法要在这个方法之后,否则会因 ...

  6. 基于django rest framework的mock server实践

    网上找了一下mock server的实现,发现python的基本都是基于flask来实现的,因最近在学django,就尝试用drf实现了下: A brief introduction of sui_m ...

  7. HDU 3237 Tree(树链剖分)(线段树区间取反,最大值)

    Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 9123   Accepted: 2411 Description ...

  8. javascript与java的不同之处

    javascript与java的不同之处   虽然很像,但不是一种语言. 二者的区别体现在: 首先,它们是两个公司开发的不同的两个产品,Java是SUN公司推出的新一代面向对象的程序设计语言,特别适合 ...

  9. Visual Studio Package扩展——vsct文件简介

    首先我们使用向导生成一个package的扩展,里面就会发现一个vsct文件.vsct文件的全称是Visual Studio Command Table,它其实就是一个xml文件,通过一定的规则来描述v ...

  10. 利用osql/ocmd批处理批量执行sql文件

    原文:利用osql/ocmd批处理批量执行sql文件 上周在测试环境建了几十张表,保存了.sql文件,准备在正式环境重建的时候懒得一个个打开建了,做一在网上搜寻了一下,果然有简单点的方法. 利用osq ...