项目暂时分为六大块,结构如图所示
代码地址是 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 }