LinQ to SQL и CLR определяемые пользователем типы - PullRequest
2 голосов
/ 23 апреля 2009

Я создал пользовательский тип в .Net 3.5 согласно записи в моем блоге по адресу:

http://jwsadlerdesign.blogspot.com/2009/04/this-is-how-you-register.html

Это прекрасно работает при использовании SQL с такими технологиями, как nHibernate.

Однако, когда я пытаюсь сопоставить свой класс LinQ с SQL, чтобы использовать этот UDT (с определениями атрибутов, а не XML), я устанавливаю это свойство как перечисление. Я не могу получить LinQ для отображения на этот тип. Я пробовал Image, Binary, varchar и integer, которые, похоже, выдают ошибки Invalid Cast.

В частности, я получаю сообщение об ошибке «Невозможно привести объект типа« ISTD.InstallManager.Common.Classes.SQLUDTTargetType »к типу« System.Byte [] ». Любые идеи или помощь будут высоко оценены.

Джеймс.

1 Ответ

2 голосов
/ 28 ноября 2009

ОБНОВЛЕНИЕ: Я сам недавно столкнулся с этим и обнаружил, что предыдущее решение было не совсем полным. Несмотря на то, что написано во всей документации, можно сделать, но несколько болезненно.

Первый шаг, для вашего удобства, заключается в реализации некоторых операторов преобразования:

public class MyUDT : INullable, IBinarySerialize
{
    // Class implementation would go here
    // ...

    public static explicit operator MyUDT(byte[] data)
    {
        using (MemoryStream stream = new MemoryStream(data))
        {
            using (BinaryReader reader = new BinaryReader(stream))
            {
                MyUDT result = new MyUDT();
                result.Read(reader);
                return result;
            }
        }
    }

    public static explicit operator byte[](MyUDT x)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            using (BinaryWriter writer = new BinaryWriter(ms))
            {
                x.Write(writer);
            }
            return ms.ToArray();
        }
    }
}

Linq to SQL будет по-прежнему категорически отказываться предоставлять вам поле UDT, независимо от того, как вы объявите свойство. Таким образом, вы должны вместо этого дать ему двоичное поле. Для этого вам не нужна хранимая процедура или какой-либо пользовательский SQL, просто добавьте вычисляемый столбец в таблицу:

ALTER TABLE MyTable
ADD UDTField_Data AS CAST(UDTField AS varbinary(len))

Где len - это то, что определяется вашим UDT в атрибуте MaxByteSize .

Теперь вы наконец можете получить доступ к данным столбца. Возможно, вы захотите использовать свой UDT в качестве возвращаемого типа нового свойства, полагая, что Linq to SQL найдет ваш оператор преобразования и автоматически выполнит преобразование из байтового массива; не беспокойся Linq to SQL решит, что это на самом деле сериализованный объект .NET, и выдаст сообщение о том, что «входной поток не является допустимым двоичным форматом». Вместо этого вам нужен еще один слой косвенности:

private MyUDT udtField;

[Column(Name = "UDTField_Data", DbType = "varbinary(len)")]
private byte[] UdtFieldData
{
    get { return (byte[])udtField; }
    set { udtField = (MyUDT)value; }
}

public MyUDT UdtProperty
{
    get { return udtField; }
    set { udtField = value; }
}

Несколько замечаний, чтобы прояснить, что здесь происходит:

  • Фактические данные поля ( udtField ) объявляются как сам UDT, а не как байтовый массив. Причина этого в том, что мы хотим, чтобы преобразование происходило только при загрузке или сохранении в базе данных. Если бы вам приходилось преобразовывать байтовый массив в UDT каждый раз, когда вы обращались к нему, это не только ухудшало бы производительность, но и вызывало бы несоответствия, если бы UDT объявлял любые изменяемые поля.
  • Свойство raw byte [] ( UdtFieldData ) объявлено закрытым, поэтому потребители видят только сам UDT. Linq to SQL все равно будет читать его, пока он имеет атрибут [Column].
  • Свойство UdtFieldData не объявляет свойство хранения . Это очень важно; если вы попытаетесь использовать поле UDT в качестве свойства хранилища, вы просто получите ту же ошибку преобразования типов.
  • Наконец, свойство UdtProperty - это то, как потребители получают доступ к данным. Для них это выглядит как любое другое свойство.

К сожалению, вам приходится прыгать через столько обручей, чтобы заставить это работать, но это работает. Вы, вероятно, будете испытывать трудности при выполнении этого вида массажа с помощью дизайнера поверхностей Linq, что является лишь одной из нескольких причин, почему я не использую его; Лучше написать сами классы и использовать SqlMetal, чтобы помочь вам, если это необходимо.

...