1

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

是否有一个分布式缓存解决方案(或通用策略)允许在缓存中存储对象同时保持它们之间引用的完整性?

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

是否有现有的分布式缓存解决方案可以让我在维护引用的同时加载foo,foo2bar进入缓存?foo.bar foo2.bar

4

1 回答 1

3

首先也是最重要的

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

现在,让我们继续表演

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

示例代码:

[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> 返回实例或从缓存/数据库(如果尚未加载)。

编辑:

我写了一些示例代码来说明我的意思。

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;
    }
}
于 2009-03-31T16:05:11.830 回答