目录索引 

【无私分享:ASP.NET CORE 项目实战】目录索引

简介

  

  经过 N 久反复的尝试,翻阅了网上无数的资料,GitHub上下载了十几个源码参考, Memory 和 Redis 终于写出一个 简陋 的 封装,为了统一和易用,我们两种缓存都统一实现了一个接口 ICacheService,微软也有很多是通过IDistributedCache,大家可以参考 https://docs.asp.net/en/latest/performance/caching/distributed.html ,GitHub上也有很多很好的封装,这里我们就不一一介绍了,大家自行参考,现在搞 Asp.net Core的还是不是很多,中文的资料也少的可怜,而且基本都是千篇一律照搬,对于只认识 ABCDEFG的我来说,过程是十分艰辛的,一篇文章往往要看四五遍,逐行逐句翻译,说多了都是泪,不说了,我们开始。期间,得到了很多朋友的帮助,在此表示感谢!

  

缓存接口 ICacheService

  缓存也好,数据库也好,我们就是进行CRUD操作,接口没什么好解释的,注释我写的很明白,这里就列给大家:

  #  验证缓存项是否存在

         /// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
bool Exists(string key); /// <summary>
/// 验证缓存项是否存在(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
Task<bool> ExistsAsync(string key);

  # 添加缓存

  /// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <returns></returns>
bool Add(string key, object value); /// <summary>
/// 添加缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <returns></returns>
Task<bool> AddAsync(string key, object value); /// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte); /// <summary>
/// 添加缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
Task<bool> AddAsync(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte); /// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
bool Add(string key, object value, TimeSpan expiresIn, bool isSliding = false); /// <summary>
/// 添加缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
Task<bool> AddAsync(string key, object value, TimeSpan expiresIn, bool isSliding = false);

  # 删除缓存

  /// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
bool Remove(string key); /// <summary>
/// 删除缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
Task<bool> RemoveAsync(string key); /// <summary>
/// 批量删除缓存
/// </summary>
/// <param name="key">缓存Key集合</param>
/// <returns></returns>
void RemoveAll(IEnumerable<string> keys); /// <summary>
/// 批量删除缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key集合</param>
/// <returns></returns>
Task RemoveAllAsync(IEnumerable<string> keys);

     # 获取缓存

 /// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
T Get<T>(string key) where T : class; /// <summary>
/// 获取缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
Task<T> GetAsync<T>(string key) where T : class; /// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
object Get(string key); /// <summary>
/// 获取缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
Task<object> GetAsync(string key); /// <summary>
/// 获取缓存集合
/// </summary>
/// <param name="keys">缓存Key集合</param>
/// <returns></returns>
IDictionary<string, object> GetAll(IEnumerable<string> keys); /// <summary>
/// 获取缓存集合(异步方式)
/// </summary>
/// <param name="keys">缓存Key集合</param>
/// <returns></returns>
Task<IDictionary<string, object>> GetAllAsync(IEnumerable<string> keys);

  # 修改缓存

 /// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <returns></returns>
bool Replace(string key, object value); /// <summary>
/// 修改缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <returns></returns>
Task<bool> ReplaceAsync(string key, object value); /// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
bool Replace(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte); /// <summary>
/// 修改缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
Task<bool> ReplaceAsync(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte); /// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
bool Replace(string key, object value, TimeSpan expiresIn, bool isSliding = false); /// <summary>
/// 修改缓存(异步方式)
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
Task<bool> ReplaceAsync(string key, object value, TimeSpan expiresIn, bool isSliding = false);

缓存实现类 MemoryCache

  MemoryCache 这个比较简单,有微软比较完善的文档和一些比较好的文章,这个大家都用好久了。

  我们新建一个缓存实现类 MemoryCacheService 实现接口 ICacheService

  public class MemoryCacheService :ICacheService { }

  通过构造器注入 IMemoryCache:

  protected IMemoryCache _cache;

  public MemoryCacheService(IMemoryCache cache)
  {
    _cache = cache;
  }

  

  实现接口的方法,我们的方法都带有 异步 和 同步 两种,我们节约篇章和时间,异步的就不大家写了,大家自己添加一下就好。

  #  验证缓存项是否存在

  在MemoryCache中,我们是通过 TryGetValue 来检测 Key是否存在的

  

 /// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Exists(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
object cached;
return _cache.TryGetValue(key,out cached);
}

  # 添加缓存

  三个添加方法 :

  

 /// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <returns></returns>
