Должны ли сборки быть физическими файлами? - WepAPI

У нас есть пользовательский интерфейс MVC, который обычно рисует себя на основе типов данных, которые он получает из WebAPI. При запуске приложения пользовательского интерфейса я вызываю свой WebApi, чтобы вытащить «LeadTypes» вниз в списке списка сборок, используя двоичный форматтер для сериализации и десериализации. Проблема в том, что когда ссылки удаляются из пользовательского интерфейса, десериализатор взрывается, говоря, что не может найти InstallmentLoan версии 1.0.0.0 или одну из его зависимостей. Ну, нет никаких зависимостей, кроме системы, это просто базовые модели с пользовательскими аннотациями данных и т. д. Цель состоит в том, чтобы ни один из наших типов не упоминался в пользовательском интерфейсе.

Ошибка «Не удалось загрузить файл или сборку «LeadGenFramework.Entity.LeadType.InstallmentLoan, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = null» или одна из ее зависимостей. Система не может найти указанный файл».

Должен быть умный способ использовать сборки памяти и ссылаться на них, не имея физического файла. Чего я не понимаю, так это почему он ищет файл, если он у меня в памяти?

Любые указатели были бы замечательными!

Вот FusionLog:

=== Информация о состоянии перед привязкой === ЖУРНАЛ: DisplayName = LeadGenFramework.Entity.LeadType.InstallmentLoan, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null (полностью указанный) LOG: Appbase = file:/// E:/MainTrunk2/LeadGenFramework-Copy/trunk/LeadGenFramwork.Web.Api.RestClient.Test/bin/Журнал отладки: Initial PrivatePath = NULL

Вызов сборки: (Неизвестно)

LOG: эта привязка запускается в контексте загрузки по умолчанию. ЖУРНАЛ: Использование файла конфигурации приложения: C:\Users\charbaugh\AppData\Local\Temp\tmp8271.tmp ЖУРНАЛ: Использование файла конфигурации хоста: ЖУРНАЛ: Использование файла конфигурации компьютера из C:\Windows\Microsoft.NET\Framework\v4. 0.30319\config\machine.config. ЖУРНАЛ: в настоящее время политика не применяется к ссылке (частная, пользовательская, частичная или привязка сборки на основе местоположения). ЖУРНАЛ: Попытка загрузки нового файла URL:///E:/MainTrunk2/LeadGenFramework-Copy/trunk/LeadGenFramwork.Web.Api.RestClient.Test/bin/Debug/LeadGenFramework.Entity.LeadType.InstallmentLoan.DLL. ЖУРНАЛ: Попытка загрузки нового файла URL:///E:/MainTrunk2/LeadGenFramework-Copy/trunk/LeadGenFramwork.Web.Api.RestClient.Test/bin/Debug/LeadGenFramework.Entity.LeadType.InstallmentLoan/LeadGenFramework.Entity.LeadType .InstallmentLoan.DLL. ЖУРНАЛ: Попытка загрузки нового файла URL:///E:/MainTrunk2/LeadGenFramework-Copy/trunk/LeadGenFramwork.Web.Api.RestClient.Test/bin/Debug/LeadGenFramework.Entity.LeadType.InstallmentLoan.EXE. ЖУРНАЛ: Попытка загрузки нового файла URL:///E:/MainTrunk2/LeadGenFramework-Copy/trunk/LeadGenFramwork.Web.Api.RestClient.Test/bin/Debug/LeadGenFramework.Entity.LeadType.InstallmentLoan/LeadGenFramework.Entity.LeadType .InstallmentLoan.EXE.


person chdev77    schedule 28.02.2014    source источник
comment
Как оно у вас в памяти?   -  person Anton Tykhyy    schedule 01.03.2014
comment
просто используя базовый поток, который возвращается из получения веб-API.   -  person chdev77    schedule 01.03.2014
comment
Двоичная сериализация не включает в свой вывод никакие сборки, только их полные имена. Если вы хотите десериализовать вывод, вам либо нужны сборки, в которые их можно загрузить, либо вам нужно использовать причудливые методы десериализации (суррогаты и т. д.) для десериализации в объекты разных типов.   -  person Anton Tykhyy    schedule 01.03.2014
comment
Антон, я иду по этому пути. Я обнаружил, что класс сборки содержит только основные свойства, а не фактические данные в файлах сборки, методе, свойствах и т.д.   -  person chdev77    schedule 01.03.2014


Ответы (2)


Вам нужно будет откуда-то получить сборки, для этого вам нужно будет передать содержимое сборки и вызвать Assembly.load(byte[] rawAssembly).

