2013-04-30 2 views
4

J'ai créé un CompositeControl simple et exposé une propriété Nullable DateTimeOffset. Je suis liant le contrôle à un champ DateTimeOffset SQL Server à l'aideDistribution spécifiée n'est pas valide lorsque DataBind à Nullable DateTimeOffset et le champ est NULL

DateTimeOffset='<%# Bind("myDateTimeOffsetField") %>' 

Cela fonctionne très bien lorsque le champ DateTimeOffset a une valeur. Mais lorsque le champ est NULL, j'obtiens une erreur "Cast spécifié n'est pas valide". Comment puis-je arrêter cette erreur et définir ma propriété sur Nothing lorsque le champ est NULL? Je pensais que ce serait le comportement par défaut!

définition de la propriété est:

Public Property DateTimeOffset As DateTimeOffset? 

commentaire plus tard:

J'ai trouvé que cela fonctionne si je change d'utiliser Bind à:

DateTimeOffset='<%# iif(IsDbNull(Eval("myDateTimeOffsetField")), Nothing, Eval("myDateTimeOffsetField")) %>' 

Mais je don 't get "myDateTimeOffsetField" passé comme un argument dans l'événement FormView.ItemUpdating (oui, c'est dans un contrôle FormView) depuis ASP.NET suppose que je ne suis pas t liant à la base de données.

code réel (Ajouté par demande)

C'est la propriété de mon contrôle composite que je suis en train de se lier à:

Public Property DateTimeOffset As DateTimeOffset? 
Get 
    Return CType(ViewState("DTO"), DateTimeOffset?) 
End Get 
Set(value As DateTimeOffset?) 
    ViewState("DTO") = value 
End Set 
End Property 

Heres le balisage pour la liaison. Le contrôle est dans le EditItemTemplate d'un FormView qui est lié à une source de données SQL retournant un champ appelé [dtoMldRejOn] avec une valeur facultative DateTimeOffset.

<APS:DateTimeOffsetControl runat="server" id="dtocMldRejOn" TextBoxCssClass="inputdatetime" ValidationGroup="vw1" FieldName="<%$ Resources: Resource, rxgFrom %>" DateTimeOffset='<%# Bind("dtoMldRejOn") %>' WindowsTimeZoneID="<%# me.WindowsTimeZoneID %>" IsRequired="false" /> 

Comme vous pouvez le voir, mon contrôle Composite sert à gérer les valeurs DateTimeOffset. Tout fonctionne très bien, à moins que le champ DateTimeOffset [dtoMldRejOn] de la base de données soit NULL, alors j'obtiens l'exception.

+0

probablement une question stupide, mais obtenez-vous la même erreur si vous essayez 'DateTimeOffset = Nothing'? –

+0

Non, je peux le mettre à rien sans problème. – PapillonUK

Répondre

1

Je n'ai jamais créé des contrôles bindable avant, mais je voudrais faire des suggestions. Que diriez-vous de définir votre propriété DateTimeOffset pour être de type Object. De cette façon, la propriété acceptera tous les types de données, y compris DBNull.

Et une fois dans le code Set, vérifiez si la valeur transmise est DBNull.Value. Si oui, créez un DataTimeOffset vide? objet et enregistrez-le dans ViewState.

En cas de valeurs non DBNull, erreur de renvoi si elle ne peut pas être convertie en date/heure.

Je n'ai pas essayé ceci cependant je ne sais pas si cela fonctionnera ou pas.

################ ################ MISE À JOUR ANSWER

Ma suggestion est, vous créez 2 propriétés comme suit:

Public Property DateTimeOffset() As DateTimeOffset? 
    Get 
     Return DirectCast(ViewState("DTO"), DateTimeOffset?) 
    End Get 
    Set(ByVal Value As DateTimeOffset?) 
     ViewState("DTO") = Value 
    End Set 
End Property 

<Bindable(True, BindingDirection.TwoWay)> 
Public Property DbDateTimeOffset As Object 
    Get 
     Return Me.DateTimeOffset 
    End Get 
    Set(value As Object) 
     If IsDBNull(value) OrElse value Is Nothing Then 
      Me.DateTimeOffset = New DateTimeOffset? 
     Else 
      Me.DateTimeOffset = DirectCast(value, DateTimeOffset?) 
     End If 
    End Set 
End Property 

donc, dans votre balisage, la liaison sera à la DbDateTimeOffset propriété:

DbDateTimeOffset='<%# Bind("myDateTimeOffsetField") %>' 

Alors que dans le code derrière, vous pouvez utiliser l'autre propriété pour lire la propriété avec avoir à jeter.

+0

Salut, oui j'ai déjà essayé ça et ça marche. Je vérifie IsDbNull dans le setter de propriété d'objet et si vrai je place alors la propriété "vraie" DateTimeOffset à Nothing. Tout cela fonctionne mais malheureusement cela signifie que je dois toujours convertir la propriété object en un DateTimeOffset nul lorsque je le relire - ou bien toujours lire depuis la propriété "real" et écrire dans la propriété "object". Je ne suis pas très content de cette solution, car je conçois le contrôle pour les autres utilisateurs et je préfère éviter cette complication si possible. Cela peut être la meilleure solution de contournement si je ne trouve pas de solution. – PapillonUK

+0

Donnez votre avis sur votre réponse UPDATED: Merci ajakblackgoat. Je n'aime pas cette idée désordonnée d'objet (!), Mais comme vous je suis venu au même verdict - semble que je devrais aller jusqu'à ce que quelque chose de mieux se présente. J'accepterai votre réponse puisque c'est plus propre que ma propre implémentation et que la prime est sur le point d'expirer! Merci encore. – PapillonUK

+0

Fou, vraiment que le seul but étant servi ici est de vérifier DbNull dans la propriété Getter et le convertir en Nothing si True! Je soupçonne qu'il y a une meilleure façon, mais cela fera pour l'instant. Notez que dans l'autre sens un DateTimeOffset? de rien convertit automatiquement en une valeur nulle sans problème! – PapillonUK

0

Basé sur this post,

Je pense que vous avez juste besoin de marquer votre propriété avec l'attribut Bindable:

<System.ComponentModel.Bindable(True)> _ 
Public Property DateTimeOffset As DateTimeOffset? 
+0

Dang, je pensais que vous l'aviez alors quand j'ai lu ce post, mais non, exactement la même erreur "Cast spécifié n'est pas valide" se produit dans le CompositeControl.DataBind(). Je me demande si ASP.NET ne sait pas comment lier un NULL DateTimeOffset – PapillonUK

+0

@PapillonUK - Ce n'est pas le DTO qui est le problème. C'est juste une valeur nulle qui est le problème. Il doit comprendre que «DBNull.Value' est la même chose que null ('Nothing' dans VB). Ça devrait juste marcher. Pouvez-vous mettre à jour votre réponse et poster plus de votre code afin que je puisse exécuter exactement ce que vous exécutez? –

+0

Je reçois exactement le même problème si je rend ma propriété DateTimeOffset non nullable. Je pense vraiment que c'est le DateTimeOffset avec lequel il se bat. Je vais essayer de mettre du code! – PapillonUK

0

Le problème est que DbNull est différent de Nothing et vous devez écrire explicitement quelque part dans votre code. Ma première idée ici était d'utiliser les événements de liaison pour ajouter la valeur. Donc, si vous vous gardez code comme ceci:

DateTimeOffset='<%# iif(IsDbNull(Eval("myDateTimeOffsetField")), Nothing, Eval("myDateTimeOffsetField")) %>' 

Vous pouvez ajouter manuellement le paramètre DateTimeOffset dans les événements Updating avant que le liaison (je peux mettre à jour la réponse plus tard avec plus de détails si vous voulez)

Quoi qu'il en soit, après avoir lu votre code plus attentivement, je pensais que peut-être le CType ne cast pas correctement. Avez-vous essayé de remplacer votre Get avec quelque chose comme ça?

Get 
    Return If(IsDbNull(ViewState("DTO")), Nothing, CType(ViewState("DTO"), DateTimeOffset?)) 
End Get 
+0

La modification de la commande Get entraîne la même erreur "cast is not valid" lors de la liaison initiale. Je pense donc que l'erreur est survenue lors de l'utilisation de Set plutôt que lors de l'obtention. Oui, je sais que je peux ajouter le paramètre lors de l'événement de mise à jour en utilisant FindControl par exemple. C'est en fait ce que je fais, mais je ne l'aime pas car le contrôle est aussi utilisé par d'autres personnes! Merci pour la pensée si! – PapillonUK

+0

@PapillonUK Si c'est sur le plateau, faites-le dans l'autre sens. 'If ​​(string.IsNullOrEmpty (value))) Then ViewState (" DTO ") = DbNull.Value' – nmat

+0

Salut nmat - Le code à tester pour DbNull dans le Getter ne sera jamais atteint. L'exception de transtypage se produit avant même que le Getter ne soit appelé et semble être dû au fait que .NET ne peut pas gérer la conversion d'un DbNull en un DateTimeOffset lors de la liaison de données. – PapillonUK

0

Votre code avec iif ... Fonctionne pour moi.
J'ai créé un contrôle de test et une version de page avec du code derrière les pages (j'ai aussi testé le code derrière la version mais celui-ci est plus facile à publier). J'ai le cadre cible VS2010 est de 4,0. D'abord le contrôle (TstNullableCtrl.ascx):