public bool Add(string key, object value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_cache.Set(key, value);
return Exists(key);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
public bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_cache.Set(key, value,
new MemoryCacheEntryOptions()
.SetSlidingExpiration(expiresSliding)
.SetAbsoluteExpiration(expiressAbsoulte)
); return Exists(key);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
public bool Add(string key, object value, TimeSpan expiresIn,bool isSliding = false)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (isSliding)
_cache.Set(key, value,
new MemoryCacheEntryOptions()
.SetSlidingExpiration(expiresIn)
);
else
_cache.Set(key, value,
new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(expiresIn)
); return Exists(key);
}

  # 删除缓存

  单个删除 和 批量删除

 /// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Remove(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
_cache.Remove(key); return !Exists(key);
}
/// <summary>
/// 批量删除缓存
/// </summary>
/// <param name="key">缓存Key集合</param>
/// <returns></returns>
public void RemoveAll(IEnumerable<string> keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
} keys.ToList().ForEach(item => _cache.Remove(item));
}

  # 获取缓存

 /// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public T Get<T>(string key) where T : class
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return _cache.Get(key) as T;
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public object Get(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return _cache.Get(key);
}
/// <summary>
/// 获取缓存集合
/// </summary>
/// <param name="keys">缓存Key集合</param>
/// <returns></returns>
public IDictionary<string, object> GetAll(IEnumerable<string> keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
} var dict = new Dictionary<string, object>(); keys.ToList().ForEach(item => dict.Add(item, _cache.Get(item))); return dict;
}

  # 修改缓存

  /// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <returns></returns>
public bool Replace(string key, object value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (Exists(key))
if (!Remove(key)) return false; return Add(key, value); }
/// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
public bool Replace(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (Exists(key))
if (!Remove(key)) return false; return Add(key, value, expiresSliding, expiressAbsoulte);
}
/// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
public bool Replace(string key, object value, TimeSpan expiresIn, bool isSliding = false)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (Exists(key))
if (!Remove(key)) return false; return Add(key, value, expiresIn, isSliding);
}

  最后 释放:

  public void Dispose()
  {
    if (_cache != null)
      _cache.Dispose();
    GC.SuppressFinalize(this);
  }

缓存实现类 Redis

  Redis一般都是运行的Liunx的,我们在 【(第十章)】发布项目到 Linux 上运行 Core 项目 中介绍了,如何在Linux上开发运行Core项目,当然,我相信,很多朋友还是在 Windows 上做开发、测试的,这里给大家一个windows 上的Redis和管理软件,如果没有或者不知道怎么找的朋友可以下载下来,在windows上测试 Redis :连接可能失效  百度网盘 提取码:uglb    百度网盘 提取码:gl0e

  安装第一个后,启动Redis服务就行了,管理软件的界面也很简洁

  

  

  我们先来看我们的 RedisCacheService:

  为了统一管理和切换使用,我们的 RedisCacheService 同样实现接口 ICacheService

  public class RedisCacheService : ICacheService { }

  同样,我们通过构造器注入:

  

  protected IDatabase _cache;

  private ConnectionMultiplexer _connection;

  private readonly string _instance;

  public RedisCacheService(RedisCacheOptions options, int database = 0)
  {
    _connection = ConnectionMultiplexer.Connect(options.Configuration);
    _cache = _connection.GetDatabase(database);
    _instance = options.InstanceName;
  }

  

  说明一下:我们需要添加一个Redis包:Microsoft.Extensions.Caching.Redis,这是官方的,但是不知道是我程序的原因还是什么原因,总是还原失败,命令强行还原包也不行,所以 我用的是移植的包: Microsoft.Extensions.Caching.Redis.Core,如果你们可以,最好还是用官方的包。

  这里我们写了个方法,来组合Key值和实例名,就是Key值转为 实例名+Key   

  public string GetKeyForRedis(string key)
  {
    return _instance + key;
  }

  # 验证缓存项是否存在

 /// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Exists(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return _cache.KeyExists(GetKeyForRedis(key));
}

  # 添加缓存

  注意:我翻阅了很多资料,没有找到Redis支持滑动和绝对过期,但是都是继承的统一接口,所以这里添加方法 滑动过期时没有用的。

 /// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <returns></returns>
