2012-03-14 9 views
10

मैं पहले की इकाई को अद्यतन नहीं कर सकता। मैं संदेश के साथ एक StaleObjectException अपवाद हो रही है:अद्यतन इकाइयों को कैसे संभालें। एनएचबीर्नेट + एएसपी.नेट एमवीसी

Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Project.DomainLayer.Entities.Employee#00000000-0000-0000-0000-000000000000]

मैं किसी के साथ अद्यतन प्रक्रिया का हिस्सा नहीं है। समस्या क्या है?

डेटा एक्सेस/डि

public class DataAccessModule : Ninject.Modules.NinjectModule 
{ 
    public override void Load() 
    { 
     this.Bind<ISessionFactory>() 
      .ToMethod(c => new Configuration().Configure().BuildSessionFactory()) 
      .InSingletonScope(); 

     this.Bind<ISession>() 
      .ToMethod(ctx => ctx.Kernel.TryGet<ISessionFactory>().OpenSession()) 
      .InRequestScope(); 

     this.Bind(typeof(IRepository<>)).To(typeof(Repository<>)) 
      .InRequestScope(); 
    } 
} 

डेटा एक्सेस/मैपिंग

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Project.DomainLayer" namespace="Project.DomainLayer.Entities"> 
<class name="Employee" optimistic-lock="version"> 
    <id name="ID" column="EmployeeID" unsaved-value="00000000-0000-0000-0000-000000000000"> 
     <generator class="guid.comb" /> 
    </id> 
    <version name="Version" type="Int32" column="Version" /> 
    <!-- properties --> 
    <property name="EmployeeNumber" /> 
    <!-- ... --> 
    <property name="PassportRegistredOn" not-null="true" /> 
    <!-- sets --> 
    <set name="AttachedInformation" cascade="all"> 
     <key column="EmployeeID" /> 
     <element column="Attachment" /> 
    </set> 
    <set name="TravelVouchers" cascade="all"> 
     <key column="EmployeeID" /> 
     <one-to-many class="TravelVoucher" /> 
    </set> 
    </class> 
</hibernate-mapping> 

डेटा एक्सेस/भंडार

public class Repository<T> : IRepository<T> where T : AbstractEntity<T>, IAggregateRoot 
{ 
    private ISession session; 

    public Repository(ISession session) 
    { 
     this.session = session; 
    } 

    // other methods are omitted 

    public void Update(T entity) 
    {    
     using(var transaction = this.session.BeginTransaction()) 
     { 
      this.session.Update(entity); 
      transaction.Commit(); 
     } 
    } 
    public void Update(Guid id) 
    {    
     using(var transaction = this.session.BeginTransaction()) 
     { 
      this.session.Update(this.session.Load<T>(id)); 
      transaction.Commit(); 
     } 
    } 
} 

एक नियंत्रक

public class EmployeeController : Controller 
{ 
    private IRepository<Employee> repository; 

    public EmployeeController(IRepository<Employee> repository) 
    { 
     this.repository = repository; 
    }   
    public ActionResult Edit(Guid id) 
    { 
     var e = repository.Load(id); 
     return View(e); 
    } 
    [AcceptVerbs(HttpVerbs.Post)] 
    public ActionResult Edit(Employee employee) 
    { 
     if(ModelState.IsValid) 
     { 
      repository.Update(employee); 
      return RedirectToAction("Deatils", "Employee", new { id = employee.ID }); 
     } 
     else 
     { 
      return View(employee); 
     } 
    } 
} 

मैं अपने संस्थाओं को कैसे अपडेट कर अंदर? धन्यवाद!

संपादित

तो मैं अपने मार्कअप को unsaved-value="{Guid.Empty goes here}" गयी। इसके अलावा मैं अगली बात करने की कोशिश की है:

