1

这是下载链接 https://skydrive.live.com/redir.aspx?cid=c565323a2a3927de&page=self&resid=C565323A2A3927DE!234&parid=C565323A2A3927DE!119&authkey=!Av1VEdfY3hmpl14&Bpub=SDX.SkyDrive&Bsrc=Share

所以我有一个这样的域模型。

public enum ItemType
{
    TypeA,
    TypeB
}    

public class Item
{
    public int ID;
    public ItemType Type;
    public ItemServiceFactory Factory = new ItemServiceFactory();

    public IItemService ItemService { get; set; } 

    public Item(ItemType type )
    {
        ItemService = Factory.GetItemService(type);
        Type = type;
    }

    public ItemValue GetValue()
    {
        return ItemService.GetValue(this);
    }

}

public interface IItemService
{
    ItemValue GetValue(Item item);
}

public class ItemServiceA : IItemService
{
    private readonly WebServiceA _webServiceA = new WebServiceA();

    public ItemValue GetValue(Item item)
    {
        return new ItemValue(_webServiceA.GetValuesA(new List<int> { item.ID }).FirstOrDefault());
    }
}

public class ItemServiceB : IItemService
{
    private readonly WebServiceB _webServiceB = new WebServiceB();

    public ItemValue GetValue(Item item)
    {
        return new ItemValue(_webServiceB.GetValuesB(new List<int> { item.ID }).FirstOrDefault());
    }
}

public class ItemServiceFactory
{
    public IItemService GetItemService(ItemType type)
    {
        switch (type)
        {
            case ItemType.TypeA: return new ItemServiceA();
            case ItemType.TypeB: return new ItemServiceB();

            default: throw new ArgumentOutOfRangeException();
        }
    }
}

public class ItemValue
{
    public int Value;

    public ItemValue(int value)
    {
        Value = value;
    }
}

public class WebServiceB
{
    public IEnumerable<int> GetValuesB(IEnumerable<int> idList)
    {//call web service. dummy here.
        return idList.Select(x => x + 100);
    }
}

public class WebServiceA
{
    public IEnumerable<int> GetValuesA(IEnumerable<int> idList)
    {//call web service. dummy here.
        return idList.Select(x => x - 100);
    }
}



var tooManyItems = new List<Item>
    {
        new Item(ItemType.TypeA),
        new Item(ItemType.TypeA),
        new Item(ItemType.TypeA),
        new Item(ItemType.TypeA),
        new Item(ItemType.TypeA),
        new Item(ItemType.TypeA),
        new Item(ItemType.TypeA),
        new Item(ItemType.TypeA),

        new Item(ItemType.TypeB),
        new Item(ItemType.TypeB),
        new Item(ItemType.TypeB),
        new Item(ItemType.TypeB),
        new Item(ItemType.TypeB),
        new Item(ItemType.TypeB),
        new Item(ItemType.TypeB),
        new Item(ItemType.TypeB)
    };

    Console.WriteLine(tooManyItems.Select(x => x.GetValue().Value));//or whatever.

现在我正在关注 DDD(领域驱动设计),我真的很想保留Item类以了解如何获得它的价值。在当前模型中,初始化 Item 类时,它会根据Itemtype 设置ItemServiceA/ ItemServiceB。现在,当调用 ItemObj.GetValue() 时,Item 类“知道”它必须使用什么服务,然后在这段代码中,知道要调用什么 Web 服务。

注意:虽然WebService支持调用item列表,但在当前模型中,它总是作为一个item list传递。(对于每个Item对象的GetValue())

现在当前代码中的问题是,如果我有一个很长的项目列表并且我想要所有项目的值,并且它们来自某个 Web 服务,那么这是非常低效和缓慢的。

问题:在这个设计中,我想让 Item 知道如何获取它的值,并且希望将多个项目的调用分组为对 WebServiceA 和 WebServiceB 的两个不同调用。有任何想法吗?

谢谢

4

3 回答 3

3

您遇到问题的原因是您错误地分配了班级职责。具体来说,这个Item类有太多的职责——除了它自己的状态之外,它还包含一个工厂和一个服务。您在使用 Web 服务时遇到的问题是分布式计算最常见的缺陷之一,即假设您可以像调用进程内代码一样调用网络绑定代码。要解决这些问题,请执行以下操作:

1) 在模型中明确地调用 Web 服务。您的 Web 服务类已经设计为返回多个值,因此您的抽象(接口)应该具有相同的签名。

2) 将调用服务的职责从Item类中移开。这也将允许您摆脱工厂,在这种情况下我将其称为服务定位器反模式。有时这是可以接受的,但在这种情况下不是这样。

3) 重构 Web 服务抽象,使其可以返回指定项目的多个项目的值,而不仅仅是单个项目类型。这样,您可以一次为多个项目调用它,并让抽象担心要调用哪个实际的 Web 服务。

于 2013-07-22T19:22:45.577 回答
1

好的,由于没有人回答这个问题,我将为此发布我自己的解决方案。我所做的是基于多线程方法。每个 Item::GetValue 调用都会有一个新的工作线程。我创建了一个名为 BatchItemService 的新类,它的工作是监听这些调用并进行自己的调用,在根据其逻辑进行分组之后,当调用完成时,通知等待线程让它运行完成。我必须对此制定严格的超时策略,以确保线程数无论如何都能恢复正常。

