Можно конечно взять сторонние утилиты, например какой-нибудь опенсорсный ILSpy, сохранить сборку на диск и потом декомпилировать.

Но хочется просто подключиться к БД и посмотреть все сборки и что там внутри.
И к тому же, есть очень много качественных Opensource компонент на все случаи программистской жизни, да и писать на C# удобно и легко :)
Итак.
Для этого нам потребуется Nuget-пакет

SqlDataReader-ом, SQL-запрос тривиальный:
i.e. загружаем наши байты в класс AssemblyDefinition
rdr — тут как раз SqlDataReader, но можно зачитать все сборки в какой-нибудь список, например список объектов вот такого простенького класса
Поскольку сборок обычно много, то их удобно выводить на экран в виде списка или таблички, а вот внутренности в виде дерева конечно же!
Примерно так будет норм

например в TreeView
Всего три строчки кода! (например я сделал в событии treeView1_AfterSelect)
Сам исходник, где всё сведено воедино
Выглядит в итоге примерно так

Естественно сборки могут зависеть от сборок, и всё это может лежать в SQL базе.
Тут если декомпилировать «в лоб» — будет ошибка декомпиляции.
Избежать ее легко (т. к. в ICSharpCode.Decompiler есть AssemblyResolver):
Напишем свой резолвер, просто передав ему все доступные сборки в анализируемой SQL базе:
Переменная _listItems — это как раз список всех сборок из sys.assembly_files
Теперь при декомпиляции все зависимости удается разрешить!

P.S.: Лили Джеймс была использована для привлечения внимания.

Но хочется просто подключиться к БД и посмотреть все сборки и что там внутри.
И к тому же, есть очень много качественных Opensource компонент на все случаи программистской жизни, да и писать на C# удобно и легко :)
Итак.
Для этого нам потребуется Nuget-пакет

Читаем из базы все сборки
SqlDataReader-ом, SQL-запрос тривиальный:
SELECT af.name, af.content FROM sys.assemblies a INNER JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id
Инициализируем сборку
i.e. загружаем наши байты в класс AssemblyDefinition
var _assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(new MemoryStream(rdr.GetSqlBytes(1).Value), pars);
rdr — тут как раз SqlDataReader, но можно зачитать все сборки в какой-нибудь список, например список объектов вот такого простенького класса
public class SqlAssemblyObject { public string AssemblyName { get; set; } public SqlBytes Data { get; set; } }
Поскольку сборок обычно много, то их удобно выводить на экран в виде списка или таблички, а вот внутренности в виде дерева конечно же!
Примерно так будет норм

Выводим на экран
например в TreeView
foreach (var typeInAssembly in _assemblyDefinition.MainModule.Types) { if (typeInAssembly.IsPublic) { var node = new TreeNode(typeInAssembly.FullName); node.Tag = typeInAssembly; } treeView1.Nodes.Add(node); }
Декомпиляция
Всего три строчки кода! (например я сделал в событии treeView1_AfterSelect)
var decompiler = new ICSharpCode.Decompiler.CSharp.CSharpDecompiler(_assemblyDefinition.MainModule, new DecompilerSettings()); var str = decompiler.DecompileAsString(e.Node.Tag as IMemberDefinition); textbox1.Text = str;
Сам исходник, где всё сведено воедино
Выглядит в итоге примерно так

Немного о подводных камнях
Естественно сборки могут зависеть от сборок, и всё это может лежать в SQL базе.
Тут если декомпилировать «в лоб» — будет ошибка декомпиляции.
Избежать ее легко (т. к. в ICSharpCode.Decompiler есть AssemblyResolver):
Напишем свой резолвер, просто передав ему все доступные сборки в анализируемой SQL базе:
public class DatabaseAssemblyResolver : IAssemblyResolver { List<SqlAssemblyObject> _databaseLibs; DefaultAssemblyResolver _resolver; public DatabaseAssemblyResolver(List<SqlAssemblyObject> dlls) { _databaseLibs = dlls; _resolver = new DefaultAssemblyResolver(); } public void Dispose() { //throw new NotImplementedException(); } public AssemblyDefinition Resolve(AssemblyNameReference name) { foreach (var item in _databaseLibs) { if(item.AssemblyName.Contains(name.Name)) { return AssemblyDefinition.ReadAssembly(new MemoryStream(item.Data.Value)); } } return _resolver.Resolve(name); } public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) { foreach (var item in _databaseLibs) { if (item.AssemblyName.Contains(name.Name)) { return AssemblyDefinition.ReadAssembly(new MemoryStream(item.Data.Value)); } } return _resolver.Resolve(name, parameters); }
Переменная _listItems — это как раз список всех сборок из sys.assembly_files
var pars = new ReaderParameters(); pars.AssemblyResolver = new DatabaseAssemblyResolver(_listItems); _assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(new MemoryStream(_listItems[idx].Data.Value), pars);
Теперь при декомпиляции все зависимости удается разрешить!

P.S.: Лили Джеймс была использована для привлечения внимания.