public void Update(T entity) 
{ 
    using(var transaction = this.session.BeginTransaction()) 
    { 
     try 
     { 
      this.session.Update(entity); 
      transaction.Commit(); 
     } 
     catch(StaleObjectStateException ex) 
     { 
      try 
      { 
       session.Merge(entity); 
       transaction.Commit(); 
      } 
      catch 
      { 
       transaction.Rollback(); 
       throw; 
      } 
     } 

    } 
} 

और यह मुझे एक ही प्रभाव देता है .. मेरा मतलब है transaction.Commit();Merge के बाद एक ही अपवाद देता है।

इसके अलावा मुझे आश्चर्य है कि छिपे हुए इनपुट का उपयोग करके, पर Edit दृश्य पर मुझे खुलासा करना चाहिए?

संपादित

तो इकाई वास्तव में अलग हो जाता है। जब यह नियंत्रक को पास करता है IDGuid.Empty के बराबर होता है। मैं इसे कैसे संभालूं, Merge या Reattach?

+0

क्या आप जेनरेट किए गए कुछ एसक्यूएल पोस्ट कर सकते हैं? साथ ही, क्या आपने सत्यापित किया है कि कर्मचारियों की इकाई के पास पोस्ट पर एक आईडी है? – shanabus

उत्तर

9

आपके कोड पैटर्न दिए गए दो परिदृश्य हैं जिन्हें आप चला सकते हैं।

1) आप ऑब्जेक्ट का उपयोग कर डीबी से ऑब्जेक्ट पुनर्प्राप्त कर सकते हैं। प्राप्त करें जिसे पुनर्प्राप्त ऑब्जेक्ट में परिवर्तन/अपडेट के बाद किया जा सकता है। इस परिवर्तन को प्रभावी बनाने के लिए, आपको केवल सत्र को फ्लश करना होगा या लेनदेन करना होगा क्योंकि निबर्ननेट स्वचालित रूप से आपके लिए सभी परिवर्तनों को ट्रैक करेगा।

2) आपके पास एक क्षणिक उदाहरण है, एक ऑब्जेक्ट जो संदर्भ में ISession से संबद्ध नहीं है, जिससे आप अपडेट करना चाहते हैं। इस मामले में, मेरे अनुभव से, सबसे अच्छा अभ्यास ISession के लिए है। ऑब्जेक्ट प्राप्त करें और उस ऑब्जेक्ट में संबंधित परिवर्तन करें जो आप अभी पुनर्प्राप्त करते हैं। (आमतौर पर आपका व्यू मॉडल आपके डोमेन मॉडल से अलग होता है, दोनों को मिश्रण न करें) यह पैटर्न नीचे दिखाया गया है। यह हर समय काम करता है। सुनिश्चित करें कि आप ISession.SaveOrUpdate का भी उपयोग करें।

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Employee employee) 
{ 
    if(ModelState.IsValid) 
    { 
     var persistentEmployee = repository.Get(employee.Id); 
     if( persistentEmployee == null){ 
      throw new Exception(String.Format("Employee with Id: {0} does not exist.", employee.Id)); 
     } 
     persistentEmployee.Name = employee.Name; 
     persistentEmployee.PhoneNumber = employee.PhoneNumber; 
     //and so on 
     repository.Update(persistentEmployee); 
     return RedirectToAction("Deatils", "Employee", new { id = employee.ID }); 
    } 
    else 
    { 
     return View(employee); 
    } 
} 

इसके अलावा, सूचना है कि आपके नियंत्रक शायद एक अनुरोध के अनुसार आधार पर instantiated है, इसलिए, अपने ISession के जीवनकाल विभिन्न तरीकों आप अपने नियंत्रक में है करने के लिए कई कॉल अवधि नहीं है। दूसरे शब्दों में, हर विधि लगभग हमेशा एक नए ISession (काम की इकाई) के संदर्भ में काम कर रही है।

+0

ऐसा लगता है कि मैं हमेशा 'id == guid.Empty' के साथ 'कर्मचारी' पुनर्प्राप्त करूँगा? – lexeme

+0

जब आप ऑब्जेक्ट को अपडेट करते हैं, तो आईडी GUID नहीं होनी चाहिए। लक्षण। केवल तभी जब आप नई वस्तु को बनाने/सहेजने की प्रक्रिया में हैं तो यह होगा। आईडी या तो आपके ऐप द्वारा डेटाबेस या डेटाबेस द्वारा उत्पन्न किया जा सकता है। [Nhibernate दस्तावेज़ीकरण] देखें (http://www.nhforge.org/doc/nh/en/index.html#mapping-declaration-id- जनरेटर) – Newbie

+0

नीचे मेरा उत्तर देखें। जिस तरह से यह काम करता है। सलाह की सराहना की जाती है। – lexeme

1

"सहेजे गए मान" गायब हैं। इसलिए राष्ट्रीय राजमार्ग सोचता है कि Guid.Empty एक मान्य आईडी

<id name="ID" column="EmployeeID" unsaved-value="0000000-0000-0000-0000-000000000000"> 
+0

इससे मदद नहीं मिलती है ( – lexeme

1

है आप कुछ इकाई के फ़ील्ड अपडेट कर आप पास लेन-देन से पहले session.Update(), उपयोग session.Flush() का उपयोग करने की आवश्यकता नहीं करना चाहते हैं।

सत्र। अद्यतन() -> दिए गए क्षणिक उदाहरण के पहचानकर्ता के साथ लगातार उदाहरण अपडेट करें।

2

मुझे विश्वास है कि आपकी कर्मचारी वस्तु बन गई है जो एनएचबीर्नेट ने आपके संपादन क्रिया विधियों के जीईटी और पोस्ट के बीच "अलग" कहा है। अधिक जानकारी और कुछ समाधानों के लिए इस विषय पर NHibernate documentation देखें। असल में, लिंक सटीक जीईटी-पोस्ट परिदृश्य का वर्णन करता है जिसका आप उपयोग कर रहे हैं।

आपको अपने कर्मचारी ऑब्जेक्ट को दोबारा जोड़ना होगा और/या "असुरक्षित मूल्य" निर्दिष्ट करना होगा क्योंकि फिरो ने सुझाव दिया था कि एनएचबीर्नेट एक कर्मचारी को GUID की आईडी के साथ जानता है। अभी तक डेटाबेस को नहीं रखा गया है। अन्यथा, जैसा कि फिरो ने सुझाव दिया था, NHHernate Guid.Empty को मान्य आईडी के रूप में देखता है, और सोचता है कि ऑब्जेक्ट पहले ही डेटाबेस में सहेजा गया है लेकिन जिस सत्र में इसे पुनर्प्राप्त किया गया था उसे छोड़ दिया गया है (इसलिए, वस्तु "अलग हो रही है")।

उम्मीद है कि इससे मदद मिलती है।

3

आपका तर्क अच्छा नहीं है, क्योंकि आप डोमेन मॉडल जैसे व्यूमोडेल के रूप में डोमेन मॉडल का उपयोग करते हैं। सर्वश्रेष्ठ अभ्यास CreateEmploeeViewModel और EditEmployeeViewModel और अलग डोमेन तर्क और मॉडल मॉडल तर्क का उपयोग करना है। उदाहरण के लिए:

public class Employee 
{ 
     public virtual int Id { get; set; } 

     public virtual string FirstName { get; set; } 

     public virtual string LastName { get; set; } 

     public virtual string MiddleName { get; set; } 
} 

public class CreateEmployeeViewModel 
{ 
     public virtual string FirstName { get; set; } 

     public virtual string LastName { get; set; } 

     public virtual string MiddleName { get; set; } 
} 

public class EditEmployeeViewModel : CreateEmployeeViewModel 
{ 
     public virtual int Id { get; set; } 
} 

ViewModel करने के लिए कर्मचारी से परिवर्तित करने के लिए मैं यो उपयोग Automapper पसंद करते हैं।

तो नियंत्रक क्रिया की तरह दिखता हो:

[HttpGet] 
    public virtual ActionResult Edit(int id) 
    { 
     Employee entity = GetEntityById(id); 
     EmployeeEditViewModel model = new EmployeeEditViewModel(); 

     Mapper.Map(source, destination);    

     return View("Edit", model); 
    } 

    [HttpPost] 
    public virtual ActionResult Edit(EmployeeEditViewModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      Employee entity = GetEntityById(model.Id); 

      entity = Mapper.Map(model, entity);    
      EntitiesRepository.Save(entity); 

      return GetIndexViewActionFromEdit(model); 
     }   

     return View("Edit", model); 
    } 

इस मामले NHibernate जानता है कि आप कर्मचारी अपडेट करने और नहीं कर सकते कुछ गुण है जो आपके दृश्य में मौजूद नहीं हटाने में।

+0

आपने ओप 'प्रश्न के बिंदु को याद किया है। आपका उत्तर खाते में समेकन नहीं लेता है। – Chev

+0

हालांकि आप सही हैं, डोमेनमोडेल बनाम व्यूमोडेल अच्छा है। लेकिन इस सवाल का कोई संबंध नहीं है। –

+0

यदि आप anwser को देखते हैं तो आप देख सकते हैं कि समाधान क्या है। विकल्प 2. –

2

आप पूछ,

इसके अलावा, मैं मैं का पर्दाफाश करना चाहिए सोच रहा हूँ, छिपे हुए इनपुट, दृश्य संपादित पर इकाई आईडी का उपयोग कर?

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

आप इकाई को फिर से लोड करके और मैपिंग करके इसे हमेशा प्राप्त कर सकते हैं, यह सुनिश्चित कर सकते हैं कि संस्करण मान मिलान होने की संभावना है, लेकिन ऐसा लगता है कि इसका उद्देश्य घट गया है।

आईएमएचओ, मैं एक छिपी हुई इनपुट में इकाई आईडी और संस्करण डालता हूं, और पोस्टबैक पर, इकाई को फिर से लोड करता हूं और डेटा को मैप करता हूं। इस तरह, Ivan Korytin जैसा ऊपर बताया गया है, आपको उन गुणों को ले जाना नहीं होगा जिन्हें आपके दृश्य में आवश्यक नहीं है। आप नियंत्रक स्तर पर स्थिरता को भी संभाल सकते हैं और NHBernate आपको बताएंगे कि आपकी ऑब्जेक्ट बासी है, इसकी बजाय सत्यापन त्रुटि जोड़ें।

Ivan Korytin किसी इकाई के सरल संपादन को संभालने के लिए मानक प्रक्रिया की रूपरेखा तैयार करता है। उनके जवाब के साथ एकमात्र मुद्दा यह है कि यह संस्करण संपत्ति को संबोधित नहीं करता है। आईएमएचओ, डेटाबेस को संस्करणित नहीं किया जाना चाहिए, या संस्करण संपत्ति को महत्व देना चाहिए।

+0

ठीक है। तो संपादन/अद्यतन करते समय इकाइयों को अलग-अलग व्यूमोडल्स के साथ 'ऑटोमैपर' का उपयोग नहीं करना होगा? अन्य 3 डी पार्टी सॉफ़्टवेयर का उपयोग करना अधिक जटिल बनाता है। मैं क्या जानना चाहता हूं कि इकाइयों को अलग किए बिना 'nhibernate' के साथ संपादन/अद्यतन कैसे करें। – lexeme

+0

प्रक्रिया जो अंततः विचलन का कारण बनती है वह यह है कि आप एक वेब वातावरण में हैं, डेस्कटॉप वातावरण नहीं। मान लीजिए कि आप ASP.Net का उपयोग कर रहे हैं एमवीसी, और आपका उपयोगकर्ता एक संपादन फॉर्म में बदलाव करता है और सबमिट बटन पर क्लिक करता है। मॉडल बाइंडर एक से जुड़ने जा रहा है "मॉडल" (इस मामले में, आपकी डोमेन ऑब्जेक्ट) जिसमें एक शून्य कन्स्ट्रक्टर है और उसे वापस पोस्ट किए गए आधार पर "मॉडल ऑब्जेक्ट" के मान असाइन करने के लिए सर्वोत्तम प्रयास करें। – brightgarden

+0

यदि आप समाधान ढूंढने पर पूरी तरह से जोर दे रहे थे जिसमें परिणामी मॉडल ऑब्जेक्ट पूरी तरह से संलग्न इकाई है, तो मुझे यकीन है कि एक तरीका है, लेकिन यह केवल अकादमिक अर्थ में ही दिलचस्प होगा। सॉफ्टवेयर बनाने और सॉफ्टवेयर को कुशलतापूर्वक प्रदान करने और कोड आधार को बनाए रखने के मामले में जो बाद में आने वाले अन्य डेवलपर्स के लिए आसान है, मानक दृष्टिकोणों का पालन करना बहुत बुद्धिमान है। किसी तृतीय पक्ष automapper की कोई आवश्यकता नहीं है: आप गुणों को मैन्युअल रूप से मैप कर सकते हैं, और उस दृष्टिकोण के बारे में कुछ भी अमान्य नहीं है। – brightgarden

0

सब के बाद यह मदद करता है, लेकिन मुझे लगता है यह भयानक है: यहां तक ​​कि अगर मैं ID (और Version) आईडी पारित किया जा रहा साधारण एक Guid.Empty जिसमें कहा गया है employee है के लिए Edit दृश्य पर HiddenFor क्षेत्रों डाल

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Guid id, Employee employee) 
{ 
    if(ModelState.IsValid) 
    { 
     var e = repository.Get(id); 

     if(Guid.Empty != e.ID) 
     { 
      e.Department = employee.Department; 
      repository.Update(employee.ID); 
      return RedirectToAction("Details", "Employee", new { id = e.ID }); 
     } 
     /*...*/ 
    } 
} 

