단일 트랜잭션에 LINQ-to-SQL 변경 및 ADO.NET 데이터 세트 테이블 어댑터 업데이트를 포함시킬 수 있습니까?
-
23-08-2019 - |
문제
내가 함께 일하는 관련 기술은 다음과 같습니다.
- Oracle에 대한 Devart의 DOT Connect (Oracle의 Linq-to-SQL을 용이하게하기 위해).
- 강력하게 입력 한 ado.net 데이터 세트.
- Oracle 데이터베이스.
다음은 도전입니다.
- 레거시 코드는 ado.net 데이터 세트 및 테이블 어댑터로 데이터베이스 업데이트를 제출합니다.
- 해당 코드를 LINQ-to-SQL로 변환하기 시작하고 싶지만 코드 휘트 및 위험을 최소화하기 위해 단편적으로 수행하고 싶습니다.
내 개념 증명 스키마는 다음과 같습니다.
부모 테이블
- parent.id
- 부모님 성함
어린이 테이블
- child.id
- child.parentid
- child.name
내 개념 증명 코드 블록은 다음과 같습니다.
using System;
using System.Data.Common;
using DevArtTry1.DataSet1TableAdapters;
namespace DevArtTry1
{
class Program
{
static void Main(string[] args)
{
using (DataContext1 dc = new DataContext1())
{
dc.Connection.Open();
using (DbTransaction transaction = dc.Connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
dc.Transaction = transaction;
Parent parent = new Parent();
parent.Id = 1;
parent.Name = "Parent 1";
dc.Parents.InsertOnSubmit(parent);
dc.SubmitChanges(); // By virtue of the Parent.Id -> Child.ParentId (M:N) foreign key, this statement will impose a write lock on the child table.
DataSet1.CHILDDataTable dt = new DataSet1.CHILDDataTable();
DataSet1.CHILDRow row = dt.NewCHILDRow();
row.ID = 1;
row.PARENTID = 1;
row.NAME = "Child 1";
dt.AddCHILDRow(row);
CHILDTableAdapter cta = new CHILDTableAdapter();
// cta.Transaction = transaction; Not allowed because you can't convert source type 'System.Data.Common.DbTransaction to target type 'System.Data.OracleClient.OracleTransaction.
cta.Update(dt); // The thread will encounter a deadlock here, waiting for a write lock on the Child table.
transaction.Commit();
}
}
Console.WriteLine("Successfully inserted parent and child rows.");
Console.ReadLine();
}
}
}
- 위의 의견에서 알 수 있듯이, 하위 데이터 어댑터의 업데이트 호출에서 스레드는 하위 테이블의 쓰기 잠금을 무기한 대기하기 때문에 스레드는 무기한 중단됩니다. [외국의 주요 관계에 주목하십시오 : parent.id-> child.parentid (m : n)
내 질문은 다음과 같습니다.
- 트랜잭션에서 전체 코드 블록을 마무리하고 싶습니다.
- 이걸 할 수 있나요? 고려해 보면:
- LINQ-to-SQL의 부모 테이블에 업데이트를 커밋하고 싶습니다. Submitchanges 방법...
- Ado.net 데이터 세트로 Child Table에 업데이트를 커밋하고 싶습니다. 테이블 어댑터.
다음은 두 가지 흥미로운 각주입니다.
- 이 모든 것들 공장 반대로. 즉, 데이터 어댑터와 함께 부모 테이블에 변경 사항을 제출하고 LINQ-to-SQL을 사용하여 하위 테이블을 변경하려면 ... 작동 할 것이다.
트랜잭션을 DataAdapter에 명시 적으로 첨부하려고했지만 컴파일러는 다른 유형의 트랜잭션이기 때문에 허용하지 않습니다.
CHILDTableAdapter cta = new CHILDTableAdapter(); cta.Transaction = transaction; // Not allowed because you can't convert source type 'System.Data.Common.DbTransaction' to target type 'System.Data.OracleClient.OracleTransaction'. cta.Update(dt); transaction.Commit();
해결책
나는 Oracle의 거래에 대해 아무것도 모른다. 그러나 Dotnet 측면에서는 거래를 직접 통제하는 것이 좋을 것이다. 두 기술 모두 동일한 연결 인스턴스를 사용하고 있는지 확인하십시오.
ORM을 통한 연결을 통해 트랜잭션을 제어하면 트랜잭션 범위를 사용합니다. http://msdn.microsoft.com/en-us/library/ms172152.aspx
다른 팁
이 두 가지 오류가 발생하여 같은 문제가있었습니다.
- 무결성 제약 위반 (ORA-02291)
- "키가 생성되지 않은 경우 동일한 키로 엔티티를 삽입 할 수 없습니다."
문제는 Child Object의 신원 열이 제대로 설정되지 않았다는 것입니다. DotConnect LINQ가 ID 키를 가정하지 않으면 객체 속성이 임시로 설정된 것으로 보이며 비 순위행 업데이트가 발생하여 무결성 위반으로 이어집니다.
다음은 수정 사항입니다.
- LINQ는 아동의 기본 키가 엔티티 키이며 자동 생성임을 알아야합니다.
- Oracle에서는 Child Object의 자동 증가 키를 설정하십시오.
먼저 시퀀스를 만듭니다.
DROP SEQUENCE MyChild_SEQ; CREATE SEQUENCE MyChild_SEQ MINVALUE 1 MAXVALUE 999999999999999999999999999 START WITH 1 INCREMENT BY 1 CACHE 20;
다음으로 OnInsert 트리거를 만듭니다.
CREATE OR REPLACE TRIGGER MyChild_AUTOINC BEFORE INSERT ON MyChildObject FOR EACH ROW BEGIN SELECT MyChild_SEQ.nextval INTO :NEW.MyChild_ID FROM dual; END MyChild_AUTOINC ; ALTER TRIGGER MyChild_AUTOINC ENABLE
새 자동 생성 된 기본 키를 통합하기 위해 스토리지 모델을 수정하십시오.
- DotConnect의 EntityDeveloper에서 LINQ 스토리지 모델 (.LQML 파일)을 엽니 다.
- Child Object의 엔티티 키를 '자동 생성 값'으로 설정하고 'OnInsert'로 자동 동기화됩니다.
- 스토리지 모델을 저장하고 Visual Studio에서 솔루션을 청소하고 재건하십시오.
- 자녀의 기본 키를 명시 적으로 설정하는 코드를 제거하십시오.
- LINQ는 이것을 자동으로 인식하여 인식하고 트리거 제작 ID를 검색합니다.
코드에서, 자식 객체를 만든 후 다음과 같이 부모에게 첨부하십시오.
ChildType newChild = new ChildType(); DataContext.InsertOnSubmit(newChild); Parent.Child = newChild;
추가 리소스는 다음과 같습니다.
- 기본 키 증가로 행을 삽입하십시오
- 계단식 시퀀스 ORA를 사용하여 엔티티 컨텍스트 저장에 문제가 있습니다
- 자동 증분 열 지원 (LinqConnect+Oracle)
건배!
TransactionScope 클래스를 사용하십시오.
다른 데이터베이스를 사용하는 경우 (또는 별도의 서버에 상주하는 경우) DTC 구성을 확인해야합니다.