2009-10-13 6 views
8

क्या कॉलम को गंदे के रूप में इलाज करने के लिए LINQ-to-SQL को मजबूर करने का कोई तरीका है? वैश्विक स्तर पर पर्याप्त होगा ....क्या आप कॉलम को हमेशा गंदा के रूप में मानने के लिए डेटा कॉन्टेक्स्ट को समझ सकते हैं?

मूल रूप से, मैं एक विरासत प्रणाली है कि मैं L2S के साथ बात कर रहा हूँ पर कुछ लेखा परीक्षा कोड के साथ एक समस्या है, कल्पना:

var ctx = new SomeDataContext(); // disposed etc - keeping it simple for illustration 
var cust = ctx.Customers.First(); // just for illustration 
cust.SomeRandomProperty = 17; // whatever 
cust.LastUpdated = DateTime.UtcNowl; 
cust.UpdatedBy = currentUser; 
ctx.SubmitChanges(); // uses auto-generated TSQL 

यह ठीक है, लेकिन एक ही उपयोगकर्ता इसे दो बार अपडेट हो जाता है, तो एक पंक्ति में, UpdatedBy एक एनओपी है, और TSQL हो जाएगा (लगभग):

UPDATE [dbo].[Customers] 
SET SomeRandomColumn = @p0 , LastUpdated = @p1 -- note no UpdatedBy 
WHERE Id = @p2 AND Version = @p3 

मेरे मामले में, समस्या यह है कि वर्तमान में एक बेल्ट और ब्रेसिज़ है सभी तालिकाओं पर ऑडिट ट्रिगर, जो यह देखने के लिए जांच करता है कि ऑडिट कॉलम अपडेट किया गया है, और यदि नहीं मानता है डेवलपर गलती पर है (SUSER_SNAME() को प्रतिस्थापित कर रहा है, हालांकि यह आसानी से एक त्रुटि फेंक सकता है)।

जो मैं वास्तव में करना चाहता हूं वह कहता है "हमेशा इस कॉलम को अपडेट करें, भले ही यह गंदा न हो" - क्या यह संभव है?

+0

मुझे यकीन नहीं है कि मैं प्रश्न (शायद नहीं) समझता हूं। आप इसका उपयोग नहीं कर सकते: आंशिक शून्य ऑनलोडेड() {this.UpdatedBy = currentUser; } तो यह हमेशा अपडेटेडबी सेट करता है? – Razzie

+0

यदि अपडेटेडबी को वास्तविक परिवर्तन के रूप में नहीं देखा जाता है (क्योंकि यह पिछली बार के समान मूल्य है), तो उस कॉलम के लिए 'अपडेट' नहीं होता है। –

+0

हम्म मैन्युअल रूप से इकाई को संलग्न करके ऐसा नहीं कर सका? पहले कभी नहीं किया, लेकिन मेरा मानना ​​है कि यह डिफ़ॉल्ट रूप से सभी कॉलम अपडेट करता है। Http://blogs.rev-net.com/ddewinter/2009/04/07/linq-to-sql-updating-entities/ विशेष रूप से दूसरा अध्याय देखें। – Razzie

उत्तर

8

KristoferA's answer के आधार पर, मैं जैसे कुछ के साथ समाप्त हुआ; यह बुराई और भंगुर है (प्रतिबिंब अक्सर होता है), लेकिन अब इसके लिए पर्याप्त होना पड़ सकता है। युद्ध के दूसरी तरफ ट्रिगर्स को व्यवहार करने के लिए बदलना है:

