zoukankan      html  css  js  c++  java
  • Aoite 系列(03)

    Aoite 是一个适于任何 .Net Framework 4.0+ 项目的快速开发整体解决方案。Aoite.Data 适用于市面上大多数的数据库提供程序,通过统一封装,可以在日常开发中简单便捷的操作数据库。

    赶紧加入 Aoite GitHub 的大家庭吧!!

    插几句话:开源对我来讲,是一种分享。没有人可以从中获取金钱上的利益。每一套框架都有不足和亮点所在。Aoite 目的是让园友多一种可尝试的选择。对我来说的根本目的在于让 Aoite 真正的成为一个快速开发整体解决方案

    1. 快速入门

    Redis 在 .NET 上有非常多成熟的框架。Aoite.Redis 仅仅目前只是实现其中的一员。实现 Aoite.Redis 的根本目的是为了迎合 Aoite.CommandModel 模块,并且减少对外部框架的依赖。

    using(var client = new RedisClient(6379, null /* password*/))
    {
        client.FlushAll();
        client.Set("Int32Value", 1);
        client.Set("StringValue", "a");
        client.Set("DoubleValue", 1.0);
        client.Set("DecimalValue", 1.0m);
        client.Set("AnonymousValue", new BinaryValue(new { A = "a" }));
    
        Console.WriteLine((Int32)client.Get("Int32Value"));
        Console.WriteLine((String)client.Get("StringValue"));
        Console.WriteLine((Double)client.Get("DoubleValue"));
        Console.WriteLine((Decimal)client.Get("DecimalValue"));
        Console.WriteLine(client.Get("AnonymousValue").ToModel());
    }
    

    1.1 二进制值 BinaryValue

    System.BinaryValue 是一个非常有趣的类。它提供了从 decimal、Guid、string、DateTime、DateTimeOffset、TimeSpan、bool、ulong、char、uint、double、ushort、short、float、int 和 long 与 byte[] 之间的隐式转换。

    比如你可以这么写:

    BinaryValue value1 = 5;
    BinaryValue value2 = "abc";
    
    int x = value1;
    string y = value2;
    

    当然了,对于复杂的类型,就必须通过构造函数来创建。

     BinaryValue value = new BinaryValue(new { Username = "username", Passowrd = "passowrd" });
     Console.WriteLine(value.ToModel());
    

    ToModel 还提供了一个泛型,可以执行返回值的数据类型。

    不过,当遇见一个你无法预期判断出具体类型时,你可以这么做:

    static void Print<T>(T obj)
    {
        BinaryValue value = BinaryValue.Create(obj);
        Console.WriteLine(value.Parse(typeof(T)));
    }
    

    然后你就可以像这样调用:

    Print("a");
    Print(1);
    Print(new { Username = "username", Passowrd = "passowrd" });
    

    相信看到这里的你,应该就明白 System.BinaryValue 在 Redis 中可发挥的重大作用了。

    1.2 事务

    Redis 的事务不同于关系型数据库的事务。在事务范围内的任何命令,都只会返回一个内容“QUENED”,表示此命令已成功加入命令列表(但不代表执行一定会成功)。所以在 Redis 上的事务,我们要一种稍微奇怪的方式进行。

    你看见的代码,可能是这样的:

    using(var client = new RedisClient(6379, null /* password*/))
    {
        using(var tran = client.BeginTransaction())
        {
            tran.On(tran.Set("key1", "value1"), r =>
            {
                Console.WriteLine("设置 Key1 为 value1 的结果是 {0}", r);
            });
            tran.On(tran.IncrBy("key2"), r =>
            {
                Console.WriteLine(r);
            });
            tran.Commit();
        }
        Console.WriteLine((string)client.Get("key1"));
        Console.WriteLine((string)client.Get("key2"));//- 根据 Redis 的协议,返回应该是 String 类型,而不是 Int64
    }
    

    1.3 Hash 的改进

    如果有一个对象,你想存储在 Redis 中时,是以字段进行存储的,那就需要用到 Redis 的 HASH。

    class MyModel
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
    

    以下代码将 MyModel 存储到 Hash 中、从 Hash 获取所有域和获取部分域:

    using(var client = new RedisClient(6379, null /* password*/))
    {
        client.FlushAll();
        string key = "model001";
        client.HMSet(key, new MyModel() { Username = "admin", Password = "123456" });
        var model = client.HGetAll<MyModel>(key);
        Console.WriteLine("Model -> {0}	{1}", model.Username, model.Password);
    
        Console.WriteLine("Username : {0}", client.HGet(key, "Username"));
        Console.WriteLine("Password : {0}", client.HGet(key, "Password"));
    }
    

    上面的代码输出:

    Model -> admin  123456
    Username : admin
    Password : 123456
    

    1.4 Scan Redis 的扫描

    Redis 中有 SCAN、HSCAN、SSCAN 和 ZSCAN 四个扫描的命令。Aoite.Redis 针对扫描的命令进行封装,返回一个 IEnumerable<T> 类型。扫描的过程并不是一次性全部加载。而是充分利用 yield 进行懒加载,第一次扫描至多返回 10 条数据(可以干涉需要返回的数量),再填充到 Queue 先进先出的队列中。等到队列中没有数据时,再重新扫描至多 10 条数据。直至扫描的队列为空并且下一批扫描的数据,整个扫描结束。

    Scan:返回类型 IEnumerable<string>,以下代码输出内容: 11(1,10,11,12...18,19)。

    static void Code7()
    {
        using(var client = new RedisClient(6379, null /* password*/))
        {
            client.FlushAll();
            for(int i = 0; i < 20; i++)
            {
                client.Set("key" + i, "Value" + i);
            }
            Console.WriteLine(client.Scan(pattern: "key1*").Count());
        }
    }
    

    HScan:返回类型 IEnumerable<RedisFieldItem>

    using(var client = new RedisClient(6379, null /* password*/))
    {
        client.FlushAll();
        for(int i = 0; i < 20; i++)
        {
            client.HSet("Key", "Field" + i, "Value" + i);
        }
        Console.WriteLine("[Field]	|	[Value]");
        foreach(var item in client.HScan("Key", pattern: "Field1*"))
        {
            Console.WriteLine("{0}	|	{1}",item.Field, item.Value);
        }
    }
    

    以上代码输出:

    [Field] |       [Value]
    Field1  |       Value1
    Field10 |       Value10
    Field...|       Value...
    Field18 |       Value18
    Field19 |       Value19
    

    SScan:返回类型 IEnumerable<BinaryValue>,以下代码输出内容 5

    using(var client = new RedisClient(6379, null /* password*/))
    {
        client.FlushAll();
        for(int i = 0; i < 20; i++)
        {
            client.SAdd("Key", "Member" + i % 5);
        }
        Console.WriteLine(client.SScan("Key").Count());
    }
    

    ZAdd:返回类型 IEnumerable<RedisScoreItem>,代码可以参考 HScan

    1.5 哪些命令还没有被实现?

    String 未实现命令

    Key 未实现命令

    Pub/Sub 未实现命令

    • 所有命令等找到合适的方式再去实现。

    Connection

    Server

    隐式实现的命令

    • Transaction,事务性命令通过 IRedisClient.BeginTransaction 来隐士完成。
    • Connection
      • AUTH:初始化 RedisClient 就应该提供 Redis 的密码,来简化整体的流程。

    3. 序列化

    Aoite.Serialization 里有包含两个部分内容。

    • 继承 System.Serializer 的序列化器。比如已实现好的:System.Serializer.BinarySystem.Serializer.JsonSystem.Serializer.Xml,当然还有本节的主角 `System.Serializer.Quickly'。
    • 一套二进制的序列化组件 Aoite.Serialization.ObjectWriterObjectReader。这是一套针对字段级别的序列化,支持的类型非常多,Aoite.Tests 里有一个非常复杂的测试对象,想要了解它支持的类型,可以通过那个单元测试进行了解。

    3.1 快速入门

    我们定义一个稍微有一丝丝复杂的对象:

    class Dict2 : Dictionary<string, object>
    {
        public string MyProperty { get; set; }
    }
    

    然后我们可以这么序列化和反序列化:

    Dict2 dict = new Dict2() { MyProperty = "Hello" };
    dict.Add("1", new { A = "a" });
    dict.Add("2", "haha");
    dict.Add("3", 3);
    dict.Add("4", new[] { 1, 2, 3, 4 });
    var bytes = Serializer.Quickly.FastWriteBytes(dict);
    Console.WriteLine("bytes length {0}", bytes.Length);
    
    var dict2 = Serializer.Quickly.FastReadBytes(bytes) as Dict2;
    
    foreach(var item in dict2)
    {
        Console.WriteLine("{0}		{1}", item.Key, item.Value);
        if(item.Value is Array)
        {
            foreach(var value in item.Value as Array)
            {
                Console.Write(value);
                Console.Write(",");     
            }
        }
    }
    

    3.2 序列化特殊对象

    Aoite.Redis.RedisSessionState 这个特殊类,有一个数据类型为 SessionStateItemCollection 的特殊属性,这个通过 Quickly 的序列化是没有问题的,但是在使用时,便会抛出内存错误,具体什么原因我是没有深入研究。所以,针对无法序列化的对象,特性 SerializableUsage 便排上用场。

    class RedisSessionState
    {
        //...
        [SerializableUsage(typeof(Serializable))]
        public SessionStateItemCollection Items { get; set; }
    	//...
    
        class Serializable : Aoite.Serialization.ICustomSerializable
        {
            public object Deserialize(ObjectReader reader)
            {
                SessionStateItemCollection items = new SessionStateItemCollection();
                var count = reader.ReadInt32();
                for(int i = 0; i < count; i++)
                {
                    var name = (string)reader.Deserialize();
                    var value = reader.Deserialize();
                    items[name] = value;
                }
                return items;
            }
    
            public void Serialize(ObjectWriter writer, object value)
            {
                var items = value as SessionStateItemCollection;
                writer.InnerWrite(items.Count);
                foreach(string name in items.Keys)
                {
                    writer.Serialize(name);
                    writer.Serialize(items[name]);
                }
            }
        }
    
    }
    

    SerializableUsage 特性指定一个类型,这样序列化器在处理过程中,将会初始化这个类型的实例,并调用 SerializeDeserialize 方法。

    这样,理论上任何对象都可以序列化为文本。当然了,如果你想序列化 System.Windows.Forms.Form(这个还是有办法的) 或者 System.Web.Page 这就比较难了……

    4. 结束

    关于 Aoite.Ioc 和 Aoite.Serialization 的简单介绍,就到此结束了,如果你喜欢这个框架,不妨点个推荐吧!如果你非常喜欢这个框架,那请顺便到Aoite GitHub Star 一下 :)

  • 相关阅读:
    Binary Search Tree Iterator 解答
    Invert Binary Tree 解答
    Min Stack 解答
    Trapping Raining Water 解答
    Candy 解答
    Jump Game II 解答
    Implement Hash Map Using Primitive Types
    Gas Station 解答
    Bucket Sort
    HashMap 专题
  • 原文地址:https://www.cnblogs.com/sofire/p/aoite_redis.html
Copyright © 2011-2022 走看看