[設(shè)計(jì)模式] 靜態(tài)代理居然能解決這種問題,我驚訝了侦副!


前言

23種設(shè)計(jì)模式都會(huì)了嗎秦驯?今天講一下靜態(tài)代理模式的實(shí)戰(zhàn)場(chǎng)景。

代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象亲桥,并由代理對(duì)象控制對(duì)原對(duì)象的引用题篷。通俗的來講代理模式就是我們生活中常見的中介。

舉個(gè)例子來說明:假如說我現(xiàn)在想買一輛二手車法严,雖然我可以自己去找車源葫笼,做質(zhì)量檢測(cè)等一系列的車輛過戶流程路星,但是這確實(shí)太浪費(fèi)我得時(shí)間和精力了。我只是想買一輛車而已為什么我還要額外做這么多事呢呈昔?于是我就通過中介公司來買車韩肝,他們來給我找車源九榔,幫我辦理車輛過戶流程哲泊,我只是負(fù)責(zé)選擇自己喜歡的車,然后付錢就可以了育特。

為什么要用代理模式缰冤?

中介隔離作用:在某些情況下喳魏,一個(gè)客戶類不想或者不能直接引用一個(gè)委托對(duì)象刺彩,而代理類對(duì)象可以在客戶類和委托對(duì)象之間起到中介的作用枝恋,其特征是代理類和委托類實(shí)現(xiàn)相同的接口焚碌。


解決問題

這篇文章借用 FreeSql.Cloud 開源項(xiàng)目的代碼霸妹,講解代理模式的實(shí)際應(yīng)用和收益抑堡,以及需要注意的地方。

FreeSql 是 c#.NET 功能強(qiáng)大的 ORM 框架,定義了 IFreeSql 接口有缆,主要針對(duì)單個(gè) ConnectionString 生產(chǎn) ORM 操作對(duì)象棚壁。

跨多數(shù)據(jù)庫的時(shí)候栈虚,不同的 ConnectionString 需要生成多個(gè) IFreeSql 原始對(duì)象魂务,如果是多租戶場(chǎng)景每個(gè)租戶 ConnectionString 都不相同的情況下,就需要?jiǎng)?chuàng)建 N個(gè) IFreeSql 原始對(duì)象鬓照。

FreeSql.Cloud 正是為了跨多數(shù)據(jù)庫的問題而產(chǎn)生豺裆,它可以解決:

1号显、FreeSqlCloud 實(shí)現(xiàn)多庫版 IFreeSql 接口押蚤,從使用習(xí)慣上保持與單庫版 IFreeSql 一致;

2丐膝、運(yùn)行時(shí)帅矗,F(xiàn)reeSqlCloud 可動(dòng)態(tài)添加或刪除多個(gè) ConnectionString 對(duì)應(yīng)的 IFreeSql;

3累颂、FreeSqlCloud 存儲(chǔ)多租戶 IFreeSql紊馏,最后活躍時(shí)間 > 10分鐘的租戶蒲犬,釋放對(duì)應(yīng) IFreeSql 減少內(nèi)存開銷原叮;

4、FreeSqlCloud 支持隨時(shí) Change 切換到對(duì)應(yīng)的 IFreeSql 進(jìn)行操作擂送;


代理模式實(shí)戰(zhàn)(一)Scoped FreeSqlCloud 多庫版本

IFreeSql 是一個(gè)極為嚴(yán)格嘹吨、簡(jiǎn)單境氢,且功能強(qiáng)大的接口产还,我們一直在嚴(yán)格控制 API 泛濫增長(zhǎng)脐区,泛濫的 API 在后續(xù)改造時(shí)非常痛苦。

正因?yàn)樗暮?jiǎn)單定義炕柔,讓我們有機(jī)會(huì)使用到代理模式實(shí)現(xiàn)新的 IFreeSql 實(shí)現(xiàn)類 FreeSqlCloud匕累。

public class FreeSqlCloud : IFreeSql
{
    IFreeSql CurrentOrm => ...; //請(qǐng)看后面

    public IAdo Ado => CurrentOrm.Ado;
    public IAop Aop => CurrentOrm.Aop;
    public ICodeFirst CodeFirst => CurrentOrm.CodeFirst;
    public IDbFirst DbFirst => CurrentOrm.DbFirst;
    public GlobalFilter GlobalFilter => CurrentOrm.GlobalFilter;