person Yishai Galatzer    schedule 01.03.2014
comment
Да, я не могу заставить его работать. Но также может быть, что я неправильно сериализую на стороне webapi. - person chdev77; 02.03.2014
comment
Итак, снова пытаемся понять: пытаетесь ли вы сериализовать свою фактическую сборку типов POCO по проводам к клиенту? Звучит как дыра в безопасности, которую можно использовать. Выясните, какие метаданные вы хотите передать клиенту для описания ваших типов, и передайте только это, а затем сконструируйте то, что вам нужно на клиенте. Помимо безопасности, передача сборок приводит ко многим вещам, которые могут пойти не так (1. Отсутствие зависимостей, 2. Только клиенты C#, 3. Проблемы совместимости с фреймворком) - person Yishai Galatzer; 03.03.2014
comment
Да, поко. У меня было бы беспокойство, о котором вы упомянули, но это частный веб-сайт. Зная, что я делаю сейчас, я бы просто сериализовал типы... но мне также нужен доступ к пользовательским атрибутам, аннотациям данных и т. д. Спасибо. - person chdev77; 04.03.2014

Хорошо, я понял это. Я использовал отражения, чтобы получить сборку, а затем сериализовал тип сборки. Сборка предоставляет только информацию о свойствах сборки физического файла и не содержит полного графа файла. Надеюсь, это кому-нибудь поможет, урок усвоен!

Так что я просто использовал это на контроллере, а затем на клиенте.

    public MemoryStream Get()
    {
        List<MemoryStream> leadTypeAssemblyStreams = new List<MemoryStream>();

        foreach (string dllAssembly in _leadTypeNamespaces)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                string filename = dllAssembly;

                if (!filename.Contains(".dll"))
                    filename = filename + ".dll";

                using (FileStream file = new FileStream(AssemblyDirectory + "\\" + filename, FileMode.Open, FileAccess.Read))
                {
                    byte[] bytes = new byte[file.Length];
                    file.Read(bytes, 0, (int)file.Length);
                    ms.Write(bytes, 0, (int)file.Length);
                    file.Close();

                    leadTypeAssemblyStreams.Add(ms);
                    ms.Close();
                }
            }    
        }

        return new MemoryStream(SerializeObj(leadTypeAssemblyStreams));
    }

    internal static string AssemblyDirectory
    {
        get
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            UriBuilder uri = new UriBuilder(codeBase);
            string path = Uri.UnescapeDataString(uri.Path);
            return Path.GetDirectoryName(path);
        }
    }


    internal static byte[] SerializeObj(object obj)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();

            formatter.Serialize(stream, obj);

            byte[] bytes = stream.ToArray();
            stream.Close();

            return bytes;
        }
    }

Теперь я могу просто вызвать GetDeserializedFileStreamPaths(stream), и он вернет физический путь к файлам, которые были переданы из WebApi. Вот мой класс WebApiClient, на который будет ссылаться пользовательский интерфейс.

public class LFGWebApiClientHelpers
{
    public List<Type> GetLeadTypes(Stream stream)
    {
        List<string> assembliesFilePath = GetDeserializedFileStreamPaths(stream);
        List<Assembly> assemblies = new List<Assembly>();

        foreach (string filePath in assembliesFilePath)
        {
            assemblies.Add(Assembly.LoadFile(filePath));
        }

        List<Type> leadTypes = new List<Type>();

        foreach (Assembly asm in assemblies)
        {
            Type leadType = asm.GetTypes().Where(x => x.GetCustomAttributes(typeof(WebApiExportedTypeAttribute), false).Length > 0).FirstOrDefault(x => x.Name.Contains("Lead"));
            leadTypes.Add(leadType);
        }

        return leadTypes;
    }

    private static List<string> GetDeserializedFileStreamPaths(Stream stream)
    {
        try
        {
            List<string> assembliesPath = new List<string>();
            IFormatter formatter = new BinaryFormatter();
            List<MemoryStream> leadTypeAssemblyStreams = formatter.Deserialize(stream) as List<MemoryStream>;

            int fileNumber = 1;

            string leadTypeAsseblyDirectory = AssemblyDirectory.Replace("bin", "DownloadedLeadTypeAssemblies");

            if (!Directory.Exists(leadTypeAsseblyDirectory))
                Directory.CreateDirectory(leadTypeAsseblyDirectory);
            try
            {
                if (leadTypeAssemblyStreams == null)
                    throw new Exception("Lead type assembly stream cannot be null.");

                    foreach (MemoryStream leadTypeAssemblyStream in leadTypeAssemblyStreams)
                    {
                        string localFileLocation = leadTypeAsseblyDirectory +
                                                   string.Format("\\LeadType{0}.dll", fileNumber);

                        //add path for return type
                        assembliesPath.Add(localFileLocation);
                        using (
                            FileStream file = new FileStream(localFileLocation, FileMode.Create, FileAccess.Write))
                        {
                            leadTypeAssemblyStream.WriteTo(file);
                            leadTypeAssemblyStream.Close();
                        }

                        fileNumber++;
                    }
            }
            catch (Exception ex)
            {
                throw new LGFException().SetMessage(ex);
            }

            stream.Close();

            return assembliesPath;
        }
        catch (Exception ex)
        {
            throw new LGFException().SetMessage(ex, "There was a problems during the deserializtion of LeadTypes.");
        }
    }

    private static string AssemblyDirectory
    {
        get
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            UriBuilder uri = new UriBuilder(codeBase);
            string path = Uri.UnescapeDataString(uri.Path);
            return Path.GetDirectoryName(path);
        }
    }
}
person chdev77    schedule 02.03.2014