Постараюсь объяснить проблему, предельно упрощая ситуацию: одна (внешняя) контора разработала нам библиотеку (сборку) для выполнения некоторой охренительно важной для нас функции (назовем сборку asm.dll, версия 1). Но стандарты в этой области поменялись, и контора выпустила новую версию сборки для выполнения той же самой функции (назовем ее asm.dll, версия 2). Предполагалось, что в "Час X" все разработчики-пользователи библиотеки пересоберут свой софт с новой версией сборки и все чудесным образом заработает по новым стандартам.
Для простоты будем полагать, что внутри сборок что-то вроде такого:
namespace MySpace
{
public class MyObject
{
// тут конструкторы всякиеpublic string Transform(string s)
{
// ....
}
}
}
Но, как всегда, жизнь внесла свои коррективы. Оказалось, что вместо "Часа Х" будет "Год Х", в течение которого оба подхода должны использоваться совместно. Ну то есть в зависимости от некоторых условий вызывать Transform или из одной, или из другой сборки. То есть прилинковать к своей проге их обе (а скоро будет и третья, а там и четвертая не за горами).
Ну то есть я хочу сделать что-то вроде
MySpace.MyObject t1 = new MySpace.MyObject(); // из первой сборки
MySpace.MyObject t2 = new MySpace.MyObject(); // из второй сборкиstring s=...
if (SomeConditionOnS(s))
{
s = t1.Transform(s);
}
else
{
s = t2.Transform(s);
}
Понятно, что в таком виде это работать не будет. А как правильно решить данную задачу?
Здравствуйте, Аноним, Вы писали: А>Ну то есть я хочу сделать что-то вроде А>
А>MySpace.MyObject t1 = new MySpace.MyObject(); // из первой сборки
А>MySpace.MyObject t2 = new MySpace.MyObject(); // из второй сборки
А>string s=...
А>if (SomeConditionOnS(s))
А>{
А> s = t1.Transform(s);
А>}
А>else
А>{
А> s = t2.Transform(s);
А>}
А>
А>Понятно, что в таком виде это работать не будет. А как правильно решить данную задачу?
Здравствуйте, hardcase, Вы писали:
А>>Понятно, что в таком виде это работать не будет. А как правильно решить данную задачу?
H>extern alias
Спасибо за ответ. А то я уж было подумал, что придется мутить с динамической загрузкой сборок. То есть хоть какой-то вариант у меня есть.
Но на самом деле дела обстоят значительно хуже. Каждая из сборок содержит несколько десятков классов и порядка сотни методов и, таким образом, весь мой код будет забит if'ами. Хотелось бы поступить проще: инстанцировать в некоторую переменную объект класса либо из одной, либо из другой сборки, типа такого:
SomeType t = ( boolCondition ? new Assembly1::MySpace.MyObject() : new Assembly2::MySpace.MyObject());
А дальше уже совершать все опирации с этим единственным объектом.
Можно ли каким-нибудь хитрым способом определить тип SomeType, чтоб переменной этого типа можно было присваивать объекты MyObject из обеих сборок и вызывать их методы?
PS. Краем уха слышал про "утиную" типизацию в C#. Мне кажется, это именно та ситуация, где такая типизация необходима. Т.е. хотелось бы определить SomeType как класс, реализующий заданный набор методов и чтоб переменной этого класса можно было присвоить объект любого класса, в котором эти методы реализованы. Что-нибудь подобное в C# есть?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, hardcase, Вы писали:
А>>>Понятно, что в таком виде это работать не будет. А как правильно решить данную задачу?
H>>extern alias
А>Спасибо за ответ. А то я уж было подумал, что придется мутить с динамической загрузкой сборок. То есть хоть какой-то вариант у меня есть.
А>Но на самом деле дела обстоят значительно хуже. Каждая из сборок содержит несколько десятков классов и порядка сотни методов и, таким образом, весь мой код будет забит if'ами. Хотелось бы поступить проще: инстанцировать в некоторую переменную объект класса либо из одной, либо из другой сборки, типа такого:
А>
А>SomeType t = ( boolCondition ? new Assembly1::MySpace.MyObject() : new Assembly2::MySpace.MyObject());
А>
А>А дальше уже совершать все опирации с этим единственным объектом.
А>Можно ли каким-нибудь хитрым способом определить тип SomeType, чтоб переменной этого типа можно было присваивать объекты MyObject из обеих сборок и вызывать их методы?
А>PS. Краем уха слышал про "утиную" типизацию в C#. Мне кажется, это именно та ситуация, где такая типизация необходима. Т.е. хотелось бы определить SomeType как класс, реализующий заданный набор методов и чтоб переменной этого класса можно было присвоить объект любого класса, в котором эти методы реализованы. Что-нибудь подобное в C# есть?
public T GenerateProxy<T>()
{
return (T)Activator.CreateInstance(typeof(T));
}
Кажется, рано обрадовался. При построении проекта получаю следующее сообщение:
Primary reference "myasm, Version=2.0.199.13, Culture=neutral, PublicKeyToken=8283b45d91d69b7a" Resolved file path is "libs\new\myasm.dll".
Reference found at search path location "{HintPathFromItem}".
There was a conflict between "myasm, Version=2.0.199.13, Culture=neutral, PublicKeyToken=8283b45d91d69b7a" and
"myasm, Version=2.0.160.14, Culture=neutral, PublicKeyToken=8283b45d91d69b7a".
C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets : warning MSB3243: No way to resolve conflict between "myasm, Version=2.0.199.13, Culture=neutral, PublicKeyToken=8283b45d91d69b7a"
and "myasm, Version=2.0.160.14, Culture=neutral, PublicKeyToken=8283b45d91d69b7a".
Choosing "myasm, Version=2.0.199.13, Culture=neutral, PublicKeyToken=8283b45d91d69b7a" arbitrarily.
Consider app.config remapping of assembly "myasm, Culture=neutral, PublicKeyToken=8283b45d91d69b7a" from Version "2.0.160.14" [libs\old\myasmold.dll] to Version "2.0.199.13" [libs\new\myasm.dll] to solve conflict and get rid of warning.
C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets : warning MSB3247: Found conflicts between different versions of the same dependent assembly.
Done executing task "ResolveAssemblyReference".
Правильно ли я понимаю, что студия выкинула старую сборку, заменив все ссылки на нее ссылками на новую сборку? Если да, то как все-таки работать с двумя версиями сборки в одном проекте?
Здравствуйте, Аноним, Вы писали:
А> назовем сборку asm.dll, версия 1 А> назовем ее asm.dll, версия 2
Правильно ли я понимаю, что типы и члены в сборках одинаковые? Отличается лишь внутренняя реализация и номер версии?
Тогда достаточно указать в манифесте, что приложение должно использовать новую версию сборки. Без перекомпиляции. Не?
Re[2]: Подскажите, как выбраться из такой ловушки
От:
Аноним
Дата:
16.08.12 13:35
Оценка:
Здравствуйте, koodeer, Вы писали:
K>Правильно ли я понимаю, что типы и члены в сборках одинаковые? Отличается лишь внутренняя реализация и номер версии? K>Тогда достаточно указать в манифесте, что приложение должно использовать новую версию сборки. Без перекомпиляции. Не?
Все гораздо хуже. В новой сборке, естественно, членов и типов больше, но это в данном случае неважно. На самом деле сборки — это интерфейс к специализированному удаленному серверу. Версии сервера поменялась и поменялись протоколы доступа, поэтому обратной совместимости нет. Новая сборка не может работать со старым сервером — в коде выскакивает исключение. А мне нужно работать с обоими серверами.
Здравствуйте, Аноним, Вы писали:
K>>Правильно ли я понимаю, что типы и члены в сборках одинаковые? Отличается лишь внутренняя реализация и номер версии? K>>Тогда достаточно указать в манифесте, что приложение должно использовать новую версию сборки. Без перекомпиляции. Не?
А>Все гораздо хуже. В новой сборке, естественно, членов и типов больше, но это в данном случае неважно. На самом деле сборки — это интерфейс к специализированному удаленному серверу. Версии сервера поменялась и поменялись протоколы доступа, поэтому обратной совместимости нет. Новая сборка не может работать со старым сервером — в коде выскакивает исключение. А мне нужно работать с обоими серверами.
Не понял.
1) Если код работы (даже не интерфейс) у в1 и в2 одинаковый — просто пихаете их в \subdir1\asm.dll и \subdir2\asm.dll, а в .exe.config ставите probingpath на нужную поддиректорию.
2) Если код отличается или одновременно надо работать с обоими — делайте свой движок и работайте уже с ним. Сам движок пускать в другом домене, процессе, сервисе — как нравится.
3) Ну и external alias уже говорили.
Re[4]: Подскажите, как выбраться из такой ловушки
От:
Аноним
Дата:
17.08.12 09:22
Оценка:
Здравствуйте, Nikolay_P_I, Вы писали:
N_P>1) Если код работы (даже не интерфейс) у в1 и в2 одинаковый — просто пихаете их в \subdir1\asm.dll и \subdir2\asm.dll, а в .exe.config ставите probingpath на нужную поддиректорию.
Поясните, что такое "код работы"?
Есть две dll-ки (два файла), которые являются различными версиями одной и той же сборки:
Унутре у них классы, у классов — методы, у более новой, понятно, набор классов шире и методов больше. Общие для двух сборок методы идентичны по сигнатуре (т.е. перечню и типам аргументов и возвращаемому значению), все пространства имен также совпадают. Сделано это было специально — изначально предполагалось разработчиком этих библиотек, что "новая" просто заменит "старую". Но жизнь внесла свои коррективы и возникла необходимость работать одновременно с обеими.
N_P>2) Если код отличается или одновременно надо работать с обоими — делайте свой движок и работайте уже с ним. Сам движок пускать в другом домене, процессе, сервисе — как нравится.
Да, надо работать с обеими сборками одновременно. Сценарий работы такой: Инстанцируем класс из "новой" dll-ки, куда добавили новую функцию, возвращающую логическое значение для переданных ей данных — "моё/не моё". Вызываем эту функцию, если сказала "моё", продолжаем работать с инстанцированным объектом из новой dll-ки и вызываем его методы. Если сказала "не мое", должны инстанцировать точно такой же (с тем же именем) объект из "старой" dll-ки и все методы (те же самые) вызывать для этого объекта.
Поясните ваши слова "делайте свой движок и работайте уже с ним". Просто так подлинковать обе dll-ки в проект и выбирать нужную для вызова кода через extern alias не получится? Я пытался сделать именно так (добавил ссылки на обе сборки в проект, задал для каждой из сборок свой алиас), но получаю при билде проекта сообщение, что имеется конфликт версий и все ссылки на одну из сборок "перекидываются" на другую:
Consider app.config remapping of assembly "myasm, Culture=neutral, PublicKeyToken=8283b45d91d69b7a" from Version "2.0.160.14" [libs\old\myasmold.dll] to Version "2.0.199.13" [libs\new\myasm.dll] to solve conflict and get rid of warning.
C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets : warning MSB3247: Found conflicts between different versions of the same dependent assembly.
Правильно ли я понимаю, что без "плясок с бубном" заставить обе dll-ки работать в одном экзешнике одновременно невозможно? Насколько сложны эти "пляски"? Что надо сделать?
N_P>3) Ну и external alias уже говорили.
Оказывается, это далеко не самое главное.
Здравствуйте, Аноним, Вы писали:
А>skipped
А код, работающий с разными версиями сборок, будет одинаковым?
Или все-таки возможна ситуация, когда будет, например, так:
if (SomeConditionOnS(s))
{
s = t1.Transform(s);
}
else
{
s1 = t2.Transform1(s);
s2 = t2.Transform2(s);
s = s1 + s2;
}
А>
?
Re[2]: Подскажите, как выбраться из такой ловушки
От:
Аноним
Дата:
19.08.12 18:26
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:
HL>Здравствуйте, Аноним, Вы писали:
А>>skipped HL>А код, работающий с разными версиями сборок, будет одинаковым? HL>Или все-таки возможна ситуация, когда будет, например, так:
Требуется (при некоторых условиях) вызывать методы из обеих сборок. Во вторую сборку добавили специальный метод, который говорит, какая сборка должна использоваться. Вызовы примерно такие:
string s=...
MySpace.MyObject t2 = new MySpace.MyObject(); // из второй сборкиif (t2.IfAdvancedProcessingRequired(s))
{
s = t2.Transform(s);
}
else
{
MySpace.MyObject t1 = new MySpace.MyObject(); // из первой сборки
s = t1.Transform(s);
}
В остальном две ветки (if-else) идентичны по вызываемым методам объектов из разных сборок.
Здравствуйте, Аноним, Вы писали:
А>В остальном две ветки (if-else) идентичны по вызываемым методам объектов из разных сборок.
Тогда, может быть, так:
1) создать свой
public interface IMyObject
{
string Transform(string value);
}
2) создать несколько реализаций IMyObject (одна версия сторонней сборки — одна реализация). Внутри каждой реализации создавать отдельный домен, грузить в него сборку нужной версии, и работать с ней, используя фичи конкретной версии. Если вы не можете влиять на код сторонней сборки, то, возможно, придется дописать еще свою MBR-обертку для каждого варианта MyObject (если он не является MBR);
3) работать с реализациями IMyObject, как с плагинами. Логику "с какой версией сторонней сборки будем работать" поместить в хост для этих плагинов:
public class MyObjectHost
{
public IMyObject Current
{
get
{
if (SomeConditionOnS(s))
return ...; // v 1.0return ...; // v 2.0
}
}
}
4) ну и в основном коде:
var myObjectHost = new MyObjectHost(/* параметры конструктора */);
var s = "...";
s = myObjectHost.Current.Transform(s);