Upozornění na duplicitní záznam   zodpovězená otázka

ASP.NET WebForms, Databáze

Dobrý den, předem se omlouvám, ale jsem opravdový začátečník a potřeboval bych poradit. Mám jednoduchou webovou aplikaci, která využívá databázi. A teď k mému dotazu. Mám jednoduchý formulář, kterým vkládám do databáze záznamy ( jedná se o evidenci počítačů ). Mimo jiné tam zadávám Ip adresu, hardware, software, atd. Primary key mám nastaven na sloupečku Ip adresa, protože chci, aby se mi zde nedala zdvojit Ip adresa a tudíž chci, aby byla unikátní. A problém mám v tom, že se mi tam skutečně stejná Ip adresa nevloží, ale chtěl bych nějak ošetřit, aby mě při vložení duplicitního záznamu ( Ip adresy ), formulář na toto nějak upozornil, protože nyní když formulář odešlu tak se sice záznam neprovede, ale nic mě neupozorní, že k záznamu nedošlo ( to znamená, že nevyskočí žádná chyba asp.netu, že v databázi již stejný záznam je ) a tudíž ani nevím jak by se dala odchytit nějaká chybová událost. V případě potřeby sem dám i kód pro lepší orientaci. Děkuji moc za případné rady.

Pavel Nový

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den, napadají mě 2 možnosti. Buď nejdříve pošlete dotaz do databáze kde parametrem pošlete IP adresu a budete kontrolovat zda se vám vrátí nějaký záznam a podle toho zobrazíte uživateli informaci zda je možné IP adresu uložit nebo ne.

Pokud ale používáte SQL server tak bych to osobně řešil pomocí stored procedury, kde na začátku budete také kontrolovat zda IP adresa už v seznamu není (pomocí IF EXISTS blablabla). Pokud v seznamu není tak ji vložíte, pokud tam je tak ji nevložíte. Navíc si ale vytvoříte outupu parameter (pojmenovaný třeba errorID) a budete vědět, že např.

@errorID=0 znamená že záznam byl v pořádku vložen

@errorID=1 znamená že se jedná o duplicitu

@errorID=2 znamená... kreativnosti se meze nekladou.

No a na klientské straně aplikace se zachováte podle toho jaké errorID se Vám vrátí a případně zobrazíte nějaké upozornění.

Takhle to řeším já když se potřebuju dozvědět zda byl záznam vložen a pokud ne tak proč (občas když je složitější aplikační logika tak se tenhle způsob hodí, protože si nadefinujete chybové stavy tak jak se Vám hodí).

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jé moc děkuji za rychlý příspěvek, ale jak jsem uvedl jsem hodně velký začátečník. Dám jsem kousek kódu :