    public void Transaction(Action handler) => CurrentOrm.Transaction(handler);
    public void Transaction(IsolationLevel isolationLevel, Action handler) => CurrentOrm.Transaction(isolationLevel, handler);
    public ISelect<T1> Select<T1>() where T1 : class => CurrentOrm.Select<T1>();
    public ISelect<T1> Select<T1>(object dywhere) where T1 : class => Select<T1>().WhereDynamic(dywhere);
    public IDelete<T1> Delete<T1>() where T1 : class => CurrentOrm.Delete<T1>();
    public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => Delete<T1>().WhereDynamic(dywhere);
    public IUpdate<T1> Update<T1>() where T1 : class => CurrentOrm.Update<T1>();
    public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => Update<T1>().WhereDynamic(dywhere);
    public IInsert<T1> Insert<T1>() where T1 : class => CurrentOrm.Insert<T1>();
    public IInsert<T1> Insert<T1>(T1 source) where T1 : class => Insert<T1>().AppendData(source);
    public IInsert<T1> Insert<T1>(T1[] source) where T1 : class => Insert<T1>().AppendData(source);
    public IInsert<T1> Insert<T1>(List<T1> source) where T1 : class => Insert<T1>().AppendData(source);
    public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => Insert<T1>().AppendData(source);
    public IInsertOrUpdate<T1> InsertOrUpdate<T1>() where T1 : class => CurrentOrm.InsertOrUpdate<T1>();
}

如上代碼衰琐,若 CurrentOrm 返回值是原始 IFreeSql 對(duì)象炼蹦,即會(huì)代理調(diào)用原始數(shù)據(jù)庫 ORM 操作方法掐隐。方法不多,功能卻強(qiáng)大匿刮,代理模式應(yīng)用起來就會(huì)很輕松熟丸。

由于多個(gè) ConnectionString 的原因膝擂,我們需要定義一個(gè)字典保存多個(gè)原始 IFreeSql 對(duì)象:

    readonly Dictionary<string, IFreeSql> _orms = new Dictionary<string, IFreeSql>();

我們已經(jīng)有了 _orms架馋,還缺什么叉寂?总珠?還缺一個(gè)當(dāng)前 string _dbkey局服,有了它我們的 CurrentOrm 方法才知道怎么獲取對(duì)應(yīng)的 IFreeSql:

    string _dbkey;
    IFreeSql CurrentOrm => _orms[_dbkey]; //測(cè)試不糾結(jié)代碼安全

    //切換數(shù)據(jù)庫
    IFreeSql Change(string db)
    {
        _dbkey = db;
        return _orms[_dbkey];
    }
    //添加 IFreeSql
    FreeSqlCloud Add(string db, IFreeSql orm)
    {
        if (_dbkey == null) _dbkey = db;
        _orms.Add(db, orm);
        return this;
    }

至此我們基于 Scoped 生命周期的 FreeSqlCloud 就完成了淫奔,DI 代碼大概如下:

public void ConfigureServices(IServiceCollection services)
{
    var db1 = new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, "data source=db1.db").Build();
    var db2 = new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, "data source=db2.db").Build();
    services.AddScoped(provider =>
    {
        var cloud = new FreeSqlCloud();
        cloud.Add("db1", db1);
        cloud.Add("db2", db2);
        return cloud;
    });
}

代理模式實(shí)戰(zhàn)(二)Singleton FreeSqlCloud 多庫版本

實(shí)戰(zhàn)(一)我們實(shí)現(xiàn)了 Scoped 版本唆迁,可是其實(shí)項(xiàng)目中 Singleton 單例才是高性能的保證,特別是多租戶場(chǎng)景鳞溉,每次 new FreeSqlCloud 不止還要循環(huán) Add 那么多次熟菲,實(shí)屬浪費(fèi)!T黍肌厉萝!

其實(shí)單例并非難事榨崩,只需要將 _dbkey 類型修改成 AsyncLocal母蛛,這個(gè)類型多線程是安全的彩郊,有關(guān)它的原理請(qǐng)看資料:https://www.cnblogs.com/eventhorizon/p/12240767.html

    AsyncLocal<string> _dbkey;
    IFreeSql CurrentOrm => _orms[_dbkey.Value];

    IFreeSql Change(string db)
    {
        _dbkey.Value = db;
        return _orms[_dbkey];
    }
    FreeSqlCloud Add(string db, IFreeSql orm)
    {
        if (_dbkey.Value == null) _dbkey.Value = db;
        _orms.Add(db, orm);
        return this;
    }

至此我們就完成了一個(gè)多線程安全的代理模式實(shí)現(xiàn)秫逝,因此我們只需要在 Ioc 注入之前 Register 好原始 IFreeSql 對(duì)象即可:

public void ConfigureServices(IServiceCollection services)
{
    var db1 = new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, "data source=db1.db").Build();
    var db2 = new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, "data source=db2.db").Build();
    var cloud = new FreeSqlCloud();
    cloud.Add("db1", db1);
    cloud.Add("db2", db2);

    services.AddSingleton(cloud);
}

代理模式實(shí)現(xiàn)(三)Singleton FreeSqlCloud 多庫多租戶版本

如上违帆,我們使用字典存儲(chǔ)多個(gè) IFreeSql 原始對(duì)象,在數(shù)量不多的情況下是可行的的畴。

但是如果我們做的是多租戶系統(tǒng)丧裁,那么數(shù)量很可能達(dá)到幾百含衔,甚至上千個(gè) IFreeSql 對(duì)象抱慌,并且這些租戶不全都是活躍狀態(tài)抑进。

因此我們需要一種釋放機(jī)制,當(dāng)租戶最后活躍時(shí)間 > 10分鐘匿情,釋放 IFreeSql 資源炬称,減少內(nèi)存開銷;

我們可以引用 IdleBus 組件解決該問題据德,IdleBus 空閑對(duì)象管理容器棘利,有效組織對(duì)象重復(fù)利用善玫,自動(dòng)創(chuàng)建密强、銷毀或渤,解決【實(shí)例】過多且長(zhǎng)時(shí)間占用的問題薪鹦。有時(shí)候想做一個(gè)單例對(duì)象重復(fù)使用提升性能,但是定義多了,有的又可能一直空閑著占用資源框仔。

IdleBus 專門解決:又想重復(fù)利用离斩,又想少占資源的場(chǎng)景瘪匿。

此時(shí)我們只需要修改內(nèi)部實(shí)現(xiàn)部分代碼如下:

public class FreeSqlCloud : IFreeSql
{
    IdleBus<IFreeSql> _orms = new IdleBus<string, IFreeSql>();
    AsyncLocal<string> _dbkey;
    IFreeSql CurrentOrm => _orms.Get(_dbkey.Value);

    IFreeSql Change(string db)
    {
        _dbkey.Value = db;
        return this;
    }
    FreeSqlCloud Register(string db, Func<IFreeSql> create) //注意 create 類型是 Func
    {
        if (_dbkey.Value == null) _dbkey.Value = db;
        _orms.Register(db, create);
        return this;
    }
    //...
}

public void ConfigureServices(IServiceCollection services)
{
    var cloud = new FreeSqlCloud();
    cloud.Add("db1", () => new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, "data source=db1.db").Build());
    cloud.Add("db2", () => new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, "data source=db1.db").Build());

    services.AddSingleton(cloud);
}

代理模式實(shí)現(xiàn)(四)跟隨切換數(shù)據(jù)庫的倉儲(chǔ)對(duì)象

1幻梯、靜態(tài)倉儲(chǔ)對(duì)象

FreeSql.Repository 對(duì)象創(chuàng)建時(shí)固定了原始 IFreeSql吧碾,因此無法跟隨 FreeSqlCloud Change 切換數(shù)據(jù)庫莹桅。

注意:是同一個(gè)對(duì)象實(shí)例創(chuàng)建之后左腔,無法跟隨切換捅儒,創(chuàng)建新對(duì)象實(shí)例不受影響巧还。

因?yàn)橐?Repository 創(chuàng)建之前狞悲,先調(diào)用 fsql.Change 切換好數(shù)據(jù)庫。

2丹拯、動(dòng)態(tài)倉儲(chǔ)對(duì)象

但是乖酬。咬像。生宛。仍然有一種特殊需求陷舅,Repository 在創(chuàng)建之后莱睁,仍然能跟隨 fsql.Change 切換數(shù)據(jù)庫仰剿。

實(shí)戰(zhàn)中 Scoped 生命同期可能有多個(gè) Repository 對(duì)象,因此切換 cloud 即改變所有 Repository 對(duì)象狀態(tài)琳彩,才算方便汁针。

var repo1 = cloud.GetCloudRepository<User>();
var repo2 = cloud.GetCloudRepository<UserGroup>();
cloud.Change("db2");
Console.WriteLine(repo1.Orm.Ado.ConnectionString); //repo -> db2
Console.WriteLine(repo2.Orm.Ado.ConnectionString); //repo -> db2
cloud.Change("db1");
Console.WriteLine(repo1.Orm.Ado.ConnectionString); //repo -> db1
Console.WriteLine(repo2.Orm.Ado.ConnectionString); //repo -> db1

