基于.Net Core3.1 MVC + EF Core的项目(一)框架的初步搭建
项目暂时分为六大块,结构如图所示
代码地址是 https://github.com/hudean/VacantCloud-
里面有许多没有完成,不过一些大致的内容都写的差不多了,权限认证依赖注入,支持多种数据库等等。
Vacant.EntityFrameWorkCore 暂时没有用到
一、Vacant.Entity 顾名思义主要是存放与数据库交互的实体类,这个项目库里面主要有一个Entitys文件夹存放实体类,还有实体类用到的2个抽象类和两个接口
1、首先是Entity类代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Reflection;
4 using System.Text;
5
6 namespace Vacant.Entitys
7 {
8 /// <summary>
9 /// A shortcut of <see cref="Entity{TPrimaryKey}"/> for most used primary key type (<see cref="int"/>).
10 /// </summary>
11 [Serializable]
12 public abstract class Entity : Entity<long>, IEntity
13 {
14
15 }
16
17
18 /// <summary>
19 /// Basic implementation of IEntity interface.
20 /// An entity can inherit this class of directly implement to IEntity interface.
21 /// </summary>
22 /// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
23 [Serializable]
24 public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey>
25 {
26 /// <summary>
27 /// Unique identifier for this entity.
28 /// </summary>
29 public virtual TPrimaryKey Id { get; set; }
30
31 /// <summary>
32 /// Checks if this entity is transient (it has not an Id).
33 /// </summary>
34 /// <returns>True, if this entity is transient</returns>
35 public virtual bool IsTransient()
36 {
37 if (EqualityComparer<TPrimaryKey>.Default.Equals(Id, default(TPrimaryKey)))
38 {
39 return true;
40 }
41
42 //Workaround for EF Core since it sets int/long to min value when attaching to dbcontext
43 if (typeof(TPrimaryKey) == typeof(int))
44 {
45 return Convert.ToInt32(Id) <= 0;
46 }
47
48 if (typeof(TPrimaryKey) == typeof(long))
49 {
50 return Convert.ToInt64(Id) <= 0;
51 }
52
53 return false;
54 }
55
56 /// <inheritdoc/>
57 public virtual bool EntityEquals(object obj)
58 {
59 if (obj == null || !(obj is Entity<TPrimaryKey>))
60 {
61 return false;
62 }
63
64 //Same instances must be considered as equal
65 if (ReferenceEquals(this, obj))
66 {
67 return true;
68 }
69
70 //Transient objects are not considered as equal
71 var other = (Entity<TPrimaryKey>)obj;
72 if (IsTransient() && other.IsTransient())
73 {
74 return false;
75 }
76
77 //Must have a IS-A relation of types or must be same type
78 var typeOfThis = GetType();
79 var typeOfOther = other.GetType();
80 if (!typeOfThis.GetTypeInfo().IsAssignableFrom(typeOfOther) && !typeOfOther.GetTypeInfo().IsAssignableFrom(typeOfThis))
81 {
82 return false;
83 }
84
85 //if (this is IMayHaveTenant && other is IMayHaveTenant &&
86 // this.As<IMayHaveTenant>().TenantId != other.As<IMayHaveTenant>().TenantId)
87 //{
88 // return false;
89 //}
90
91 //if (this is IMustHaveTenant && other is IMustHaveTenant &&
92 // this.As<IMustHaveTenant>().TenantId != other.As<IMustHaveTenant>().TenantId)
93 //{
94 // return false;
95 //}
96
97 return Id.Equals(other.Id);
98 }
99
100 public override string ToString()
101 {
102 return $"[{GetType().Name} {Id}]";
103 }
104 }
105 }
2、然后是EntityNotFoundException 类代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Runtime.Serialization;
4 using System.Text;
5 using Vancant.Comman;
6
7 namespace Vacant.Entitys
8 {
9 /// <summary>
10 /// This exception is thrown if an entity excepted to be found but not found.
11 /// </summary>
12 [Serializable]
13 public class EntityNotFoundException : VacantException
14 {
15 /// <summary>
16 /// Type of the entity.
17 /// </summary>
18 public Type EntityType { get; set; }
19
20 /// <summary>
21 /// Id of the Entity.
22 /// </summary>
23 public object Id { get; set; }
24
25 /// <summary>
26 /// Creates a new <see cref="EntityNotFoundException"/> object.
27 /// </summary>
28 public EntityNotFoundException()
29 {
30
31 }
32
33 /// <summary>
34 /// Creates a new <see cref="EntityNotFoundException"/> object.
35 /// </summary>
36 public EntityNotFoundException(SerializationInfo serializationInfo, StreamingContext context)
37 : base(serializationInfo, context)
38 {
39
40 }
41
42 /// <summary>
43 /// Creates a new <see cref="EntityNotFoundException"/> object.
44 /// </summary>
45 public EntityNotFoundException(Type entityType, object id)
46 : this(entityType, id, null)
47 {
48
49 }
50
51 /// <summary>
52 /// Creates a new <see cref="EntityNotFoundException"/> object.
53 /// </summary>
54 public EntityNotFoundException(Type entityType, object id, Exception innerException)
55 : base($"There is no such an entity. Entity type: {entityType.FullName}, id: {id}", innerException)
56 {
57 EntityType = entityType;
58 Id = id;
59 }
60
61 /// <summary>
62 /// Creates a new <see cref="EntityNotFoundException"/> object.
63 /// </summary>
64 /// <param name="message">Exception message</param>
65 public EntityNotFoundException(string message)
66 : base(message)
67 {
68
69 }
70
71 /// <summary>
72 /// Creates a new <see cref="EntityNotFoundException"/> object.
73 /// </summary>
74 /// <param name="message">Exception message</param>
75 /// <param name="innerException">Inner exception</param>
76 public EntityNotFoundException(string message, Exception innerException)
77 : base(message, innerException)
78 {
79
80 }
81 }
82 }
3、然后是接口 IEntityOfTPrimaryKey 代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Vacant.Entitys
6 {
7 /// <summary>
8 /// Defines interface for base entity type. All entities in the system must implement this interface.
9 /// </summary>
10 /// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
11 public interface IEntity<TPrimaryKey>
12 {
13 /// <summary>
14 /// Unique identifier for this entity.
15 /// </summary>
16 TPrimaryKey Id { get; set; }
17
18 /// <summary>
19 /// Checks if this entity is transient (not persisted to database and it has not an <see cref="Id"/>).
20 /// </summary>
21 /// <returns>True, if this entity is transient</returns>
22 bool IsTransient();
23 }
24 }
4、接口 IEntity 的代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Vacant.Entitys
6 {
7 /// <summary>
8 /// A shortcut of <see cref="IEntity{TPrimaryKey}"/> for most used primary key type (<see cref="long"/>).
9 /// </summary>
10 public interface IEntity : IEntity<long>
11 {
12
13 }
14 }
二 、现在 实体类库大概完成了,现在开始建一个仓储类库,Vacant.Repositorys里面有三个文件夹分别是DbContexts 、IRepositorys、Repositorys
1、DbContexts文件夹是放数据库上下文类,添加引用 nuget安装包 Microsoft.EntityFrameworkCore.Design 、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools;建一个类名为MSSqlEFDbContext的数据库上下文类
添加对实体类项目库Entity的引用
代码如下
1 using Microsoft.EntityFrameworkCore;
2 using System;
3 using System.Collections.Generic;
4 using System.Text;
5
6 namespace Vacant.Repositorys.DbContexts
7 {
8 public class MSSqlEFDbContext : DbContext
9 {
10 public MSSqlEFDbContext(DbContextOptions<MSSqlEFDbContext> options) : base(options)
11 {
12
13 }
14
15 protected override void OnModelCreating(ModelBuilder modelBuilder)
16 {
17 base.OnModelCreating(modelBuilder);
18 }
19 //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
20 //{
21 // base.OnConfiguring(optionsBuilder);
22 // //连接数据库-一般不用
23 // optionsBuilder.UseSqlServer("");
24 //}
25 }
26 }
2、IRepositorys 是放仓存类的泛型接口,这样我们就不需要写三层里的dal层,代码如下
(1)先建一个接口IRepository
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Vacant.Repositorys.IRepositorys
6 {
7 /// <summary>
8 /// 仓储接口
9 /// </summary>
10 public interface IRepository
11 {
12
13 }
14 }
(3)再建 接口 IRepositoryOfTEntity
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using Vacant.Entitys;
5
6 namespace Vacant.Repositorys.IRepositorys
7 {
8 /// <summary>
9 /// A shortcut of <see cref="IRepository{TEntity,TPrimaryKey}"/> for most used primary key type (<see cref="long"/>).
10 /// </summary>
11 /// <typeparam name="TEntity">实体类</typeparam>
12 public interface IRepository<TEntity> : IRepository<TEntity, long> where TEntity : class, IEntity<long>
13 {
14
15 }
16 }
(2)再建接口 IRepositoryOfTEntityAndTPrimaryKey
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Linq.Expressions;
5 using System.Text;
6 using System.Threading.Tasks;
7 using Vacant.Entitys;
8
9 namespace Vacant.Repositorys.IRepositorys
10 {
11 /// <summary>
12 /// This interface is implemented by all repositories to ensure implementation of fixed methods.
13 /// </summary>
14 /// <typeparam name="TEntity">Main Entity type this repository works on</typeparam>
15 /// <typeparam name="TPrimaryKey">Primary key type of the entity</typeparam>
16 public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : class, IEntity<TPrimaryKey>
17 {
18 #region Select/Get/Query
19
20 /// <summary>
21 /// Used to get a IQueryable that is used to retrieve entities from entire table.
22 /// </summary>
23 /// <returns>IQueryable to be used to select entities from database</returns>
24 IQueryable<TEntity> GetAll();
25
26 /// <summary>
27 /// Used to get a IQueryable that is used to retrieve entities from entire table.
28 /// One or more
29 /// </summary>
30 /// <param name="propertySelectors">A list of include expressions.</param>
31 /// <returns>IQueryable to be used to select entities from database</returns>
32 IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors);
33
34 /// <summary>
35 /// Used to get all entities.
36 /// </summary>
37 /// <returns>List of all entities</returns>
38 List<TEntity> GetAllList();
39
40 /// <summary>
41 /// Used to get all entities.
42 /// </summary>
43 /// <returns>List of all entities</returns>
44 Task<List<TEntity>> GetAllListAsync();
45
46 /// <summary>
47 /// Used to get all entities based on given <paramref name="predicate"/>.
48 /// </summary>
49 /// <param name="predicate">A condition to filter entities</param>
50 /// <returns>List of all entities</returns>
51 List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);
52
53 /// <summary>
54 /// Used to get all entities based on given <paramref name="predicate"/>.
55 /// </summary>
56 /// <param name="predicate">A condition to filter entities</param>
57 /// <returns>List of all entities</returns>
58 Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate);
59
60 /// <summary>
61 /// Used to run a query over entire entities.
62 /// <see cref="UnitOfWorkAttribute"/> attribute is not always necessary (as opposite to <see cref="GetAll"/>)
63 /// if <paramref name="queryMethod"/> finishes IQueryable with ToList, FirstOrDefault etc..
64 /// </summary>
65 /// <typeparam name="T">Type of return value of this method</typeparam>
66 /// <param name="queryMethod">This method is used to query over entities</param>
67 /// <returns>Query result</returns>
68 T Query<T>(Func<IQueryable<TEntity>, T> queryMethod);
69
70 /// <summary>
71 /// Gets an entity with given primary key.
72 /// </summary>
73 /// <param name="id">Primary key of the entity to get</param>
74 /// <returns>Entity</returns>
75 TEntity Get(TPrimaryKey id);
76
77 /// <summary>
78 /// Gets an entity with given primary key.
79 /// </summary>
80 /// <param name="id">Primary key of the entity to get</param>
81 /// <returns>Entity</returns>
82 Task<TEntity> GetAsync(TPrimaryKey id);
83
84 /// <summary>
85 /// Gets exactly one entity with given predicate.
86 /// Throws exception if no entity or more than one entity.
87 /// </summary>
88 /// <param name="predicate">Entity</param>
89 TEntity Single(Expression<Func<TEntity, bool>> predicate);
90
91 /// <summary>
92 /// Gets exactly one entity with given predicate.
93 /// Throws exception if no entity or more than one entity.
94 /// </summary>
95 /// <param name="predicate">Entity</param>
96 Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate);
97
98 /// <summary>
99 /// Gets an entity with given primary key or null if not found.
100 /// </summary>
101 /// <param name="id">Primary key of the entity to get</param>
102 /// <returns>Entity or null</returns>
103 TEntity FirstOrDefault(TPrimaryKey id);
104
105 /// <summary>
106 /// Gets an entity with given primary key or null if not found.
107 /// </summary>
108 /// <param name="id">Primary key of the entity to get</param>
109 /// <returns>Entity or null</returns>
110 Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id);
111
112 /// <summary>
113 /// Gets an entity with given given predicate or null if not found.
114 /// </summary>
115 /// <param name="predicate">Predicate to filter entities</param>
116 TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
117
118 /// <summary>
119 /// Gets an entity with given given predicate or null if not found.
120 /// </summary>
121 /// <param name="predicate">Predicate to filter entities</param>
122 Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
123
124 /// <summary>
125 /// Creates an entity with given primary key without database access.
126 /// </summary>
127 /// <param name="id">Primary key of the entity to load</param>
128 /// <returns>Entity</returns>
129 TEntity Load(TPrimaryKey id);
130
131 #endregion
132
133 #region Insert
134
135 /// <summary>
136 /// Inserts a new entity.
137 /// </summary>
138 /// <param name="entity">Inserted entity</param>
139 TEntity Insert(TEntity entity);
140
141 /// <summary>
142 /// Inserts a new entity.
143 /// </summary>
144 /// <param name="entity">Inserted entity</param>
145 Task<TEntity> InsertAsync(TEntity entity);
146
147 /// <summary>
148 /// Inserts a new entity and gets it's Id.
149 /// It may require to save current unit of work
150 /// to be able to retrieve id.
151 /// </summary>
152 /// <param name="entity">Entity</param>
153 /// <returns>Id of the entity</returns>
154 TPrimaryKey InsertAndGetId(TEntity entity);
155
156 /// <summary>
157 /// Inserts a new entity and gets it's Id.
158 /// It may require to save current unit of work
159 /// to be able to retrieve id.
160 /// </summary>
161 /// <param name="entity">Entity</param>
162 /// <returns>Id of the entity</returns>
163 Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity);
164
165 /// <summary>
166 /// Inserts or updates given entity depending on Id's value.
167 /// </summary>
168 /// <param name="entity">Entity</param>
169 TEntity InsertOrUpdate(TEntity entity);
170
171 /// <summary>
172 /// Inserts or updates given entity depending on Id's value.
173 /// </summary>
174 /// <param name="entity">Entity</param>
175 Task<TEntity> InsertOrUpdateAsync(TEntity entity);
176
177 /// <summary>
178 /// Inserts or updates given entity depending on Id's value.
179 /// Also returns Id of the entity.
180 /// It may require to save current unit of work
181 /// to be able to retrieve id.
182 /// </summary>
183 /// <param name="entity">Entity</param>
184 /// <returns>Id of the entity</returns>
185 TPrimaryKey InsertOrUpdateAndGetId(TEntity entity);
186
187 /// <summary>
188 /// Inserts or updates given entity depending on Id's value.
189 /// Also returns Id of the entity.
190 /// It may require to save current unit of work
191 /// to be able to retrieve id.
192 /// </summary>
193 /// <param name="entity">Entity</param>
194 /// <returns>Id of the entity</returns>
195 Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity);
196
197 #endregion
198
199 #region Update
200
201 /// <summary>
202 /// Updates an existing entity.
203 /// </summary>
204 /// <param name="entity">Entity</param>
205 TEntity Update(TEntity entity);
206
207 /// <summary>
208 /// Updates an existing entity.
209 /// </summary>
210 /// <param name="entity">Entity</param>
211 Task<TEntity> UpdateAsync(TEntity entity);
212
213 /// <summary>
214 /// Updates an existing entity.
215 /// </summary>
216 /// <param name="id">Id of the entity</param>
217 /// <param name="updateAction">Action that can be used to change values of the entity</param>
218 /// <returns>Updated entity</returns>
219 TEntity Update(TPrimaryKey id, Action<TEntity> updateAction);
220
221 /// <summary>
222 /// Updates an existing entity.
223 /// </summary>
224 /// <param name="id">Id of the entity</param>
225 /// <param name="updateAction">Action that can be used to change values of the entity</param>
226 /// <returns>Updated entity</returns>
227 Task<TEntity> UpdateAsync(TPrimaryKey id, Func<TEntity, Task> updateAction);
228
229 #endregion
230
231 #region Delete
232
233 /// <summary>
234 /// Deletes an entity.
235 /// </summary>
236 /// <param name="entity">Entity to be deleted</param>
237 void Delete(TEntity entity);
238
239 /// <summary>
240 /// Deletes an entity.
241 /// </summary>
242 /// <param name="entity">Entity to be deleted</param>
243 Task DeleteAsync(TEntity entity);
244
245 /// <summary>
246 /// Deletes an entity by primary key.
247 /// </summary>
248 /// <param name="id">Primary key of the entity</param>
249 void Delete(TPrimaryKey id);
250
251 /// <summary>
252 /// Deletes an entity by primary key.
253 /// </summary>
254 /// <param name="id">Primary key of the entity</param>
255 Task DeleteAsync(TPrimaryKey id);
256
257 /// <summary>
258 /// Deletes many entities by function.
259 /// Notice that: All entities fits to given predicate are retrieved and deleted.
260 /// This may cause major performance problems if there are too many entities with
261 /// given predicate.
262 /// </summary>
263 /// <param name="predicate">A condition to filter entities</param>
264 void Delete(Expression<Func<TEntity, bool>> predicate);
265
266 /// <summary>
267 /// Deletes many entities by function.
268 /// Notice that: All entities fits to given predicate are retrieved and deleted.
269 /// This may cause major performance problems if there are too many entities with
270 /// given predicate.
271 /// </summary>
272 /// <param name="predicate">A condition to filter entities</param>
273 Task DeleteAsync(Expression<Func<TEntity, bool>> predicate);
274
275 #endregion
276
277 #region Aggregates
278
279 /// <summary>
280 /// Gets count of all entities in this repository.
281 /// </summary>
282 /// <returns>Count of entities</returns>
283 int Count();
284
285 /// <summary>
286 /// Gets count of all entities in this repository.
287 /// </summary>
288 /// <returns>Count of entities</returns>
289 Task<int> CountAsync();
290
291 /// <summary>
292 /// Gets count of all entities in this repository based on given <paramref name="predicate"/>.
293 /// </summary>
294 /// <param name="predicate">A method to filter count</param>
295 /// <returns>Count of entities</returns>
296 int Count(Expression<Func<TEntity, bool>> predicate);
297
298 /// <summary>
299 /// Gets count of all entities in this repository based on given <paramref name="predicate"/>.
300 /// </summary>
301 /// <param name="predicate">A method to filter count</param>
302 /// <returns>Count of entities</returns>
303 Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate);
304
305 /// <summary>
306 /// Gets count of all entities in this repository (use if expected return value is greather than <see cref="int.MaxValue"/>.
307 /// </summary>
308 /// <returns>Count of entities</returns>
309 long LongCount();
310
311 /// <summary>
312 /// Gets count of all entities in this repository (use if expected return value is greather than <see cref="int.MaxValue"/>.
313 /// </summary>
314 /// <returns>Count of entities</returns>
315 Task<long> LongCountAsync();
316
317 /// <summary>
318 /// Gets count of all entities in this repository based on given <paramref name="predicate"/>
319 /// (use this overload if expected return value is greather than <see cref="int.MaxValue"/>).
320 /// </summary>
321 /// <param name="predicate">A method to filter count</param>
322 /// <returns>Count of entities</returns>
323 long LongCount(Expression<Func<TEntity, bool>> predicate);
324
325 /// <summary>
326 /// Gets count of all entities in this repository based on given <paramref name="predicate"/>
327 /// (use this overload if expected return value is greather than <see cref="int.MaxValue"/>).
328 /// </summary>
329 /// <param name="predicate">A method to filter count</param>
330 /// <returns>Count of entities</returns>
331 Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate);
332
333 #endregion
334 }
335 }
3、Repositorys 是放仓存类的泛型类的
(1)建一个 RepositoryBase 抽象父类实现IRepository接口,代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Linq.Expressions;
5 using System.Text;
6 using System.Threading.Tasks;
7 using Vacant.Entitys;
8 using Vacant.Repositorys.IRepositorys;
9
10 namespace Vacant.Repositorys.Repositorys
11 {
12 /// <summary>
13 /// 抽象仓储类
14 /// </summary>
15 /// <typeparam name="TEntity">实体类</typeparam>
16 /// <typeparam name="TPrimaryKey">主键</typeparam>
17 public abstract class RepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey>
18 {
19 public abstract IQueryable<TEntity> GetAll();
20
21 public virtual IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
22 {
23 return GetAll();
24 }
25
26 public virtual List<TEntity> GetAllList()
27 {
28 return GetAll().ToList();
29 }
30
31 public virtual Task<List<TEntity>> GetAllListAsync()
32 {
33 return Task.FromResult(GetAllList());
34 }
35
36 public virtual List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
37 {
38 return GetAll().Where(predicate).ToList();
39 }
40
41 public virtual Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate)
42 {
43 return Task.FromResult(GetAllList(predicate));
44 }
45
46 public virtual T Query<T>(Func<IQueryable<TEntity>, T> queryMethod)
47 {
48 return queryMethod(GetAll());
49 }
50
51 public virtual TEntity Get(TPrimaryKey id)
52 {
53 var entity = FirstOrDefault(id);
54 if (entity == null)
55 {
56 throw new EntityNotFoundException(typeof(TEntity), id);
57 }
58
59 return entity;
60 }
61
62 public virtual async Task<TEntity> GetAsync(TPrimaryKey id)
63 {
64 var entity = await FirstOrDefaultAsync(id);
65 if (entity == null)
66 {
67 throw new EntityNotFoundException(typeof(TEntity), id);
68 }
69
70 return entity;
71 }
72
73 public virtual TEntity Single(Expression<Func<TEntity, bool>> predicate)
74 {
75 return GetAll().Single(predicate);
76 }
77
78 public virtual Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
79 {
80 return Task.FromResult(Single(predicate));
81 }
82
83 public virtual TEntity FirstOrDefault(TPrimaryKey id)
84 {
85 return GetAll().FirstOrDefault(CreateEqualityExpressionForId(id));
86 }
87
88 public virtual Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id)
89 {
90 return Task.FromResult(FirstOrDefault(id));
91 }
92
93 public virtual TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
94 {
95 return GetAll().FirstOrDefault(predicate);
96 }
97
98 public virtual Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
99 {
100 return Task.FromResult(FirstOrDefault(predicate));
101 }
102
103 public virtual TEntity Load(TPrimaryKey id)
104 {
105 return Get(id);
106 }
107
108 public abstract TEntity Insert(TEntity entity);
109
110 public virtual Task<TEntity> InsertAsync(TEntity entity)
111 {
112 return Task.FromResult(Insert(entity));
113 }
114
115 public virtual TPrimaryKey InsertAndGetId(TEntity entity)
116 {
117 return Insert(entity).Id;
118 }
119
120 public virtual async Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity)
121 {
122 var insertedEntity = await InsertAsync(entity);
123 return insertedEntity.Id;
124 }
125
126 public virtual TEntity InsertOrUpdate(TEntity entity)
127 {
128 return entity.IsTransient()
129 ? Insert(entity)
130 : Update(entity);
131 }
132
133 public virtual async Task<TEntity> InsertOrUpdateAsync(TEntity entity)
134 {
135 return entity.IsTransient()
136 ? await InsertAsync(entity)
137 : await UpdateAsync(entity);
138 }
139
140 public virtual TPrimaryKey InsertOrUpdateAndGetId(TEntity entity)
141 {
142 return InsertOrUpdate(entity).Id;
143 }
144
145 public virtual async Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity)
146 {
147 var insertedEntity = await InsertOrUpdateAsync(entity);
148 return insertedEntity.Id;
149 }
150
151 public abstract TEntity Update(TEntity entity);
152
153 public virtual Task<TEntity> UpdateAsync(TEntity entity)
154 {
155 return Task.FromResult(Update(entity));
156 }
157
158 public virtual TEntity Update(TPrimaryKey id, Action<TEntity> updateAction)
159 {
160 var entity = Get(id);
161 updateAction(entity);
162 return entity;
163 }
164
165 public virtual async Task<TEntity> UpdateAsync(TPrimaryKey id, Func<TEntity, Task> updateAction)
166 {
167 var entity = await GetAsync(id);
168 await updateAction(entity);
169 return entity;
170 }
171
172 public abstract void Delete(TEntity entity);
173
174 public virtual Task DeleteAsync(TEntity entity)
175 {
176 Delete(entity);
177 return Task.CompletedTask;
178 }
179
180 public abstract void Delete(TPrimaryKey id);
181
182 public virtual Task DeleteAsync(TPrimaryKey id)
183 {
184 Delete(id);
185 return Task.CompletedTask;
186 }
187
188 public virtual void Delete(Expression<Func<TEntity, bool>> predicate)
189 {
190 foreach (var entity in GetAllList(predicate))
191 {
192 Delete(entity);
193 }
194 }
195
196 public virtual async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate)
197 {
198 var entities = await GetAllListAsync(predicate);
199
200 foreach (var entity in entities)
201 {
202 await DeleteAsync(entity);
203 }
204 }
205
206 public virtual int Count()
207 {
208 return GetAll().Count();
209 }
210
211 public virtual Task<int> CountAsync()
212 {
213 return Task.FromResult(Count());
214 }
215
216 public virtual int Count(Expression<Func<TEntity, bool>> predicate)
217 {
218 return GetAll().Count(predicate);
219 }
220
221 public virtual Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate)
222 {
223 return Task.FromResult(Count(predicate));
224 }
225
226 public virtual long LongCount()
227 {
228 return GetAll().LongCount();
229 }
230
231 public virtual Task<long> LongCountAsync()
232 {
233 return Task.FromResult(LongCount());
234 }
235
236 public virtual long LongCount(Expression<Func<TEntity, bool>> predicate)
237 {
238 return GetAll().LongCount(predicate);
239 }
240
241 public virtual Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate)
242 {
243 return Task.FromResult(LongCount(predicate));
244 }
245
246 protected virtual Expression<Func<TEntity, bool>> CreateEqualityExpressionForId(TPrimaryKey id)
247 {
248 var lambdaParam = Expression.Parameter(typeof(TEntity));
249
250 var leftExpression = Expression.PropertyOrField(lambdaParam, "Id");
251
252 var idValue = Convert.ChangeType(id, typeof(TPrimaryKey));
253
254 Expression<Func<object>> closure = () => idValue;
255 var rightExpression = Expression.Convert(closure.Body, leftExpression.Type);
256
257 var lambdaBody = Expression.Equal(leftExpression, rightExpression);
258
259 return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
260 }
261 }
262 }
(2)建一个 子类EFRepository 继承RepositoryBase 这个类,代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using Vacant.Entitys;
6 using Vacant.Repositorys.DbContexts;
7 using Vacant.Repositorys.IRepositorys;
8
9 namespace Vacant.Repositorys.Repositorys
10 {
11 /// <summary>
12 /// 使用Ef的sqlserver版本
13 /// </summary>
14 /// <typeparam name="TEntity"></typeparam>
15 public class EFRepository<TEntity> : EFRepository<TEntity, long>, IRepository<TEntity> where TEntity : class, IEntity<long>
16 {
17 public EFRepository(MSSqlEFDbContext dbContext) : base(dbContext)
18 {
19
20 }
21 }
22
23
24 public class EFRepository<TEntity, TPrimaryKey> : RepositoryBase<TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey>
25 {
26 public readonly MSSqlEFDbContext _dbContext;
27
28 public EFRepository(MSSqlEFDbContext dbContext)
29 {
30 _dbContext = dbContext;
31 }
32 public override void Delete(TEntity entity)
33 {
34 _dbContext.Set<TEntity>().Remove(entity);
35 _dbContext.SaveChanges();
36 }
37
38 public override void Delete(TPrimaryKey id)
39 {
40 var entity = _dbContext.Set<TEntity>().SingleOrDefault(t => t.Id.Equals(id));
41 Delete(entity);
42
43 }
44
45 public override IQueryable<TEntity> GetAll()
46 {
47 return _dbContext.Set<TEntity>().AsQueryable();
48 }
49
50 public override TEntity Insert(TEntity entity)
51 {
52 _dbContext.Set<TEntity>().Add(entity);
53 _dbContext.SaveChanges();
54 return entity;
55 }
56
57 public override TEntity Update(TEntity entity)
58 {
59 _dbContext.Set<TEntity>().Update(entity);
60 _dbContext.SaveChanges();
61 return entity;
62 }
63 }
64
65 }
三、现在泛型仓储库大致完成了,我们开始建一个Vacant.Services类库、里面有两个文件夹,分别是IServices 和Services ,添加对仓库项目库和实体项目库的引用
四、Vancant.Comman是项目公共帮助类库 ,先建3个类分别是 HttpHelper、VacantException、ValidateCode,上面的类库都要添加对此项目的引用
(1)HttpHelper是发送http请求的帮助类的代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Net.Http;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace Vancant.Comman
8 {
9 /// <summary>
10 /// http请求帮助类
11 /// </summary>
12 public class HttpHelper
13 {
14 /// <summary>
15 /// 发起POST同步请求
16 ///
17 /// </summary>
18 /// <param name="url"></param>
19 /// <param name="postData"></param>
20 /// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
21 /// <param name="headers">填充消息头</param>
22 /// <returns></returns>
23 public static string HttpPost(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary<string, string> headers = null)
24 {
25 postData = postData ?? "";
26 using (HttpClient client = new HttpClient())
27 {
28 if (headers != null)
29 {
30 foreach (var header in headers)
31 client.DefaultRequestHeaders.Add(header.Key, header.Value);
32 }
33 using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8))
34 {
35 if (contentType != null)
36 httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
37
38 HttpResponseMessage response = client.PostAsync(url, httpContent).Result;
39 return response.Content.ReadAsStringAsync().Result;
40 }
41 }
42 }
43
44
45 /// <summary>
46 /// 发起POST异步请求
47 /// </summary>
48 /// <param name="url"></param>
49 /// <param name="postData"></param>
50 /// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
51 /// <param name="headers">填充消息头</param>
52 /// <returns></returns>
53 public static async Task<string> HttpPostAsync(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary<string, string> headers = null)
54 {
55 postData = postData ?? "";
56 using (HttpClient client = new HttpClient())
57 {
58 client.Timeout = new TimeSpan(0, 0, timeOut);
59 if (headers != null)
60 {
61 foreach (var header in headers)
62 client.DefaultRequestHeaders.Add(header.Key, header.Value);
63 }
64 using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8))
65 {
66 if (contentType != null)
67 httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
68
69 HttpResponseMessage response = await client.PostAsync(url, httpContent);
70 return await response.Content.ReadAsStringAsync();
71 }
72 }
73 }
74
75 /// <summary>
76 /// 发起GET同步请求
77 /// </summary>
78 /// <param name="url"></param>
79 /// <param name="headers"></param>
80 /// <param name="contentType"></param>
81 /// <returns></returns>
82 public static string HttpGet(string url, Dictionary<string, string> headers = null)
83 {
84 using (HttpClient client = new HttpClient())
85 {
86 if (headers != null)
87 {
88 foreach (var header in headers)
89 client.DefaultRequestHeaders.Add(header.Key, header.Value);
90 }
91 HttpResponseMessage response = client.GetAsync(url).Result;
92 return response.Content.ReadAsStringAsync().Result;
93 }
94 }
95
96 /// <summary>
97 /// 发起GET异步请求
98 /// </summary>
99 /// <param name="url"></param>
100 /// <param name="headers"></param>
101 /// <param name="contentType"></param>
102 /// <returns></returns>
103 public static async Task<string> HttpGetAsync(string url, Dictionary<string, string> headers = null)
104 {
105 using (HttpClient client = new HttpClient())
106 {
107 if (headers != null)
108 {
109 foreach (var header in headers)
110 client.DefaultRequestHeaders.Add(header.Key, header.Value);
111 }
112 HttpResponseMessage response = await client.GetAsync(url);
113 return await response.Content.ReadAsStringAsync();
114 }
115 }
116 }
117 }
(2)VacantException是错误异常
1 using System;
2 using System.Collections.Generic;
3 using System.Runtime.Serialization;
4 using System.Text;
5
6 namespace Vancant.Comman
7 {
8 /// <summary>
9 /// Base exception type for those are thrown by Abp system for Abp specific exceptions.
10 /// </summary>
11 [Serializable]
12 public class VacantException : Exception
13 {
14 /// <summary>
15 /// Creates a new <see cref="AbpException"/> object.
16 /// </summary>
17 public VacantException()
18 {
19
20 }
21
22 /// <summary>
23 /// Creates a new <see cref="AbpException"/> object.
24 /// </summary>
25 public VacantException(SerializationInfo serializationInfo, StreamingContext context)
26 : base(serializationInfo, context)
27 {
28
29 }
30
31 /// <summary>
32 /// Creates a new <see cref="AbpException"/> object.
33 /// </summary>
34 /// <param name="message">Exception message</param>
35 public VacantException(string message)
36 : base(message)
37 {
38
39 }
40
41 /// <summary>
42 /// Creates a new <see cref="AbpException"/> object.
43 /// </summary>
44 /// <param name="message">Exception message</param>
45 /// <param name="innerException">Inner exception</param>
46 public VacantException(string message, Exception innerException)
47 : base(message, innerException)
48 {
49
50 }
51 }
52 }
(3)ValidateCode是生成随机的验证码 ,注意 一定添加nuget安装包 System.Drawing.Common
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Drawing;
5 using System.Drawing.Imaging;
6 using System.IO;
7
8 namespace Vancant.Comman
9 {
10 /// <summary>
11 /// 验证码
12 /// </summary>
13 public class ValidateCode
14 {
15 public byte[] GetVerifyCode(out string code)
16 {
17 code = string.Empty;
18 int codeW = 80;
19 int codeH = 30;
20 int fontSize = 16;
21 string chkCode = string.Empty;
22 Random rnd = new Random();
23 //颜色列表,用于验证码、噪线、噪点
24 Color[] color = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.Brown, Color.DarkBlue };
25 //字体列表,用于验证码
26 string[] font = { "Times New Roman" };
27 //验证码的字符集,去掉了一些容易混淆的字符
28 char[] character = { '2', '3', '4', '5', '6', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'h', 'k', 'm', 'n', 'r', 'x', 'y', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', 'X', 'Y' };
29 //生成验证码字符串
30 for (int i = 0; i < 4; i++)
31 {
32 chkCode += character[rnd.Next(character.Length)];
33 }
34 code = chkCode;
35
36 //创建画布
37 Bitmap bmp = new Bitmap(codeW, codeH);
38 Graphics g = Graphics.FromImage(bmp);
39 g.Clear(Color.White);
40 //画噪线
41 for (int i = 0; i < 1; i++)
42 {
43 int x1 = rnd.Next(codeW);
44 int y1 = rnd.Next(codeH);
45 int x2 = rnd.Next(codeW);
46 int y2 = rnd.Next(codeH);
47 Color clr = color[rnd.Next(color.Length)];
48 g.DrawLine(new Pen(clr), x1, y1, x2, y2);
49 }
50 //画验证码字符串
51 for (int i = 0; i < chkCode.Length; i++)
52 {
53 string fnt = font[rnd.Next(font.Length)];
54 Font ft = new Font(fnt, fontSize);
55 Color clr = color[rnd.Next(color.Length)];
56 g.DrawString(chkCode[i].ToString(), ft, new SolidBrush(clr), (float)i * 18, (float)0);
57 }
58 //将验证码图片写入内存流,并将其以 "image/Png" 格式输出
59 MemoryStream ms = new MemoryStream();
60 try
61 {
62 bmp.Save(ms, ImageFormat.Png);
63 return ms.ToArray();
64 }
65 catch (Exception)
66 {
67 return null;
68 }
69 finally
70 {
71 g.Dispose();
72 bmp.Dispose();
73 }
74 }
75
76 }
77 }
五、现在开始建.net core mvc 3.1 版本的web应用程序 Vacant.Web添加对上面四个项目的引用
statupp类进行配置,里面代码如下,
services.AddScoped(typeof(IRepository<,>), typeof(EFRepository<,>));
services.AddScoped(typeof(IRepository<>), typeof(EFRepository<>)); 是泛型注入
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Reflection;
5 using System.Threading.Tasks;
6 using Microsoft.AspNetCore.Builder;
7 using Microsoft.AspNetCore.Hosting;
8 using Microsoft.AspNetCore.HttpsPolicy;
9 using Microsoft.EntityFrameworkCore;
10 using Microsoft.Extensions.Configuration;
11 using Microsoft.Extensions.DependencyInjection;
12 using Microsoft.Extensions.Hosting;
13 using Vacant.Repositorys.DbContexts;
14 using Vacant.Repositorys.IRepositorys;
15 using Vacant.Repositorys.Repositorys;
16 using Vacant.Services.IServices;
17
18 namespace Vacant.Web
19 {
20 public class Startup
21 {
22 public Startup(IConfiguration configuration)
23 {
24 Configuration = configuration;
25 }
26
27 public IConfiguration Configuration { get; }
28
29 // This method gets called by the runtime. Use this method to add services to the container.
30 public void ConfigureServices(IServiceCollection services)
31 {
32 services.AddControllersWithViews();
33 //注册服务连接数据库
34 services.AddDbContext<MSSqlEFDbContext>(options =>
35 {
36 options.UseSqlServer(Configuration.GetConnectionString("Default"));//获取配置的连接字符串
37 //options.UseMySql(Configuration.GetConnectionString("Default"));
38 //options.UseSqlite(Configuration.GetConnectionString("Default"));
39 //options.UseNpgsql(Configuration.GetConnectionString("Default"));
40 });
41
42 #region 依赖注入仓储
43
44 //重点仓储和服务注入方式要一样
45 //依赖注入仓储
46 services.AddScoped(typeof(IRepository<,>), typeof(EFRepository<,>));
47 services.AddScoped(typeof(IRepository<>), typeof(EFRepository<>));
48
49 //services.AddScoped<IUserService,UserService>();
50 //services.AddScoped<IRoleService, RoleService>();
51 #endregion
52
53 #region 批量注入
54 //加载程序集MyApplication
55 var serviceAsm = Assembly.Load(new AssemblyName("Vacant.Services"));
56 foreach (Type serviceType in serviceAsm.GetTypes().Where(t => typeof(IBaseService).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract))
57 {
58 var interfaceTypes = serviceType.GetInterfaces();
59 foreach (var interfaceType in interfaceTypes)
60 {
61 services.AddScoped(interfaceType, serviceType);
62 }
63 }
64
65 #endregion
66 services.AddSession();
67 services.AddMvc(options =>
68 {
69 options.Filters.Add<Filter.MyErrorExceptionFilter>();
70 });
71 }
72
73 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
74 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
75 {
76 if (env.IsDevelopment())
77 {
78 app.UseDeveloperExceptionPage();
79 }
80 else
81 {
82 app.UseExceptionHandler("/Home/Error");
83 // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
84 app.UseHsts();
85 }
86 app.UseHttpsRedirection();
87 app.UseStaticFiles();
88
89 app.UseRouting();
90
91 app.UseAuthorization();
92
93 app.UseEndpoints(endpoints =>
94 {
95 endpoints.MapControllerRoute(
96 name: "default",
97 pattern: "{controller=Home}/{action=Index}/{id?}");
98 });
99 }
100 }
101 }
基于.Net Core3.1 MVC + EF Core的项目(一)框架的初步搭建的更多相关文章
- 开源题材征集 + MVC&EF Core 完整教程小结
到目前为止,我们的MVC+EF Core 完整教程的理论部分就全部结束了,共20篇,覆盖了核心的主要知识点. 下一阶段是实战部分,我们将会把这些知识点串联起来,用10篇(天)来完成一个开源项目. 现向 ...
- 国产化之路-统信UOS + Nginx + Asp.Net MVC + EF Core 3.1 + 达梦DM8实现简单增删改查操作
专题目录 国产化之路-统信UOS操作系统安装 国产化之路-国产操作系统安装.net core 3.1 sdk 国产化之路-安装WEB服务器 国产化之路-安装达梦DM8数据库 国产化之路-统信UOS + ...
- Asp.net MVC + EF + Spring.Net 项目实践3
Asp.net MVC + EF + Spring.Net 项目实践 这一篇要整合Model层和Repository层,提供一个统一的操作entity的接口层,代码下载地址(博客园上传不了10M以上的 ...
- Asp.net MVC + EF + Spring.Net 项目实践(目录)
用4篇博客来搭一个MVC的框架,可能对初学者会有一些帮助,大家共勉吧.我觉得对于中小型项目,这个框架可能还是有一定的用处的,希望能够帮助到一些人. Asp.net MVC + EF + Spring. ...
- ASP.NET Core MVC+EF Core从开发到部署
笔记本电脑装了双系统(Windows 10和Ubuntu16.04)快半年了,平时有时间就喜欢切换到Ubuntu系统下耍耍Linux,熟悉熟悉Linux命令.Shell脚本以及Linux下的各种应用的 ...
- ASP.NET Core MVC+EF Core项目实战
项目背景 本项目参考于<Pro Entity Framework Core 2 for ASP.NET Core MVC>一书,项目内容为party邀请答复. 新建项目 本项目开发工具为V ...
- ABP Framework:移除 EF Core Migrations 项目,统一数据上下文
原文:Unifying DbContexts for EF Core / Removing the EF Core Migrations Project 目录 导读:软件开发的一切都需要平衡 动机 警 ...
- .Net Core2.2 + EF Core + DI,三层框架项目搭建教程
笔记: 近两年.Net Core发展的很快,目前最新版为3.0预览版,之前在网上买了一本1.1版书籍都还没来得及看呢,估计现在拿出来看也毫无意义了.已多年.net工作经验,看书不如直接实际上手来得快, ...
- ABP CORE 框架入门视频教程《电话薄》基于 Asp.NET Core2.0 EF Core
ABP框架简介 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...
- 分享基于.NET MVC+EF CodeFirst+IOC+EasyUI的框架设计
**注:要做工,没什么时间,等有空时会上传到GIT,项目结构如上,简单的说一下: **支持IOC及多数据库等,各项目由MVC区域隔离: 主要使用基于接口与抽象类进行高度的抽象与接口隔离,与其它框架比较 ...
随机推荐
- Mac 常用软件、快捷健、常用操作 和 Windows 对比
常用快捷健 Mac Windows 说明 活动监视器 任务管理器 制作替身 创建快捷方式 Command + I 右击属性 显示简介 Command + Option + I 开启信息检查器 + 鼠标 ...
- PPT 动画-莲花绽放
画两圆,合并形状 -> 相交 复制8个图片,一共9片 旋转 最后动画 -> 平滑
- vue3.0 学习使用记录
vue学习 在最近的一次项目中,前端js框架里使用了vue.vue的编程思想比较新颖,用起来也感觉高效便捷.由于个人原因,做完这个项目之后可能就接触不到前端的内容了,所以记录这个项目中对vue的学习和 ...
- anaconda学习(未完成)
1.Anaconda安装教程(以32.7.4为例)官网地址:https://www.anaconda.com/download(如无法下载可跳转清华源下载)下载完成后点击打开即可安装点击Next选择I ...
- WebSoket 的广泛应用
目前大多数网站都在使用的传统 HTTP 协议,即由 Web 服务器通过 HTTP 接收并响应来自客户端的消息,整个发起请求与响应的过程类似我们点外卖,由以下 2 部分构成: 下订单(发起请求):用户( ...
- 【JAVA基础】Swagger使用
Swagger使用 刷新权限 自定标签名称
- 汇编 | 8086 DEBUG调试学习笔记
在8086汇编中DEBUG是个非常实用的工具,并且可以非常明了的查看每一步指令每一个段的相对状态,有利于学习.下面列举一下DEBUG的一些使用方法: -A:可以开始在相应位置编写代码,其中后面可以接一 ...
- 0x02 基本算法-枚举、模拟、递推
递归实现指数型枚举 int _, n, m, k, x, y; vector<int> vec; void calc(int x) { if (x == n + 1) { for (int ...
- <vue 路由 3、路由代码跳转>
说明:在上一节的工程下继续讲解 一. 知识点说明 业务开发中更多的是使用代码方式进行页面的跳转会用到this.$router.push('/') 和this.$router.replace(' ...
- C#利用折线图分析产品销售走势
图形界面 数据 查询效果 代码 private void button1_Click(object sender, EventArgs e) { G++; DrowFont(this.comboBox ...