<%@ Control Language="vb" AutoEventWireup="false" %> 
<script runat="server"> 
    Public Property DateTimeOffset As DateTimeOffset? 
</script> 
<div ID="Label1" runat="server" > 
<%= If(DateTimeOffset.HasValue, DateTimeOffset.ToString, "Empty")%> 
</div> 

et la page - une table avec deux lignes et deux colonnes lié à DataGrid (TstNullablePage.aspx):

<%@ Page Language="vb" AutoEventWireup="false" %> 
<%@ Import Namespace="System.Data"%> 
<%@ Register src="TstNullableCtrl.ascx" tagname="TstNullableCtrl" tagprefix="uc1" %> 
<script runat="server"> 
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 
     Dim tbl As New DataTable 
     tbl.Columns.Add(New DataColumn("id", GetType(Integer)) With {.AutoIncrement = True}) 
     tbl.Columns.Add(New DataColumn("myDateTimeOffsetField", GetType(DateTimeOffset))) 
     Dim row As DataRow 
     row = tbl.NewRow : row("myDateTimeOffsetField") = DateTimeOffset.Now 
     tbl.Rows.Add(row) 
     row = tbl.NewRow : row("myDateTimeOffsetField") = DBNull.Value 
     tbl.Rows.Add(row) 
     tstgrd.DataSource = tbl : tstgrd.DataBind() 
    End Sub 