我們?nèi)匀皇褂昧舜砟J绞┪蓿琁BaseRepository 接口定義也足夠簡(jiǎn)單:

提示:關(guān)鍵看 CurrentRepo 的獲取

class RepositoryCloud<TEntity> : IBaseRepository<TEntity> where TEntity : class
{
    readonly FreeSqlCloud _cloud;
    public RepositoryCloud(FreeSqlCloud cloud)
    {
        _cloud = cloud;
    }

    public IBaseRepository<TEntity> CurrentRepo => ...; //請(qǐng)看后面
    public IUnitOfWork UnitOfWork
    {
        get => CurrentRepo.UnitOfWork;
        set => CurrentRepo.UnitOfWork = value;
    }

    public IFreeSql Orm => CurrentRepo.Orm;
    public Type EntityType => CurrentRepo.EntityType;
    public IDataFilter<TEntity> DataFilter => CurrentRepo.DataFilter;
    public ISelect<TEntity> Select => CurrentRepo.Select;
    public IUpdate<TEntity> UpdateDiy => CurrentRepo.UpdateDiy;
    public ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => CurrentRepo.Where(exp);
    public ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => CurrentRepo.WhereIf(condition, exp);

    public void Attach(TEntity entity) => CurrentRepo.Attach(entity);
    public void Attach(IEnumerable<TEntity> entity) => CurrentRepo.Attach(entity);
    public IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data) => CurrentRepo.AttachOnlyPrimary(data);
    public Dictionary<string, object[]> CompareState(TEntity newdata) => CurrentRepo.CompareState(newdata);
    public void FlushState() => CurrentRepo.FlushState();

    public void BeginEdit(List<TEntity> data) => CurrentRepo.BeginEdit(data);
    public int EndEdit(List<TEntity> data = null) => CurrentRepo.EndEdit(data);

    public TEntity Insert(TEntity entity) => CurrentRepo.Insert(entity);
    public List<TEntity> Insert(IEnumerable<TEntity> entitys) => CurrentRepo.Insert(entitys);
    public TEntity InsertOrUpdate(TEntity entity) => CurrentRepo.InsertOrUpdate(entity);
    public void SaveMany(TEntity entity, string propertyName) => CurrentRepo.SaveMany(entity, propertyName);

    public int Update(TEntity entity) => CurrentRepo.Update(entity);
    public int Update(IEnumerable<TEntity> entitys) => CurrentRepo.Update(entitys);

    public int Delete(TEntity entity) => CurrentRepo.Delete(entity);
    public int Delete(IEnumerable<TEntity> entitys) => CurrentRepo.Delete(entitys);
    public int Delete(Expression<Func<TEntity, bool>> predicate) => CurrentRepo.Delete(predicate);
    public List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate) => CurrentRepo.DeleteCascadeByDatabase(predicate);
}

如上代碼關(guān)鍵實(shí)現(xiàn)部分 CurrentRepo,我們定義了字典存儲(chǔ)多個(gè) IBaseRepository<TEntity> 原始倉儲(chǔ)對(duì)象:

因?yàn)橐粋€(gè) CloudRepository 對(duì)象會(huì)創(chuàng)建 1-N 個(gè) IBaseRepository 原始對(duì)象幢哨,在不使用 cloud.Change(..) 方法的時(shí)候只會(huì)創(chuàng)建 1 個(gè)捞镰,最多創(chuàng)建 cloud.Registers 數(shù)量毙替,真實(shí)場(chǎng)景中不會(huì)有人在同一個(gè)業(yè)務(wù)把所有 db 都切換個(gè)遍厂画。

    readonly Dictionary<string, IBaseRepository<TEntity>> _repos = new Dictionary<string, IBaseRepository<TEntity>>();
    protected void ForEachRepos(Action<IBaseRepository<TEntity>> action)
    {
        foreach (var repo in _repos.Values) action(repo);
    }
    public void Dispose()
    {
        ForEachRepos(repo => repo.Dispose());
        _repos.Clear();
    }
    
    protected IBaseRepository<TEntity> CurrentRepo
    {
        get
        {
            var dbkey = _cloud._dbkey.Value;
            if (_repos.TryGetValue(dbkey, out var repo) == false)
            {
                _repos.Add(dbkey, repo = _cloud.Use(dbkey).GetRepository<TEntity>());
                if (_dbContextOptions == null) _dbContextOptions = repo.DbContextOptions;
                else
                {
                    repo.DbContextOptions = _dbContextOptions;
                    if (_asTypeEntityType != null) repo.AsType(_asTypeEntityType);
                    if (_asTableRule != null) repo.AsTable(_asTableRule);
                }
            }
            return repo;
        }
    }

    Type _dbContextOptions;
    public DbContextOptions DbContextOptions
    {
        get => CurrentRepo.DbContextOptions;
        set => ForEachRepos(repo => repo.DbContextOptions = value);
    }
    Type _asTypeEntityType;
    public void AsType(Type entityType)
    {
        _asTypeEntityType = entityType;
        ForEachRepos(repo => repo.AsType(entityType));
    }
    Func<string, string> _asTableRule;
    public void AsTable(Func<string, string> rule)
    {
        _asTableRule = rule;
        ForEachRepos(repo => repo.AsTable(rule));
    }

