Как динамически обнаружить все файлы XAML во всех модулях в приложении призмы Silverlight - PullRequest
8 голосов
/ 09 сентября 2010

Существует ли простой способ динамического обнаружения всех файлов XAML во всех загруженных в настоящее время модулях (особенно в приложении Silverlight Prism)? Я уверен, что это возможно, но не уверен, с чего начать.

Это должно происходить на клиенте Silverlight: конечно, мы могли бы анализировать проекты на компьютере разработчика, но это снизило бы гибкость и включило бы в поиск неиспользуемые файлы.

В основном мыхочу иметь возможность анализировать все файлы XAML в очень большом проекте Prism (независимо от их загрузки), чтобы идентифицировать все строки локализации.Это позволит нам создать исходную базу данных локализации, которая включает в себя все наши строки привязки к ресурсам, а также создаст поиск файлов XAML, в которых они находятся (для облегчения редактирования для переводчиков).

Зачем это делать ?: Самое плохое для переводчиков - это изменить строку в одном контексте только для того, чтобы обнаружить, что она использовалась в другом месте с немного другим значением. Мы включаем контекстное редактированиепереводы из самого приложения.

Обновление (14 сентября):

Стандартный метод перебора сборок недоступен для Silverlight из-за ограничений безопасности.Это означает, что единственным улучшением решения, приведенного ниже, будет сотрудничество с руководством модуля Prism, если это возможно.Если кто-то хочет предоставить решение для кода для этой последней части этой проблемы, есть точки, которыми можно поделиться с вами!

Продолжение:

Итерирование содержимого файлов XAP в базе модулейПроект кажется очень удобным, чтобы иметь возможность делать это по разным причинам, поэтому добавьте еще 100 повторений, чтобы получить реальный ответ (желательно рабочий пример кода).Приветствия и удачи!

Частичное решение ниже (работает, но не оптимально):

Ниже приведен код, который я придумал, который представляет собой набор методов из по этой ссылкена встроенных ресурсах (согласно предложению Otaku) и моей собственной итерации каталога модулей Prism.

  • Проблема 1 - все модули уже загружены, поэтому в основном приходится загружать их все второй раз, поскольку я не могу понять, как выполнить итерацию всех загруженных в настоящее время модулей Prism.Если кто-то захочет поделиться за это вознаграждением, вы все равно можете помочь сделать его законченным решением!

  • Проблема 2. Очевидно, что в ResourceManager есть ошибка, которая требует от вас получитьпоток известного ресурса, прежде чем он позволит вам перебрать все элементы ресурса (см. примечание в коде ниже).Это означает, что у меня должен быть фиктивный файл ресурсов в каждом модуле.Было бы неплохо узнать, зачем нужен этот первоначальный вызов GetStream (или как его избежать).

    private void ParseAllXamlInAllModules()
    {
        IModuleCatalog mm = this.UnityContainer.Resolve<IModuleCatalog>();
        foreach (var module in mm.Modules)
        {
            string xap = module.Ref;
            WebClient wc = new WebClient();
            wc.OpenReadCompleted += (s, args) =>
            {
                if (args.Error == null)
                {
                    var resourceInfo = new StreamResourceInfo(args.Result, null);
                    var file = new Uri("AppManifest.xaml", UriKind.Relative);
                    var stream = System.Windows.Application.GetResourceStream(resourceInfo, file);
                    XmlReader reader = XmlReader.Create(stream.Stream);
                    var parts = new AssemblyPartCollection();
                    if (reader.Read())
                    {
                        reader.ReadStartElement();
                        if (reader.ReadToNextSibling("Deployment.Parts"))
                        {
                            while (reader.ReadToFollowing("AssemblyPart"))
                            {
                                parts.Add(new AssemblyPart() { Source = reader.GetAttribute("Source") });
                            }
                        }
                    }
                    foreach (var part in parts)
                    {
                        var info = new StreamResourceInfo(args.Result, null);
                        Assembly assy = part.Load(System.Windows.Application.GetResourceStream(info, new Uri(part.Source, UriKind.Relative)).Stream);
                        // Get embedded resource names
                        string[] resources = assy.GetManifestResourceNames();
                        foreach (var resource in resources)
                        {
                            if (!resource.Contains("DummyResource.xaml"))
                            {
                                // to get the actual values - create the table
                                var table = new Dictionary<string, Stream>();
                                // All resources have “.resources” in the name – so remove it
                                var rm = new ResourceManager(resource.Replace(".resources", String.Empty), assy);
                                // Seems like some issue here, but without getting any real stream next statement doesn't work....
                                var dummy = rm.GetStream("DummyResource.xaml");
                                var rs = rm.GetResourceSet(Thread.CurrentThread.CurrentUICulture, false, true);
                                IDictionaryEnumerator enumerator = rs.GetEnumerator();
                                while (enumerator.MoveNext())
                                {
                                    if (enumerator.Key.ToString().EndsWith(".xaml"))
                                    {
                                        table.Add(enumerator.Key.ToString(), enumerator.Value as Stream);
                                    }
                                }
                                foreach (var xaml in table)
                                {
                                    TextReader xamlreader = new StreamReader(xaml.Value);
                                    string content = xamlreader.ReadToEnd();
                                    {
                                        // This is where I do the actual work on the XAML content
                                    }
                                }
                            }
                        }
                    }
                }
            };
            // Do the actual read to trigger the above callback code
            wc.OpenReadAsync(new Uri(xap, UriKind.RelativeOrAbsolute));
        }
    }
    

