MISE À JOUR: Je me suis récemment heurté à ce problème et j'ai constaté que la solution précédente n'était pas tout à fait complète. Malgré ce que toute la documentation dit, est possible de le faire, mais quelque peu douloureux.
La première étape, pour votre propre commodité, est de mettre en œuvre certains opérateurs de conversion:
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 encore à plat refuser de vous donner le champ UDT, peu importe comment vous déclarez la propriété . Donc, vous devez lui donner un champ binaire à la place. Vous n'avez pas besoin d'une procédure stockée ou tout SQL personnalisé pour cela, il suffit d'ajouter une colonne calculée à votre table:
ALTER TABLE MyTable
ADD UDTField_Data AS CAST(UDTField AS varbinary(len))
Où LEN est quel que soit votre UDT définit dans l'attribut MaxByteSize.
Maintenant, vous pouvez enfin accéder aux données de la colonne. Vous pourriez être tenté d'utiliser votre UDT comme type de retour de la nouvelle propriété, en pensant que Linq to SQL trouvera votre opérateur de conversion et convertira automatiquement à partir du tableau d'octets; ne dérange pas. Linq to SQL décidera qu'il s'agit en fait d'un objet .NET sérialisé et crachera un message à l'effet que "le flux d'entrée n'est pas un format binaire valide". Au lieu de cela, vous avez besoin d'une autre couche de indirection:
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; }
}
Quelques notes à préciser ce qui se passe ici:
- Les données réelles sur le terrain (udtField) est déclarée comme l'UDT lui-même, pas un tableau d'octets. La raison en est que nous voulons seulement que la conversion se produise lors du chargement ou de l'enregistrement dans la base de données. Si vous deviez convertir le tableau d'octets en UDT chaque fois que vous y accédiez, cela nuirait non seulement aux performances, mais cela entraînerait des incohérences si l'UDT déclarait des champs mutables.
- La propriété raw byte [] (UdtFieldData) est déclarée privée, donc les consommateurs ne voient que l'UDT lui-même. Linq to SQL le lira toujours tant qu'il aura l'attribut [Column].
- La propriété UdtFieldDatane déclare pas de propriété de stockage.Ceci est critique. Si vous essayez d'utiliser le champ UDT comme propriété de stockage, vous obtiendrez simplement le même type erreur de conversion.
- Enfin, la propriété UdtProperty permet aux consommateurs d'accéder aux données. Pour eux, il ressemble à toute autre propriété.
Il est regrettable que vous devez sauter à travers tant de cerceaux pour obtenir ce travail, mais il fonctionne. Vous aurez probablement des difficultés à faire ce genre de massage à travers le concepteur de surfaces Linq, ce qui n'est qu'une des raisons pour lesquelles je ne l'utilise pas; mieux d'écrire les cours vous-même et d'utiliser SqlMetal pour vous aider si nécessaire.