partial class MyDataContext // or a base-class 
{ 
    public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode) 
    { 
     this.MakeUpdatesDirty("UpdatedBy", "Updated_By"); 
     base.SubmitChanges(failureMode); 
    } 
} 
public static class DataContextExtensions 
{ 
    public static void MakeUpdatesDirty(
     this DataContext dataContext, 
     params string[] members) 
    { 
     if (dataContext == null) throw new ArgumentNullException("dataContext"); 
     if (members == null) throw new ArgumentNullException("members"); 
     if (members.Length == 0) return; // nothing to do 
     foreach (object instance in dataContext.GetChangeSet().Updates) 
     { 
      MakeDirty(dataContext, instance, members); 
     } 
    } 
    public static void MakeDirty(
     this DataContext dataContext, object instance , 
     params string[] members) 
    { 
     if (dataContext == null) throw new ArgumentNullException("dataContext"); 
     if (instance == null) throw new ArgumentNullException("instance"); 
     if (members == null) throw new ArgumentNullException("members"); 
     if (members.Length == 0) return; // nothing to do 
     const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; 
     object commonDataServices = typeof(DataContext) 
      .GetField("services", AllInstance) 
      .GetValue(dataContext); 
     object changeTracker = commonDataServices.GetType() 
      .GetProperty("ChangeTracker", AllInstance) 
      .GetValue(commonDataServices, null); 
     object trackedObject = changeTracker.GetType() 
      .GetMethod("GetTrackedObject", AllInstance) 
      .Invoke(changeTracker, new object[] { instance }); 
     var memberCache = trackedObject.GetType() 
      .GetField("dirtyMemberCache", AllInstance) 
      .GetValue(trackedObject) as BitArray; 

     var entityType = instance.GetType(); 
     var metaType = dataContext.Mapping.GetMetaType(entityType); 
     for(int i = 0 ; i < members.Length ; i++) { 
      var member = entityType.GetMember(members[i], AllInstance); 
      if(member != null && member.Length == 1) { 
       var metaMember = metaType.GetDataMember(member[0]); 
       if (metaMember != null) 
       { 
        memberCache.Set(metaMember.Ordinal, true); 
       } 
      } 
     } 
    } 
} 
1

दुर्भाग्य से, मुझे लगता है कि आप में एक नया DataContext

+0

'DataContext' मुद्दा नहीं है; यह केवल एक ही 'DataContext' से एकल सबमिट के साथ होगा; मेरा मतलब है कि डेटाबेस से जो लोड किया गया था, उससे नया मान नहीं बदला है, लेकिन मैं अभी भी इसे इस कॉलम सहित 'अपडेट' कथन जारी करना चाहता हूं। –

1

विवरण का उपयोग करना होगा: http://blog.benhall.me.uk/2008/01/custom-insert-logic-with-linq-to-sql.html

आप डिफ़ॉल्ट अद्यतन व्यवहार को ओवरराइड कर सकते हैं। ऐसा करने के 2 तरीके हैं

संग्रहित प्रक्रिया बनाना आसान है (यदि आप अपने डेटाबेस पर ऐसा नहीं कर सकते हैं, तो दूसरी विधि काम करनी चाहिए) जो आपके ग्राहक ऑब्जेक्ट के पैरामीटर लेती है और तालिका अपडेट करती है:

  1. संग्रहीत प्रक्रिया बनाएं जिसमें ग्राहकों की प्रत्येक संपत्ति के लिए पैरामीटर है जिसे अद्यतन करने की आवश्यकता है।
  2. संग्रहीत प्रक्रिया को अपने लिंक से SQL DBML फ़ाइल में आयात करें।
  3. अब आप अपने ग्राहकों की इकाई पर राइट क्लिक कर सकते हैं और "व्यवहार कॉन्फ़िगर करें" का चयन कर सकते हैं।
  4. कक्षा ड्रॉपडाउन के तहत अपने ग्राहक वर्ग का चयन करें और व्यवहार ड्रॉप डाउन पर "अपडेट करें" चुनें।
  5. "अनुकूलित करें" रेडियो बटन का चयन करें और आपके द्वारा अभी बनाई गई संग्रहीत प्रक्रिया का चयन करें।
  6. अब आप संग्रहित प्रक्रिया में कक्षा के गुणों को मैप कर सकते हैं।