<script runat="server">

    Protected Sub IpAdressTextBox_Load(ByVal sender As Object, ByVal e As System.EventArgs)
        ''' <summary>
        ''' jsem dopsat kód
        ''' </summary>
    End Sub
</script>

   <h2>Přidat počítač</h2>
    <br />
    <moje:RedirectingSqlDataSource ID="SqlDataSource2" runat="server" RedirectAfterDataOperation="true"
        ConnectionString="<%$ ConnectionStrings:EvidenceConnectionString %>" 
        DeleteCommand="DELETE FROM [Computers] WHERE [PcId] = @PcId" 
        InsertCommand="INSERT INTO [Computers] ([IpAdress], [LocationId], [NumberPc], 
        [Domain], [SerialNumber], [OperationSystem], [Software], [Hardware], [UserName]) VALUES (@IpAdress, 
        @LocationId, @NumberPc, @Domain, @SerialNumber, @OperationSystem, @Software, @Hardware, @UserName)" 
        SelectCommand="SELECT * FROM [Computers] WHERE ([LocationId] = @LocationId) ORDER BY [IpAdress]"
        UpdateCommand="UPDATE [Computers] SET [IpAdress] = @IpAdress, [LocationId] = @LocationId, 
        [NumberPc] = @NumberPc, [Domain] = @Domain, [SerialNumber] = @SerialNumber, [OperationSystem] = @OperationSystem, 
        [Software] = @Software, [Hardware] = @Hardware, [UserName] = @UserName WHERE [PcId] = @PcId">
        <SelectParameters>
            <asp:QueryStringParameter Name="LocationId" QueryStringField="id" Type="Int32" />
        </SelectParameters>
        <DeleteParameters>
            <asp:Parameter Name="PcId" Type="Int32" />
        </DeleteParameters>
        <UpdateParameters>
            <asp:Parameter Name="IpAdress" Type="String" />
            <asp:QueryStringParameter Name="LocationId" QueryStringField="id" Type="Int32" />
            <asp:Parameter Name="NumberPc" Type="String" />
            <asp:Parameter Name="Domain" Type="String" />
            <asp:Parameter Name="SerialNumber" Type="String" />
            <asp:Parameter Name="OperationSystem" Type="String" />
            <asp:Parameter Name="Software" Type="String" />
            <asp:Parameter Name="Hardware" Type="String" />
            <asp:Parameter Name="Date" Type="DateTime" />
            <my:UserNameParameter Name="username" />
            <asp:Parameter Name="PcId" Type="Int32" />
        </UpdateParameters>
        <InsertParameters>
            <asp:Parameter Name="IpAdress" Type="String" />
            <asp:QueryStringParameter Name="LocationId" QueryStringField="id" Type="Int32" />
            <asp:Parameter Name="NumberPc" Type="String" />
            <asp:Parameter Name="Domain" Type="String" />
            <asp:Parameter Name="SerialNumber" Type="String" />
            <asp:Parameter Name="OperationSystem" Type="String" />
            <asp:Parameter Name="Software" Type="String" />
            <asp:Parameter Name="Hardware" Type="String" />
            
            <my:UserNameParameter Name="username" />
        </InsertParameters>
    </moje:RedirectingSqlDataSource>
    
    <asp:FormView ID="FormView1" runat="server" DataKeyNames="PcId" 
        DataSourceID="SqlDataSource2" DefaultMode="Insert">
       
        <InsertItemTemplate>
            <table>
                <tr>
                    <td>Ip adresa:</td>
                    <td><asp:TextBox ID="IpAdressTextBox" runat="server" 
                    Text='<%# Bind("IpAdress") %>' ValidationGroup="Insert" Width="80px" OnLoad="IpAdressTextBox_Load" />
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" 
                    ErrorMessage="*" ControlToValidate="IpAdressTextBox" ValidationGroup="Insert"></asp:RequiredFieldValidator>
                    tato položka musí být vyplněna</td>
                </tr>
                <tr>
                    <td>Číslo počítače:</td>
                    <td><asp:TextBox ID="NumberPcTextBox" runat="server" 
                    Text='<%# Bind("NumberPc") %>' ValidationGroup="Insert" Width="60px" />
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" 
                    ErrorMessage="*" ControlToValidate="NumberPcTextBox" ValidationGroup="Insert"></asp:RequiredFieldValidator>
                    tato položka musí být vyplněna</td>
                </tr>
                <tr>
                    <td>Doména:</td>
                    <td><asp:TextBox ID="DomainTextBox" runat="server" 
                    Text='<%# Bind("Domain") %>' Width="60px" />
                    
                    </td>
                </tr>
                <tr>
                    <td>Výrobní číslo:</td>
                    <td><asp:TextBox ID="SerialNumberTextBox" runat="server" 
                    Text='<%# Bind("SerialNumber") %>' Width="100px" />
                    
                    </td>
                </tr>
                <tr>
                    <td>Operační systém:</td>
                    <td><asp:TextBox ID="OperationSystemTextBox" runat="server" 
                    Text='<%# Bind("OperationSystem") %>' Width="120px" />
                  
                    </td>
                </tr>
                <tr>
                    <td>Software:</td>
                    <td><asp:TextBox ID="SoftwareTextBox" runat="server" 
                    Text='<%# Bind("Software") %>' Width="150px" TextMode="MultiLine" />
                    
                    </td>
                </tr>
                <tr>
                    <td>Hardware:</td>
                    <td><asp:TextBox ID="HardwareTextBox" runat="server" 
                    Text='<%# Bind("Hardware") %>' Width="150px" TextMode="MultiLine" />
                    
                    </td>
                </tr>
                
                <tr>
                    <td><asp:Button ID="InsertButton" runat="server" CausesValidation="True" 
                    CommandName="Insert" Text="Přidat počítač" ValidationGroup="Insert" />
                    </td>
                </tr>    
            </table>
        </InsertItemTemplate>
        
    </asp:FormView>

Jinak doplním, že nemám ostré SQL, ale pouze Express Edici 2008 ( pro naše účely plně postačuje ). Víte já bych potřeboval možná asi spíš napsat nějaký kód ve VB, který by ošetřil při Insertu, aby nejdříve zkontroloval zda tam již stejný záznam není. Za případnou odpověď předem děkuji.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Podle uvedeného kódu předpokládam, že používáte upravený SqlDataSource z tohoto článku http://www.vbnet.cz/clanek--112-zaciname... ? Komponenta je v článku upravená dost špatně, protože v událostech Inserted/Updated/Deleted ignoruje předaný parametr typu SqlDataSourceStatusEventArgs. Z tohoto důvodu aplikace "sežere" jakoukoliv chybu při zápisu do DB a uživatel nepozná, že se záznam nevložil. Doporučuju místo tohoto upraveného RedirectingSqlDataSource použít standardní SqlDataSource. Tím se začne zobrazovat chybová hláška, což už bude zlepšení.

Pro zobrazení upozornění na duplicitu bych použil CustomValidator, kde v serverové události "ServerValidate" ověříte, zda záznam již existuje. Taková úprava bude znamenat minimum úprav současného kódu a všechny validace budete mít dělané jednotným mechanismem.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Přesně tak máte pravdu mám to tam proto, abych měl nějak ošetřen refresh stránky. Dobře budu se řídit Vaším doporučením. Akorát nevím jak to přesně napsat sem :

<script runat="server">

Protected Sub CustomValidator1_ServerValidate(ByVal source As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)

    End Sub
</script>

a potom co zapsat do samotného TexBoxu zde ( nevím jestli je to tak správně, protože CustomValidator jsem ještě nikdy nepoužil )

<asp:TextBox ID="IpAdressTextBox" runat="server" 
     Text='<%# Bind("IpAdress") %>' ValidationGroup="Insert" Width="80px" />
<asp:CustomValidator ID="CustomValidator1" runat="server" 
     ErrorMessage="IP adresa v databázi existuje " ControlToValidate="IpAdressTextBox" ValidationGroup="Insert"                    OnServerValidate="CustomValidator1_ServerValidate"></asp:CustomValidator>

Je asi jasné, že jsem totální lama, ale budu moc vděčen za radu děkuji.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

1) V události ServerValidate je potřeba zjistit, zda zadaná IP v databázi existuje a podle výsledku nastavit args.IsValid.

Zhruba takto:

var ipAdressTextBox = (TextBox)FormView1.FindControl("IpAdressTextBox");
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionStringXY"].ConnectionString))
{
  using (var cmd = con.CreateCommand())
  {
    cmd.CommandText = "select count(*) from tabulkaComputers where IpAdress=@IpAdress";
    cmd.Parameters.AddWithValue("@IpAdress", ipAdressTextBox.Text);
    con.Open();
    var count = (long)cmd.ExecuteScalar();
    args.IsValid = (count == 0);
  }
}

Píšu v C#. Pro překlad do VB použijte třeba http://www.developerfusion.com/tools/con...

2) Do textboxu nic psát nemusíte. CustomValidator máte deklarovaný správně.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Děkuji moc super práce, funguje to skvěle. Díky díky moc !

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jé moc se omlouvám tak bohužel to teď nevloží žádný záznam.

Script mám napsán takhle:

    Protected Sub CustomValidator1_ServerValidate(ByVal source As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)
        Dim ipAdressTextBox = DirectCast(FormView1.FindControl("IpAdressTextBox"), TextBox)
        Using con = New SqlConnection(ConfigurationManager.ConnectionStrings("EvidenceConnectionString").ConnectionString)
            Using cmd = con.CreateCommand()
                cmd.CommandText = "select count(*) from Computers where IpAdress=@IpAdress"
                cmd.Parameters.AddWithValue("@IpAdress", ipAdressTextBox.Text)
                con.Open()
                Dim count = CLng(cmd.ExecuteScalar())
                args.IsValid = (count = 0)
            End Using
        End Using
    End Sub

Asi dělám někde chybu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jsem lama omlouvám se funguje to ( zastřelte mě ).

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Bohužel ani jedno z navrhovaných řešení (pokud jsem něco nepřehlédl) nefunguje spolehlivě. Mezi kontrolou, jestli IP adresa existuje, a vložením, může dojít k race condition. Databáze je vysoce konkurenční prostředí a to, že dvě vlákna budou chtít vložit stejnou IP, se stát může.

I když je to třeba v konkrétním případě nepravděpodobné, je vhodné to udělat správně, protože pokud to někdo použije pro podobný, ale trochu jiný případ, bude mít problém.

Nejlepší řešení je podle mě nic nekontrolovat a záznam vložit. Pokud dostanete výjimku, zachytit ji a zobrazit chybovou hlášku.

Anebo nějaké vlastní řešení typu Stored Procedura, která to zkusí vložit a má to v konstrukci try..catch, pokud vložení selže, zjistí důvod a indikuje to nějak (třeba pomocí return value).

Abyste bylo možné nejdřív zjistit, jestli záznam existuje, a případně jej vložit, je potřeba transakce s vysokým izolačním levelem (serializable, který si dělá range zámky na celou tabulku, aby vám během práce s ní dovnitř někdo něco nevložil).

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback