2011-05-07 15 views
7

में प्राथमिक कुंजी को मैन्युअल रूप से उत्पन्न करने का सबसे अच्छा तरीका क्या है एंटीटी फ्रेमवर्क 4.1 कोड में प्राथमिक कुंजी मैन्युअल रूप से उत्पन्न करने का सबसे अच्छा तरीका क्या है?एंटीटी फ्रेमवर्क 4.1 कोड प्रथम

मैं एएसपी.नेट एमवीसी 3 प्रोग्रामिंग कर रहा हूं और मैं एक भंडार पैटर्न का उपयोग करता हूं।

'Code First Class 
Public Class Foo 
    <Key()> 
    <DatabaseGenerated(DatabaseGeneratedOption.None)> 
    Public Property iId As Integer 

    Public Property sBar As String 
End Class 

'Context Class 
Public Class FooBarContext : Inherits DbContext 
    Public Property Foos As DbSet(Of Foo) 
End Class 

'Get the current Id 

'Part of code in repository that stores Entity Foo. 
Dim iCurrId as Integer = (From io In context.Foo 
         Select io.iId()).Max 

Dim iNewId as Integer = iCurrId + 1 

Foo.iId = iNewId 

मेरे समूह है (हालांकि संभावना नहीं) है कि दो (या अधिक) उन एक ही समय में एक इकाई फू को बचाने की कोशिश करेंगे:

मैं वर्तमान में नीचे दिए गए कोड का उपयोग करके एक अनुक्रमिक क्रम में कुंजी उत्पन्न और इसलिए एक ही आईडी प्राप्त होगी और सम्मिलन विफल हो जाएगा।

क्या यह एक अच्छा तरीका है, या कोई बेहतर है?

कृपया यह नहीं कि मैं डेटाबेस उत्पन्न पहचान फ़ील्ड का उपयोग नहीं कर सकता (और नहीं)!

+0

आपको पहचान का उपयोग क्यों नहीं करना है? –

उत्तर

2

यहाँ मैं का उपयोग कर समाप्त हो गया है:

अब आप ही इससे पहले कि आप रिकॉर्ड को बचाने के लिए जा रहे हैं एक मूल्य प्राप्त करने के लिए संग्रहीत प्रक्रिया कॉल करने के लिए की जरूरत है। यह कोड Ladislav Mrnka द्वारा पोस्ट पर आधारित है, लेकिन डीबीकॉन्टेक्स्ट के साथ काम करने के लिए संशोधित है।

अनुक्रम जानकारी संग्रहीत करने के लिए मॉडल (इसे अपने संदर्भ में डीबीएसटी के रूप में जोड़ने के लिए मत भूलना)।

<Table("tSequences")> 
Public Class Sequence 
    <Key()> 
    <DatabaseGenerated(DatabaseGeneratedOption.None)> 
    <Display(Name:="Model name", Order:=1)> 
    Public Property sModelName As String 

    <Required()> 
    <Display(Name:="Current Primary key value", AutoGenerateField:=False, Order:=2)> 
    Public Property iCurrentPKeyValue As Integer 
End Class 

डेटाबेस को निष्क्रिय करें और प्राप्त करने और स्वत: वृद्धि अनुक्रमों के लिए एक संग्रहित प्रक्रिया बनाएं।

Public Class DBInitializer 
    Inherits CreateDatabaseIfNotExists(Of Context) 

    Protected Overrides Sub Seed(context As Context) 
     'Create stored procedure to hold 
     Dim sStoredProcSQL As String = "CREATE PROCEDURE [dbo].[spGetNextSequenceValue]" & vbCrLf & _ 
             "@sModelName VARCHAR(30)" & vbCrLf & _ 
             "AS BEGIN" & vbCrLf & _ 
             "DECLARE" & vbCrLf & _ 
             "@Result INT" & vbCrLf & _ 
             "UPDATE [dbo].[tSequences] WITH (ROWLOCK, UPDLOCK)" & vbCrLf & _ 
             "SET @Result = iCurrentPKeyValue = iCurrentPKeyValue + 1" & vbCrLf & _ 
             "WHERE sModelName = @sModelName" & vbCrLf & _ 
             "RETURN @Result" & vbCrLf & 
             "END" 

     context.Database.ExecuteSqlCommand(sStoredProcSQL) 
    End Sub 
End Class 

संग्रहित प्रक्रिया चलाकर एंटीटी फू के लिए एक नई कुंजी (iNewKey) प्राप्त करें।

Dim iNewKey As Integer 

Using scope = New TransactionScope(TransactionScopeOption.RequiresNew, New TransactionOptions() With { _ 
    .IsolationLevel = IsolationLevel.ReadCommitted _ 
    }) 
    iNewKey = context.Database.SqlQuery(Of Integer)("DECLARE @return_value int" & vbCrLf & _ 
                "EXEC @return_value = [dbo].[spGetNextSequenceValue]" & vbCrLf & _ 
                "@sModelName = 'Foo'" & vbCrLf & _ 
                "SELECT 'Return Value' = @return_value").ToList().First() 
'Indicate that all operations are completed. 
    scope.Complete() 

    context.SaveChanges() 
End Using 
0

क्या आप एक आईएनटी के बजाय GUID का उपयोग कर सकते हैं? यदि ऐसा है, तो आप केवल

System.Guid.NewGuid().ToString() 

यदि नहीं, तो आपको उसी आईडी का उपयोग करके दो प्रविष्टियों से बचने के लिए थ्रेड या तालिका को लॉक करना होगा।