अब जब लिंक से SQL आपकी ग्राहक तालिका को अपडेट करने का प्रयास करता है, तो यह आपके संग्रहीत प्रक्रिया का उपयोग करेगा। बस सावधान रहें क्योंकि यह हर जगह ग्राहकों के लिए अद्यतन व्यवहार को ओवरराइड करेगा।

दूसरी विधि आंशिक तरीकों का उपयोग करना है। मैंने वास्तव में यह कोशिश नहीं की है, इसलिए उम्मीद है कि यह आपको आगे बढ़ने के लिए कुछ सामान्य दिशा दे सकता है। अपने डेटा संदर्भ के लिए आंशिक कक्षा में, अद्यतन के लिए आंशिक विधि बनाएं (यह आपकी कक्षा के खाली होने के साथ अपडेट_____ होगा।मुझे यकीन है कि आप सही एक)

public partial SomeDataContext 
{ 
    partial void UpdateCustomer(Customer instance) 
    { 
     // this is where you'd do the update, but I'm not sure exactly how it's suppose to work, though. :(
    } 
} 
+0

हां, यह पहले से ही मेरा बैकअप था - इस परिदृश्य के लिए मैं * उम्मीद कर रहा था * इनबिल्ट टीएसक्यूएल पीढ़ी का उपयोग करके इसे छोड़ने के लिए ... –

1

मिल आप, आप की तर्ज पर कुछ की कोशिश कर सकते [गंदा] प्रतिबिंब मार्ग नीचे जाना चाहते हैं बनाने के लिए अपने डेटा को संदर्भ के डिजाइनर फ़ाइल में खोज सुझाव देंगे:

1) अवहेलना SubmitChanges
2) परिवर्तन सेट प्रत्येक अद्यतन वस्तु के लिए परिवर्तन पर नजर की पकड़ पाने के लिए
3) का प्रयोग करें प्रतिबिंब के माध्यम से जाओ (What's the cleanest way to make a Linq object "dirty"? देखें)
4) स्तंभ गंदा करें (वहाँ एक dirtyMemberCache क्षेत्र है StandardTrackedObject क्लास में)

+1

ओह, शुद्ध अपर्याप्त बुराई। मुझें यह पसंद है। बहुत; धन्यवाद। –

+0

सच है, अंधेरे तरफ मजबूत है यह एक है ... यदि केवल एमएसएफटी कुछ उपयोगी वर्गों और सदस्यों को सार्वजनिक छोड़ देता, तो यह एक और अधिक साफ तरीके से किया जा सकता है ... :) – KristoferA

0

मेरे लिए निम्नलिखित कार्य करता है। नोट करें कि मैं DevArt से linq2sql प्रदाता का उपयोग कर रहा है, लेकिन उस बात नहीं कर सकते:

MyDataContext dc = new MyDataContext(); 

Message msg = dc.Messages.Single(m => m.Id == 1); 
Message attachingMsg = new Message(); 
attachingMsg.Id = msg.Id; 

dc.Messages.Attach(attachingMsg); 

attachingMsg.MessageSubject = msg.MessageSubject + " is now changed"; // changed 
attachingMsg.MessageBody = msg.MessageBody; // not changed 
dc.SubmitChanges(); 

यह निम्नलिखित एसक्यूएल का उत्पादन:

UPDATE messages SET messageSubject = :p1, messageBody = :p2 WHERE Id = :key1 

तो, messageBody भी अद्यतन किया जाता है, हालांकि अपने मूल्य नहीं है बदला हुआ। इसके लिए आवश्यक एक अन्य परिवर्तन यह है कि मेरी इकाई संदेश की प्रत्येक संपत्ति (कॉलम) के लिए, मैंने UpdatedCheck = UpdateCheck.Never सेट किया है, इसके आईडी को छोड़कर, जो प्राथमिक कुंजी है।

+0

यह एक त्रुटि फेंकना चाहिए कह रहे हैं कि आप एक आईडी के साथ एक संदेश संलग्न करने की कोशिश कर रहे हैं जो पहले से मौजूद है। –

संबंधित मुद्दे

 संबंधित मुद्दे