web-dev-qa-db-ja.com

相互運用構造とクラスの進化するバージョンを維持する

C#.NETアプリケーションは、既知のAPIを呼び出し、コンポーネントの応答から相互運用構造をマーシャリングすることにより、外部コンポーネントと通信します。

これはすでに実装されており、うまく機能しています。ただし、バージョン管理が方程式に入ると、状況は少し複雑になります。コンポーネントは時間の経過とともに進化するため、相互運用構造の同期を維持する必要があります。アプリケーションは引き続き古いバージョンのコンポーネントと通信できる必要があるため、コンポーネントの特定のバージョンに対して実行時に適切な相互運用構造体を選択する必要があります。

私はこの問題を2、3日間頭の中で投げ続けてきましたが、特に満足できるものは思いつきませんでした。 Googleもあまり役に立っていないので、フィードバックを得るために、これを書いてここに投稿してみようと思いました。これは私がこれまでに持っているものです。

すべてのバージョンの構造体をライブラリにパッケージ化し、レジストリを作成し、実行時に使用する構造体を決定します

ここでは、すべての新しいバージョンの構造体が新しいバージョンの.NETアプリケーションにコンパイルされ、ある種のレジストリがAPIバージョンをライブラリ内の相互運用構造体バージョンにマッピングする役割を果たします。

Class1Class1_v1_1と呼ぶか、名前に基づいて曖昧さを解消する他の方法(または、同じ名前を使用して別々に配置する)が必要になるため、クラス/構造体の命名が問題になります。名前空間)。

バージョン管理されたDLLから相互運用構造のバージョンをロードします

ここでは、相互運用構造体のすべてのバージョンが個別のDLLにコンパイルされ、APIバージョンに基づいて動的にロードされます。APIバージョンとDLL名前。

命名の問題はなくなりますが、確かに展開のためのより多くの可動部分があり、その結果、実行時にうまくいかない可能性があります。


私が言ったように、私はどちらにも満足しておらず、私を逃れているはるかに明白な/よりクリーンな/エレガントな解決策があると感じています。私が依存コンポーネントのバージョンの変更をサポートしなければならなかった最初の人だとは想像できないので、このようなシナリオのガイダンスを提供できる既知のパターンはありますか?

フィードバックや可能な代替案を事前に感謝します!

4
user5877732

「既知のAPI」を制御できる場合は、相互運用APIへの変更を最小限に抑えることが最優先事項です。優れたAPI設計により、時間の経過に伴う変化が最小限に抑えられます。

どちらのソリューションも機能し、バージョン管理されたdllソリューションはよりクリーンになりますが、バージョン間で変更されないAPIの多くがあるため、よりかさばります。おそらく共通のライブラリを使用して、何らかの方法でコードの再利用を管理する必要があります。

レジストリを使用する代わりに、可能であれば既知のAPIの呼び出しを使用してバージョンを検出します。これにより、マシン全体で同じバージョンを使用する必要がなくなります。

1
Frank Hileman

可能であれば、各構造にバージョン番号を導入することは1つのアプローチであり、これはREST APIとファイル形式(バイナリテキストとプレーンテキストの両方)で特に人気があります。アプリケーションには、それぞれをアップグレードする機能があります。つまり、1つのアップグレードパスについてのみ心配する必要があり、必要に応じてアップグレードをチェーンするだけで済みます。必要に応じて、他のアップグレードパスを追加して処理を高速化し、ダウングレードパスを設定することもできます(これらは、複数のバージョンが同時に維持されているプログラムでよく見られ、多くのお客様がすぐにアップグレードするのを妨げるコストがかかります)。

たとえば、Employee構造体があるとします。おそらくバージョン1は本当に単純です:

{
    '_version': '1.0'
    'first_name': 'Amelia',
    'last_name': 'Bedelia',
    'employee_id': 123,
    'salary': 65000
}

しかし、後で、その名前フィールドを本当に結合したいとします。たぶん、その変化の原因を、おそらくそれにはビジネス上の理由があるとしましょう!構造に新しいバージョンがあります。

{
    '_version': '1.1'
    'name': 'Amelia Bedelia',
    'employee_id': 123,
    'salary': 65000
}

さて、couldバージョン番号にコードベースブランチがあるだけですが、古い構造を新しい構造と互換性のあるものに更新するだけで、はるかに将来性があり、多くの場合よりクリーンになります。これには、妥当なデフォルトの入力、他の場所からの追加情報の読み取りによる空白の入力、ユーザーへのその情報の要求などが含まれる場合があります。ただし、コードベースが構造の最新バージョンのみを処理する必要があることを確認するという考え方です。

したがって、次のようなブランチを持つアップグレード関数があります。

if employee['_version'] == '1.0':
    # Let's just make an assumption about names and combine it blindly
    employee = {
        '_version': '1.1',
        'name': employee['first_name'] + ' ' + employee['last_name'],
        'employee_id': employee['employee_id'],
        'salary': employee['salary']
    }
# And so on for other version upgrades, returning the final object
# at the end.

そして、現在のバージョン(より強力な型に変換できる)が得られるまで、これを続けます。このパターンは、データベースの更新にも適しています(SQLiteデータベースに対するAndroidの推奨される処理方法です)。

さて、この答えはこれまでかなり高いレベルでした。ただし、データ構造を汎用辞書として表すことができる場所に固有のものではありません(ただし、システムの境界を越えて情報を交換するための多用途で適応性のあるデータ構造であることは確かです)。データをバイナリ形式で保存でき、常に特定の位置にバージョンがある場合、データの解釈に使用する低レベル構造を簡単に選択できます(そしてそこから更新を適用します)。ただし、その場合でも、前述の構造体をどこかに格納する必要があります(ただし、これらの異なるバージョンの構造体に対して多くのコードパスを用意する必要がないため、値が得られます)。

0
Kat