public bool Add(string key, object value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return _cache.StringSet(GetKeyForRedis(key), Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)));
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间,Redis中无效)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
public bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return _cache.StringSet(GetKeyForRedis(key), Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)),expiressAbsoulte);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间,Redis中无效)</param>
/// <returns></returns>
public bool Add(string key, object value, TimeSpan expiresIn, bool isSliding = false)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
} return _cache.StringSet(GetKeyForRedis(key), Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), expiresIn);
}

  # 删除缓存

 /// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Remove(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
return _cache.KeyDelete(GetKeyForRedis(key));
}
/// <summary>
/// 批量删除缓存
/// </summary>
/// <param name="key">缓存Key集合</param>
/// <returns></returns>
public void RemoveAll(IEnumerable<string> keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
} keys.ToList().ForEach(item => Remove(item));
}

  # 获取缓存

 /// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public T Get<T>(string key) where T : class
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
} var value = _cache.StringGet(GetKeyForRedis(key)); if (!value.HasValue)
{
return default(T);
} return JsonConvert.DeserializeObject<T>(value);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public object Get(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
} var value = _cache.StringGet(GetKeyForRedis(key)); if(!value.HasValue)
{
return null;
}
/// <summary>
/// 获取缓存集合
/// </summary>
/// <param name="keys">缓存Key集合</param>
/// <returns></returns>
public IDictionary<string, object> GetAll(IEnumerable<string> keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
}
var dict = new Dictionary<string, object>(); keys.ToList().ForEach(item => dict.Add(item, Get(GetKeyForRedis(item)))); return dict;
} return JsonConvert.DeserializeObject(value);
}

  # 修改缓存

 /// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <returns></returns>
public bool Replace(string key, object value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
} if(Exists(key))
if (!Remove(key))
return false; return Add(key, value); }
/// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
public bool Replace(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
} if (Exists(key))
if (!Remove(key))
return false; return Add(key, value, expiresSliding, expiressAbsoulte);
}
/// <summary>
/// 修改缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">新的缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
public bool Replace(string key, object value, TimeSpan expiresIn, bool isSliding = false)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
} if (Exists(key))
if (!Remove(key)) return false; return Add(key, value, expiresIn, isSliding);
}

  同样,释放:

  public void Dispose()
  {
    if (_connection != null)
      _connection.Dispose();
    GC.SuppressFinalize(this);
  }

Startup.cs 和 缓存配置

  我们在 【(第八章)】读取配置文件(二) 读取自定义配置文件 中讲到了如何读取自定义配置文件,同样,我们把缓存的配置 也放在 我们的自定义配置文件siteconfig.json中:

  

  

  我们写一个类,来获取配置几个配置:

  CacheProvider: _isUseRedis        --- 是否使用Redis

          _connectionString   --- Redis连接

          _instanceName     ---Redis实例名称

  在 Startup.cs 的 ConfigureServices(IServiceCollection services) 中:

  首先添加:services.AddMemoryCache();

  判断是否使用Redis,如果不使用 Redis就默认使用 MemoryCache:  

  if (_cacheProvider._isUseRedis)
  {
    //Use Redis
    services.AddSingleton(typeof(ICacheService), new RedisCacheService(new RedisCacheOptions
    {
      Configuration = _cacheProvider._connectionString,
      InstanceName = _cacheProvider._instanceName
    },0));
  }
  else
  {
    //Use MemoryCache
    services.AddSingleton<IMemoryCache>(factory =>
    {
      var cache = new MemoryCache(new MemoryCacheOptions());
      return cache;
      });
    services.AddSingleton<ICacheService, MemoryCacheService>();
  }

  

  OK,完成了,因为也是研究Core不到一个月,所以这里面肯定会有些地方写的不好,希望大家指出来,如果有更高的方案或者方法,也麻烦告知一声,在此表示感谢!

希望跟大家一起学习Asp.net Core

刚开始接触,水平有限,很多东西都是自己的理解和翻阅网上大神的资料,如果有不对的地方和不理解的地方,希望大家指正!

虽然Asp.net Core 现在很火热,但是网上的很多资料都是前篇一律的复制,所以有很多问题我也暂时没有解决,希望大家能共同帮助一下!

原创文章 转载请尊重劳动成果 http://yuangang.cnblogs.com