+0

मैं ग्रिड का उपयोग नहीं कर सकता। क्या आप थ्रेड या टेबल को लॉक करने का कोड नमूना प्रदान कर सकते हैं? – Thomas

+1

यह एक व्यापक विषय है, Google इसके बारे में सीखने पर आपका मित्र है। आप स्तंभ को अनन्य भी बना सकते हैं (यदि यह पहले से नहीं है), और डालने पर त्रुटि के लिए देखें। यदि दो धागे एक बार में प्रयास करते हैं, तो आप उनमें से एक पर एक त्रुटि प्राप्त करेंगे। त्रुटि वाला कोई भी अगले पूर्णांक के साथ फिर से प्रयास कर सकता है जब तक कि यह सफल न हो जाए। यह अच्छा डिजाइन नहीं है, लेकिन यह काम करेगा। – Mikecito

5

आपकी चिंता मान्य है - अक्सर उपयोग की जाने वाली वेबसाइट में यह होने की संभावना अधिक होगी और समाधान बहुत आसान नहीं है। आप क्लाइंट साइड Guid का उपयोग @Mikecito द्वारा वर्णित अनुसार कर सकते हैं लेकिन इसमें महत्वपूर्ण प्रदर्शन हिट है और मुझे लगता है कि आप इसका उपयोग नहीं करना चाहते हैं।

इस समय आप जिस तरह से कर रहे हैं वह बहुत खराब है क्योंकि एकमात्र समाधान आपके कोड को एकल धारावाहिक लेनदेन में लपेटना है - लेन-देन में आईडी का चयन करना और रिकॉर्ड को सहेजना शामिल होना चाहिए। इससे आपके InventoryObjects अनुक्रमिक तक पहुंच होगी क्योंकि लेन-देन पूरा होने तक प्रत्येक चयन अधिकतम पूरी तालिका को लॉक कर देगा - कोई भी सम्मिलित लेनदेन के दौरान तालिका में डेटा को पढ़ने या लिखने में सक्षम नहीं होगा। इसे शायद ही कभी देखी गई साइट में कोई समस्या नहीं होनी चाहिए लेकिन यह अक्सर देखी गई साइट पर कोई भी नहीं हो सकती है। आपके वर्तमान सेट अप में इसे अलग करने का कोई तरीका नहीं है।

आंशिक सुधार अधिकतम मूल्य प्राप्त करने के लिए अलग-अलग तालिका का उपयोग कर रहा है ताकि अगले मूल्य प्राप्त हो सके और परमाणु संचालन में संग्रहीत मूल्य को बढ़ाया जा सके - (यह वास्तव में ओरेकल से अनुक्रमों को अनुकरण करता है)। अब केवल एक ही जटिलता है यदि आपको अंतराल के बिना अनुक्रम की आवश्यकता है। उदाहरण के लिए यदि नया InventoryObject की बचत के साथ कुछ गलत हो जाता है तो चयनित आईडी खो जाएगा और यह आईडी के अनुक्रम में एक अंतर बनाएगा। यदि आपको अंतराल के बिना अनुक्रम की आवश्यकता है तो आपको अगली आईडी प्राप्त करने और रिकॉर्ड को सहेजने के लिए फिर से लेनदेन का उपयोग करना होगा, लेकिन इस बार आप केवल अनुक्रम तालिका में एकल रिकॉर्ड लॉक करेंगे। अनुक्रम रिकॉर्ड से आईडी को पुनर्प्राप्त करना अनुक्रम रिकॉर्ड लॉक होने पर समय को कम करने के लिए जितना संभव हो सके परिवर्तनों को सहेजने के करीब होना चाहिए। - आप इसे सीधे का उपयोग नहीं होगा

CREATE TABLE [dbo].[Sequences] 
(
    [SequenceType] VARCHAR(20) NOT NULL, /* Support for multiple sequences */ 
    [Value] INT NOT NULL 
) 

CREATE PROCEDURE [dbo].[GetNextSequenceValue] 
    @SequenceType VARCHAR(20) 
AS 
BEGIN 
    DECLARE @Result INT 

    UPDATE [dbo].[Sequences] WITH (ROWLOCK, UPDLOCK) 
    SET @Result = Value = Value + 1 
    WHERE SequenceType = @SequenceType 

    RETURN @Result 
END 

तालिका पहले कोड से मैप किया जा करने की जरूरत नहीं:

यहाँ एसक्यूएल सर्वर के लिए अनुक्रम मेज और अनुक्रम प्रक्रिया के नमूना है। जब ईएफ डेटाबेस बनाता है तो आपको तालिका और संग्रहीत प्रक्रिया जोड़ने के लिए कस्टम डेटाबेस प्रारंभकर्ता बनाना होगा। आप described here के समान दृष्टिकोण का प्रयास कर सकते हैं। आपको प्रारंभ मूल्य के साथ अनुक्रम के लिए प्रारंभिक रिकॉर्ड भी जोड़ना होगा।

// Prepare and insert record here 

// Transaction is needed only if you don't want gaps 
// This whole can be actually moved to overriden SaveChanges in your context 
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, 
    new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted })) 
{ 
    record.Id = context.Database.ExecuteStoreCommand("dbo.GetNextSequenceValue @SequenceType", 
     new SqlParameter("SequenceType", "InventoryObjects")); 
    context.SaveChanges(); 
} 
संबंधित मुद्दे