【问题标题】:Custom Attribute to cache read only Property value in C#自定义属性以缓存 C# 中的只读属性值
【发布时间】:2021-03-24 00:30:24
【问题描述】:

我正在尝试创建一个能够缓存属性值的自定义属性,该属性的类型是 IWebElement,创建起来有点昂贵,并且可能引发 NoSuchElement 异常。

我已经像这样实现了一些成功:

protected Lazy<IWebElement> lazyWebElement;
protected virtual IWebElement cachedWebElement
{
    get
    {
        if (!lazyWebElement.IsValueCreated)
        {
            try
            {
                lazyWebElement = new Lazy<IWebElement>(() => driver.FindElement(By.Id("someElement")),
                    LazyThreadSafetyMode.PublicationOnly);
            }
            catch (NoSuchElementException)
            {
                throw new NoSuchElementException("someElement is not present in DOM");
            }
        }

        return lazyModal.Value;
    }
}

而我想做的是

[CachedWebElement]
protected virtual IWebElement cachedWebElement => driver.FindElement(By.Id("someElement"));

Attribute 不允许在其构造函数中使用复杂类型,因此我无法将driver.FindElement(By.Id("someElement")) 作为参数传递。

如果我在派生类中覆盖该属性,那么将其缓存起来会很棒,因为现在我正在使用支持字段来保存值,并且整个机制都会丢失。

谢谢。

【问题讨论】:

标签: c# selenium custom-attributes


【解决方案1】:

您的方法可能不会很成功。我的意思是,“只是添加一个属性”。

在编译后和运行时,当您检索对象时(即执行cachedWebElement.Displayed 之类的操作时),将调用get 方法。

在构建项目时,所有属性都会附加到 get set 访问器方法。你可以在这里找到更多信息microsoft get keyword。基本上,{ get; set; } 被编译成函数,你不能真正“搞砸”。它们是编译器的默认实现。 (如果我没看错的话)。

如果您真的想采用这种方法,请查看answer

现在,你的问题

  • 如果您想为元素属性设置一个“缓存”,那很好。您可以创建一个为您保存元素的类,并在每个 FindElement() 之前检查它是否已经存在。
  • 如果您还希望能够“单击”一个元素,那是不可能的。如果您在按钮上“查找元素”,然后页面发生更改,并且您想从缓存中单击()它,那将不会发生。页面变了。抛出异常是有原因的。

我假设您只想“缓存”有关元素的属性/信息,而不是实际与之交互。

然后,我们只需要创建一个“中间人”来处理每个元素的定位,但也会缓存我们找到的元素。如果我们再次询问它们,班级会根据记忆将它们提供给我们,而不是“重新定位”它们。让我们将它们作为扩展方法来做一些漂亮的事情:

public static class ElementLocator
{
    private static readonly Dictionary<By, IWebElement> _CachedElements;

    public static IWebElement FindElementFromCache(this IWebDriver driver, By by)
    {
        if (_CachedElements.ContainsKey(by))
            return _CachedElements.Single(e => e.Key == by).Value;

        return driver.FindElement(by);
    }

    public static ReadOnlyCollection<IWebElement> FindElementsFromCache(this IWebDriver driver, By by)
    {
        if (_CachedElements.ContainsKey(by))
        {
            List<IWebElement> foundCache = _CachedElements.Where(e => e.Key == by)
                                                          .Select(e => e.Value)
                                                          .ToList();

            return new ReadOnlyCollection<IWebElement>(foundCache);
        }

        return driver.FindElements(by);
    }

    static ElementLocator()
    {
        _CachedElements = new Dictionary<By, IWebElement>();
    }
}

然后,你可以简单地做driver.FindElementFromCache(By.Id("someElement"));

但这可能不是完整的实现。因为,在搜索多个元素时,可能添加了一个新元素。一分钟前你得到了 4 个元素,现在添加了一个新的表格行,你有 5 个元素。 ElementLocator 将尝试查看缓存中是否有任何内容,将找到 4 个元素并将它们返回给您,而不搜索第 5 个元素。

IMO,创建缓存机制可能不是解决您问题的最佳方案。它引入了(请原谅我的语言)你将面临的一大堆问题。

如果您能向我们说明问题所在,我们就能找出可能不涉及缓存的问题。我的意思是,不要说“我怎样才能更好地做这个缓存”或“我的缓存有问题”,而是让我们看看为什么你需要一个缓存来开始元素。

祝你好运!

【讨论】:

  • 您好 Thodoris,感谢您的意见。让我们想象一下这个流程:我们单击一个按钮,该按钮将显示一个模态元素,该元素将停留在 DOM 上,直到我们单击该模态内的一个名为“Accept”的按钮,我们可以使用一个 SlowLoadableComponent 类来检查模态是否显示,第一次它会失败,因为我们没有点击按钮让它出现,所以类将 ExecuteLoad 加载模式。在随后的调用中,将找到模态元素,我们可以将其缓存以供其余使用
猜你喜欢
  • 2010-11-14
  • 1970-01-01
  • 2016-09-20
  • 1970-01-01
  • 2020-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-15
相关资源
最近更新 更多