【无私分享:ASP.NET CORE 项目实战(第十一章)】Asp.net Core 缓存 MemoryCache 和 Redis的更多相关文章

  1. 【无私分享:ASP.NET CORE 项目实战(第九章)】创建区域Areas,添加TagHelper

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp.net Core VS2015中,我们发现还有很多不太简便的地方,比如右击添加视图,转到试图页等功能图不见了,虽然我 ...

  2. 【无私分享:ASP.NET CORE 项目实战(第二章)】添加EF上下文对象,添加接口、实现类以及无处不在的依赖注入(DI)

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 上一章,我们介绍了安装和新建控制器.视图,这一章我们来创建个数据模型,并且添加接口和实现类. 添加EF上下文对象 按照我们以前 ...

  3. [转]【无私分享:ASP.NET CORE 项目实战(第九章)】创建区域Areas,添加TagHelper

    本文转自:http://www.cnblogs.com/zhangxiaolei521/p/5808417.html 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp ...

  4. .NET Core IdentityServer4实战 第三章-使用EntityFramework Core进行持久化配置

    内容:本文带大家使用IdentityServer4进行使用使用EntityFramework Core进行配置和操作数据 作者:zara(张子浩) 欢迎分享,但需在文章鲜明处留下原文地址. 前两章内容 ...

  5. 【无私分享:ASP.NET CORE 项目实战】目录索引

    简介 首先,我们的  [无私分享:从入门到精通ASP.NET MVC]   系列已经接近尾声,希望大家在这个过程中学到了一些思路和方法,而不仅仅是源码. 因为是第一次写博客,我感觉还是比较混乱的,其中 ...

  6. 【无私分享:ASP.NET CORE 项目实战(第十四章)】图形验证码的实现

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 很长时间没有来更新博客了,一是,最近有些忙,二是,Core也是一直在摸索中,其实已经完成了一个框架了,并且正在准备在生产环境中 ...

  7. 【无私分享:ASP.NET CORE 项目实战(第十三章)】Asp.net Core 使用MyCat分布式数据库,实现读写分离

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 MyCat2.0版本很快就发布了,关于MyCat的动态和一些问题,大家可以加一下MyCat的官方QQ群:106088787.我 ...

  8. 【无私分享:ASP.NET CORE 项目实战(第十二章)】添加对SqlServer、MySql、Oracle的支持

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 增加对多数据库的支持,并不是意味着同时对多种数据库操作,当然,后面,我们会尝试同时对多种数据库操作,这可能需要多个上下文,暂且 ...

  9. 【无私分享:ASP.NET CORE 项目实战(第十章)】发布项目到 Linux 上运行 Core 项目

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win7)上用虚拟机建了个 CentOS7 ,来演示下 ...

随机推荐

  1. Struts2入门(六)——国际化

    一.前言 1.1.国际化简介 国际化是指应用程序在运行的时候,根据客户端请求来自的国家地区.语言的不同而显示不同的界面(简单说就是根据你的地区显示相关地区的语言,如果你现在在英国,那么显示的语言就是英 ...

  2. background例子

  3. Android中Activity处理返回结果的实现方式

    大家在网上购物时都有这样一个体验,在确认订单选择收货人以及地址时,会跳转页面到我们存入网站内的所有收货信息(包含收货地址,收货人)的界面供我们选择,一旦我们点击其中某一条信息,则会自动跳转到订单提交界 ...

  4. Jenkins的一个bug-同时build一个项目两次导致失败

    我们有一个job A, A只是配置了一些参数,它会去触发模板job B. 我一开始点击构建A, 马上发现参数配置不对,于是撤消了构建,但是我没有发现B已经被触发,我重新配置参数,然后再次构建A,这个时 ...

  5. Python3中的字符串函数学习总结

    这篇文章主要介绍了Python3中的字符串函数学习总结,本文讲解了格式化类方法.查找 & 替换类方法.拆分 & 组合类方法等内容,需要的朋友可以参考下. Sequence Types ...

  6. 【已解决】Https请求——基础连接已经关闭 发送时发生错误

    本人在做商用项目的推送消息功能时,借助第三方推送服务.这里避免有打广告的嫌疑,就不报名字了.由于是通过调用API接口,所以Post方法是自己写的,但是在开发环境是可以正常推送的,但是一上线就出各种问题 ...

  7. C#与C++的发展历程第四 - C#6的新时代

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  8. Visual Studio Code预览版Ver 0.3.0试用体验

    当你开始阅读这篇文章时,请先不要把Visual Studio Code和.net.Windows联想到一起,因为VS Code是一个跨平台,支持30多种语言的开箱代码编辑器.不管你是.Net.Java ...

  9. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  10. Leetcode 笔记 35 - Valid Soduko

    题目链接:Valid Sudoku | LeetCode OJ Determine if a Sudoku is valid, according to: Sudoku Puzzles - The R ...