</script> 
<html> 
<body> 
    <form id="form1" runat="server"> 
     <asp:datagrid ID="tstgrd" runat="server"> 
     <Columns> 
     <asp:TemplateColumn HeaderText="Offset"> 
      <itemtemplate>  
      <uc1:TstNullableCtrl ID="WithNullableDate1" runat="server" 
       DateTimeOffset='<%# iif(IsDbNull(Eval("myDateTimeOffsetField")), Nothing, Eval("myDateTimeOffsetField")) %>' /> 
      </itemtemplate> 
     </asp:TemplateColumn> 
     </Columns> 
     </asp:datagrid> 
    </form> 
</body> 
</html> 

Et résultat attendu (un La valeur lorsqu'il n'y a et 'vide' lorsqu'il est nulle)

result

Modifier

Cependant, je pense que pour « éviter des complications » meilleure solution est la suivante:

Public _DateTimeOffset As Object 
Public Property DateTimeOffset As Object 
Get 
    If IsDBnull(_DateTimeOffset) then Return Nothing 
    Return Ctype(_DateTimeOffset, DateTimeOffset?) 
End Get 
Set(value As Object) 
    _DateTimeOffset = value 
End Set 
End Property 
+0

Merci pour cela. Oui, la complication est ce que j'essaie d'éviter et vous êtes arrivé à la même solution - je dois utiliser un objet pour la propriété plutôt qu'un DateTimeOffset car ASP.NET a du mal à lier un dbNull à une structure DateTimeOffset. Je pense que ajakblackgoat a fourni la solution la plus complète pour cette solution de contournement donc j'apprécie le travail ici mais je vais devoir lui donner les points! – PapillonUK

0

Je sais que cette question a été déjà répondu que je voudrais simplement documenter ma solution qui est entre ces deux réponses comme je l'ai suis tombé sur aujourd'hui:

Private _AssetId As Object 
<Bindable(True, BindingDirection.TwoWay)> 
Public Property AssetId() As Object 
    Get 
     If _AssetId Is Nothing Then     
      Return Nothing     
     Else 
      Return CType(_AssetId, Integer) 
     End If 
    End Get 
    Set(ByVal value As Object) 
     If value Is Nothing OrElse IsDBNull(value) OrElse CType(value, String) = "" Then 
      _AssetId = Nothing     
     Else 
      _AssetId = CType(value, Integer)     
     End If 
    End Set 
End Property 
Questions connexes