这是代码

    private static IEnumerable<KeyValuePair<Item, ItemValue>> GetResultsAsync(IEnumerable<Item> tooManyItems)
{
    var returnResult = new List<KeyValuePair<Item, ItemValue>>();
    var threads = new List<Thread>();

    foreach (var thread in tooManyItems.Select(item => new Thread(() => returnResult.Add(new KeyValuePair<Item, ItemValue>(item, item.GetItemValue())))))
    {
        threads.Add(thread);
        thread.Start();
    }
    foreach (var thread in threads)
        thread.Join();

    return returnResult;
}


public class ItemServiceFactory
{
    private static BatchItemService _batchItemService;

    public IItemService GetItemService(ItemType type)
    {
        return _batchItemService ?? (_batchItemService = new BatchItemService());
    }
}



public class BatchItemService : IItemService
{
    private static readonly List<Item> ItemsToCall = new List<Item>();
    private static readonly List<KeyValuePair<Item, ItemValue>> ItemsCalled = new List<KeyValuePair<Item, ItemValue>>();
    private static readonly object LockAll = new object();
    private bool _defer;



    private readonly System.Timers.Timer _timer = new System.Timers.Timer(50);

    public BatchItemService()
    {
        _timer.Enabled = true;
        _timer.Elapsed += MakeConsolidatedWebServiceCall;
    }

    private void MakeConsolidatedWebServiceCall(object sender, ElapsedEventArgs e)
    {
        if (_defer)
        {
            return;
        }

        _defer = true;

        lock (LockAll)
        {
            if (ItemsToCall.Count != 0)
            {

                var ItemAItemsToCall = new List<Item>();
                var ItemBItemsToCall = new List<Item>();


                ItemAItemsToCall.AddRange(ItemsToCall.Where(x => x.Type == ItemType.ItemA));

                if (ItemAItemsToCall.Any())
                {
                    Console.WriteLine("calling ItemAWebService");
                    var ItemAMeaureResults =
                        (new ItemAWebService()).GetItemAResults(ItemAItemsToCall.Select(x => x.ID));
                    ItemsCalled.AddRange(ItemAMeaureResults.Zip(ItemAItemsToCall,
                                                                  (first, second) =>
                                                                  new KeyValuePair<Item, ItemValue>(second,
                                                                                                           new ItemValue
                                                                                                               (first))));
                }

                ItemBItemsToCall.AddRange(ItemsToCall.Where(x => x.Type == ItemType.ItemB));

                if (ItemBItemsToCall.Any())
                {
                    Console.WriteLine("calling ItemBWebService");
                    var ItemBItemValues = (new ItemBWebService()).GetItemBResults(ItemBItemsToCall.Select(x => x.ID));
                    ItemsCalled.AddRange(ItemBItemValues.Zip(ItemBItemsToCall,
                                                                  (first, second) =>
                                                                  new KeyValuePair<Item, ItemValue>(second,
                                                                                                           new ItemValue
                                                                                                               (first))));

                }

                foreach (var Item in ItemAItemsToCall.Concat(ItemBItemsToCall))
                {
                    ItemsToCall.Remove(Item);
                }
            }


            Monitor.PulseAll(LockAll);
            _defer = false;
        }
    }

    public ItemValue GetItemValue(Item Item)
    {
        ItemValue returnVal;

        lock (LockAll)
        {
            ItemsToCall.Add(Item);
        }

        lock (LockAll)
        {
            Monitor.Wait(LockAll);
            returnVal = ItemsCalled.FirstOrDefault(x => x.Key.ID.Equals(Item.ID)).Value;
            if (returnVal == null)
            {
                throw new ApplicationException(" value cannot be null. sync issue ");
            }
            Console.WriteLine("received result for Item ID:" + returnVal.Value);
            ItemsCalled.Remove(ItemsCalled.FirstOrDefault(x => x.Key.ID.Equals(Item.ID)));
        }

        return returnVal;
    }
}
于 2013-08-06T09:07:38.073 回答
0

我确实理解您要执行的操作:您有一个 Web 服务,它接受项目列表并返回这些项目的值列表。但是您的域以这样一种方式建模,即您的客户端代码一次只能请求一个项目/值。由于我不知道您的应用程序是做什么的,也不知道您为什么要这样构建它,所以我无法对此做出任何判断。

尽管如此,启动一个新线程来处理这个问题比使用线程池要贵一些,而且这并不是真正必要的。我会将您的GetResultsAsync方法更改为如下所示:

private static IEnumerable<KeyValuePair<Item, ItemResult>> GetResultsAsync(IEnumerable<Item> tooManyItems) {
    var returnResult = new List<KeyValuePair<Item, ItemResult>>();
    Parallel.ForEach(tooManyItems, item => returnResult.Add(new KeyValuePair<Item, ItemResult>(item, item.GetItemResult())));
    return returnResult;
}

此实现使用线程池并完成您所追求的。

于 2013-08-07T15:51:36.990 回答