由于 DbContextOptions屎慢、AsType忽洛、AsTable 比較特殊欲虚,需要將多個(gè)原始倉儲(chǔ)對(duì)象傳播設(shè)置苍在,代碼如上荠商。

最終還要為 CloudRepository 創(chuàng)建擴(kuò)展方法:

public static IBaseRepository<TEntity> GetCloudRepository<TEntity>(this FreeSqlCloud cloud)
    where TEntity : class
{
    return new RepositoryCloud<TEntity>(cloud);
}

結(jié)語

Repository 是一種非常方便做設(shè)計(jì)的模式,F(xiàn)reeSql 還有很多一些其他設(shè)計(jì)模式的應(yīng)用莱没,如果有興趣以后找機(jī)會(huì)再寫文章初肉。


作者是什么人?

作者是一個(gè)入行 18年的老批饰躲,他目前寫的.net 開源項(xiàng)目有:

開源項(xiàng)目 描述 開源地址 開源協(xié)議
FreeIM 聊天系統(tǒng)架構(gòu) https://github.com/2881099/FreeIM MIT
FreeRedis Redis SDK https://github.com/2881099/FreeRedis MIT
csredis https://github.com/2881099/csredis MIT
FightLandlord 斗DI主網(wǎng)絡(luò)版 https://github.com/2881099/FightLandlord 學(xué)習(xí)用途
FreeScheduler 定時(shí)任務(wù) https://github.com/2881099/FreeScheduler MIT
IdleBus 空閑容器 https://github.com/2881099/IdleBus MIT
FreeSql ORM https://github.com/dotnetcore/FreeSql MIT
FreeSql.Cloud 分布式tcc/saga https://github.com/2881099/FreeSql.Cloud MIT
FreeSql.AdminLTE 低代碼后臺(tái)生成 https://github.com/2881099/FreeSql.AdminLTE MIT
FreeSql.DynamicProxy 動(dòng)態(tài)代理 https://github.com/2881099/FreeSql.DynamicProxy 學(xué)習(xí)用途

需要的請(qǐng)拿走牙咏,這些都是最近幾年的開源作品臼隔,以前更早寫的就不發(fā)了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妄壶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丁寄,更是在濱河造成了極大的恐慌氨淌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伊磺,死亡現(xiàn)場(chǎng)離奇詭異盛正,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屑埋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門豪筝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人摘能,你說我怎么就攤上這事续崖。” “怎么了徊哑?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵袜刷,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我莺丑,道長(zhǎng)著蟹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任梢莽,我火速辦了婚禮萧豆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昏名。我一直安慰自己涮雷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布轻局。 她就那樣靜靜地躺著洪鸭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仑扑。 梳的紋絲不亂的頭發(fā)上览爵,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音镇饮,去河邊找鬼蜓竹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俱济。 我是一名探鬼主播嘶是,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蛛碌!你這毒婦竟也來了聂喇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤左医,失蹤者是張志新(化名)和其女友劉穎授帕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浮梢,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跛十,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秕硝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芥映。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖远豺,靈堂內(nèi)的尸體忽然破棺而出奈偏,到底是詐尸還是另有隱情,我是刑警寧澤躯护,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布惊来,位于F島的核電站,受9級(jí)特大地震影響棺滞,放射性物質(zhì)發(fā)生泄漏裁蚁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一继准、第九天 我趴在偏房一處隱蔽的房頂上張望枉证。 院中可真熱鬧,春花似錦移必、人聲如沸室谚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秒赤。三九已至,卻和暖如春憎瘸,著一層夾襖步出監(jiān)牢的瞬間倒脓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工含思, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓含潘,卻偏偏與公主長(zhǎng)得像饲做,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子遏弱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容