Ответы [ 2 ]

5 голосов
/ 12 сентября 2010

Используйте GetManifestResourceNames отражения и анализируйте оттуда, чтобы получить только те, которые заканчиваются на .xaml .Вот пример использования GetManifestResourceNames: Перечисление встроенных ресурсов .Хотя в примере показано, как сделать это с помощью отдельного файла .xap, вы можете сделать это с загруженным файлом.

2 голосов
/ 14 сентября 2010

Я видел, как люди жалуются на некоторые довольно грубые ошибки в Prism

Рассмотрение ваших проблем:

Проблема 1 : я не знаком с Prism, но с объектно-ориентированной точки зрения ваш класс Module Manager должен отслеживать, был ли загружен модуль, и если он еще не загружен, позволяет рекурсивно загружать другие Модули, использующие функцию карты в List<Module> или любом другом типе, который Prism использует для абстрактного представления сборок. Короче говоря, пусть ваш Module Module реализует скрытое состояние, которое представляет список загруженных модулей. Ваша функция Map должна затем взять этот список модулей, уже загруженных, в качестве начального значения и вернуть список модулей, которые не были загружены. Затем вы можете либо усвоить логику для общедоступного метода LoadAllModules, либо разрешить кому-либо перебирать общедоступный List<UnloadedModule> where UnloadedModule : Module и позволить им выбирать, что загружать. Я не рекомендовал бы выставлять оба метода одновременно из-за проблем параллелизма, когда к диспетчеру модулей обращаются через несколько потоков.

Проблема 2 : Требуется начальный вызов GetStream, поскольку ResourceManager лениво оценивает ресурсы. Интуитивно понятно, что причина этого в том, что спутниковые сборки могут содержать несколько специфичных для локали модулей, и если все эти модули были загружены в память одновременно, это может исчерпать кучу, и тот факт, что это неуправляемые ресурсы. Вы можете посмотреть код, используя RedGate .NET Reflector, чтобы определить детали. Вы можете вызвать более дешевый метод, чем GetStream. Вы также можете запустить его для загрузки сборки, обманув его, загрузив ресурс, который есть в каждой сборке Silverlight. Попробуйте ResourceManager.GetObject ("TOOLBAR_ICON") или, возможно, ResourceManager.GetStream ("TOOLBAR_ICON") - обратите внимание, что я не пробовал это, и набираю это предложение, так как я собираюсь уйти на день. Мое обоснование для того, чтобы быть последовательно быстрее, чем ваш SomeDummy.Xaml Подход заключается в том, что я считаю, что TOOLBAR_ICON является нулевым ресурсом в каждой сборке. Таким образом, это будет прочитано очень рано в потоке. Faaaaaast. Так что это не просто исключение необходимости использования SomeDummy.Xaml в каждой сборке вашего проекта, которую я предлагаю; Я также рекомендую микрооптимизации.

Если эти приемы сработают, вы сможете значительно повысить производительность.


Дополнительные мысли:

Я думаю, вы можете очистить свой код дальше.

IModuleCatalog mm = this.UnityContainer.Resolve<IModuleCatalog>();
    foreach (var module in mm.Modules)
    {

может быть реорганизован для удаления ссылки на UnityContainer. Кроме того, IModuleCatalog будет создаваться через оболочку вокруг List<Module>, который я упомянул в своем ответе на проблему 1. Другими словами, IModuleCatalog будет динамическим представлением всех загруженных модулей. Я предполагаю, что из этого дизайна можно добиться еще большей производительности, но, по крайней мере, вы больше не зависите от Unity. Это поможет вам лучше реорганизовать ваш код позже для увеличения производительности.

...