क्षणिक।

मैं वास्तव में आपकी मदद लोगों के लिए सराहना करता हूं!

प्रश्न

I know what viewmodels are, but quite not understood how does it help with detaching

Why if I put TextBoxFor(e => e.ID) on Edit view it binds employee like a transient entity without saving the ID value?

+0

हम्म। हो सकता है कि आपको इसे एक अलग प्रश्न के रूप में रखना चाहिए क्योंकि @Newbie ने पहले ही मूल समस्या हल कर दी है। – DashK

+0

@helicera सवाल में अपने प्रश्न की सभी सामग्री को रखने की कोशिश करता है। यह यहां शिष्टाचार का हिस्सा है और सभी के लिए आपको प्रश्न समझना आसान है। वैसे भी, आप अपने पहचानकर्ता (आईडी) कैसे बना रहे हैं? – Newbie

1

आप हम में से एक यह है कि यहां से कोई जवाब नहीं मदद की कर रहे हैं, की कोशिश की तलाश में एक "आईडी" अपने इकाई में क्या भेज रहा है।

मुझे एक ही समस्या है लेकिन अंत में, मैंने देखा कि मैं आईडी को दूसरे नंबर पर बदल रहा था (एनएचबर्ननेट में आईडी स्वयं उत्पन्न होगी, यदि आप इसे इस तरह से सेट करते हैं!)।

तो, लाइन के नीचे, जांचें कि आपके द्वारा भेजे जा रहे डेटा की संरचना और मूल्य, जो आप भेज रहे हैं उससे मेल खाते हैं।

आशा है कि मैं किसी की भी मदद कर सकता हूं! :)