“真实"分布式缓存中的对象引用? [英] "Real" Object References in Distributed Cache?

查看:96
本文介绍了“真实"分布式缓存中的对象引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我个人致力于.net分布式缓存解决方案,但我认为这个问题在所有平台上都很有趣.

I'm personally committed to .net distributed caching solutions, but I think this question is interesting across all platforms.

是否存在一种分布式缓存解决方案(或通用策略),该解决方案可以将两个对象都存储在缓存中,同时保持它们之间引用的完整性?

Is there a distributed caching solution (or generic strategy) that allows to both store objects in the cache while maintaining the integrity of the references between them?

举例说明-假设我有一个引用对象Bar bar的对象Foo foo和一个引用相同Bar bar的对象Foo foo2.如果将foo加载到缓存中,则会同时存储bar的副本.如果我也将foo2加载到缓存中,则同时存储bar的单独副本.如果更改缓存中的foo.bar,则更改不会影响foo2.bar:(

To exemplify - Suppose I have an object Foo foo that references an object Bar bar and also and object Foo foo2 that references that same Bar bar. If I load foo to the cache, a copy of bar is stored along with it. If I also load foo2 to the cache, a separate copy of bar is stored along with that. If I change foo.bar in the cache, the change does not impact foo2.bar :(

是否有一个现有的分布式缓存解决方案,可以使我在保持foo.bar foo2.bar引用的同时将foofoo2bar加载到缓存中?

Is there an existing distributed cache solution that will enable me to load foo, foo2 and bar into the cache while maintaining the foo.bar foo2.bar references?

推荐答案

首要

我不知道任何分布式系统,也不假装构建一个.这篇文章说明了如何使用带有可序列化对象的IObjectReference接口使用.NET和C#来模拟这种行为.

I do not know of any distributed system, and I do not pretend to build one. This post explains how you can simulate this behavior with .NET and C# using the IObjectReference interface with serializable objects.

现在,让我们继续表演

我不知道这样的分布式系统,但是您可以使用 IObjectReference 接口.您的 ISerializable.GetObjectData 将需要调用 SerializationInfo.SetType 指出一个实现IObjectReference的代理类,并且能够(在GetObjectData方法提供的数据的帮助下)获得对应该使用的真实对象的引用.

I do not know of such a distributed system, but you can somewhat easily achive this with .NET using the IObjectReference interface. Your implementation of ISerializable.GetObjectData would need to call SerializationInfo.SetType to point out a proxy class that implements IObjectReference, and would be able (with help from data provided by your GetObjectData method) to get a reference to the real object that should be used.

示例代码:

[Serializable]
internal sealed class SerializationProxy<TOwner, TKey> : ISerializable, IObjectReference {
    private const string KeyName = "Key";
    private const string InstantiatorName = "Instantiator";
    private static readonly Type thisType = typeof(SerializationProxy<TOwner, TKey>);
    private static readonly Type keyType = typeof(TKey);

    private static readonly Type instantiatorType = typeof(Func<TKey, TOwner>);
    private readonly Func<TKey, TOwner> _instantiator;
    private readonly TKey _key;

    private SerializationProxy() {
    }

    private SerializationProxy(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException("info");

        _key = (TKey)info.GetValue(KeyName, keyType);
        _instantiator = (Func<TKey, TOwner>)info.GetValue(InstantiatorName, instantiatorType);
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
        throw new NotSupportedException("This type should never be serialized.");
    }

    object IObjectReference.GetRealObject(StreamingContext context) {
        return _instantiator(_key);
    }

    internal static void PrepareSerialization(SerializationInfo info, TKey key, Func<TKey, TOwner> instantiator) {
        if (info == null) throw new ArgumentNullException("info");
        if (instantiator == null) throw new ArgumentNullException("instantiator");

        info.SetType(thisType);
        info.AddValue(KeyName, key, keyType);
        info.AddValue(InstantiatorName, instantiator, instantiatorType);
    }
}

将从您的GetObjectData方法中使用SerializationProxy.PrepareSerialization(info,myKey,myKey => LoadedInstances.GetById(myKey))调用此代码,并且您的LoadedInstances.GetById应该从Dictionary< TKey,WeakReference>返回实例.或从缓存/数据库中加载(如果尚未加载).

This code would be called with SerializationProxy.PrepareSerialization(info, myKey, myKey => LoadedInstances.GetById(myKey)) from your GetObjectData method, and your LoadedInstances.GetById should return the instance from a Dictionary<TKey, WeakReference> or load it from cache/database if it isnt already loaded.

我已经写了一些示例代码来说明我的意思.

I've wrote some example code to show what I mean.

public static class Program {
    public static void Main() {
        // Create an item and serialize it.
        // Pretend that the bytes are stored in some magical
        // domain where everyone lives happily ever after.
        var item = new Item { Name = "Bleh" };
        var bytes = Serialize(item);

        {
            // Deserialize those bytes back into the cruel world.
            var loadedItem1 = Deserialize<Item>(bytes);
            var loadedItem2 = Deserialize<Item>(bytes);

            // This should work since we've deserialized identical
            // data twice.
            Debug.Assert(loadedItem1.Id == loadedItem2.Id);
            Debug.Assert(loadedItem1.Name == loadedItem2.Name);

            // Notice that both variables refer to the same object.
            Debug.Assert(ReferenceEquals(loadedItem1, loadedItem2));

            loadedItem1.Name = "Bluh";
            Debug.Assert(loadedItem1.Name == loadedItem2.Name);
        }

        {
            // Deserialize those bytes back into the cruel world. (Once again.)
            var loadedItem1 = Deserialize<Item>(bytes);

            // Notice that we got the same item that we messed
            // around with earlier.
            Debug.Assert(loadedItem1.Name == "Bluh");

            // Once again, force the peaceful object to hide its
            // identity, and take on a fake name.
            loadedItem1.Name = "Blargh";

            var loadedItem2 = Deserialize<Item>(bytes);
            Debug.Assert(loadedItem1.Name == loadedItem2.Name);
        }
    }

    #region Serialization helpers
    private static readonly IFormatter _formatter
        = new BinaryFormatter();

    public static byte[] Serialize(ISerializable item) {
        using (var stream = new MemoryStream()) {
            _formatter.Serialize(stream, item);
            return stream.ToArray();
        }
    }

    public static T Deserialize<T>(Byte[] bytes) {
        using (var stream = new MemoryStream(bytes)) {
            return (T)_formatter.Deserialize(stream);
        }
    }
    #endregion
}

// Supercalifragilisticexpialidocious interface.
public interface IDomainObject {
    Guid Id { get; }
}

// Holds all loaded instances using weak references, allowing
// the almighty garbage collector to grab our stuff at any time.
// I have no real data to lend on here, but I _presume_ that this
// wont be to overly evil since we use weak references.
public static class LoadedInstances<T>
    where T : class, IDomainObject {

    private static readonly Dictionary<Guid, WeakReference> _items
        = new Dictionary<Guid, WeakReference>();

    public static void Set(T item) {
        var itemId = item.Id;
        if (_items.ContainsKey(itemId))
            _items.Remove(itemId);

        _items.Add(itemId, new WeakReference(item));
    }

    public static T Get(Guid id) {
        if (_items.ContainsKey(id)) {
            var itemRef = _items[id];
            return (T)itemRef.Target;
        }

        return null;
    }
}

[DebuggerDisplay("{Id} {Name}")]
[Serializable]
public class Item : IDomainObject, ISerializable {
    public Guid Id { get; private set; }
    public String Name { get; set; }

    // This constructor can be avoided if you have a 
    // static Create method that creates and saves new items.
    public Item() {
        Id = Guid.NewGuid();
        LoadedInstances<Item>.Set(this);
    }

    #region ISerializable Members
    public void GetObjectData(SerializationInfo info, StreamingContext context) {
        // We're calling SerializationProxy to call GetById(this.Id)
        // when we should be deserialized. Notice that we have no
        // deserialization constructor. Fxcop will hate us for that.
        SerializationProxy<Item, Guid>.PrepareSerialization(info, Id, GetById);
    }
    #endregion

    public static Item GetById(Guid id) {
        var alreadyLoaded = LoadedInstances<Item>.Get(id);
        if (alreadyLoaded != null)
            return alreadyLoaded;

        // TODO: Load from storage container (database, cache).
        // TODO: The item we load should be passed to LoadedInstances<Item>.Set
        return null;
    }
}

这篇关于“真实"分布式缓存中的对象引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