2010-04-21 3 views
0

WPF/VB에서 응용 프로그램을 작성하고 비즈니스 논리와 UI를 다른 프로젝트로 구분했습니다.Dispatcher.CheckAccess()가 내 콘솔 응용 프로그램에서 작동하지 않습니다. 더 좋은 방법이 있습니다.

비즈니스 계층은 다른 스레드에서 실행되는 직렬 포트를 사용합니다. 이제 동일한 비즈니스 계층에 대한 명령 줄 인터페이스를 작성하려고 했으므로 .Invoke()가 호출 될 때 오류가 발생하는 것 같습니다. (오류 없음, 그냥 작동하지 않습니다)

나는 checkaccess 및 .invoke에 추가해야하는 이유는 분명히 알았습니다. 직렬 포트 데이터를 처리하는 동안 변경 될 수있는 콜렉션이 있었고 NotifyCollectionChanged를 WPF 데이터 바인딩으로 처리 할 수 ​​있습니다. (내가 100 % 확신하지 못하는 이유는 몇 달 전 필자가 그 부분을 썼기 때문에 GUI에서 위력을 발휘했기 때문에 이제는 콘솔 앱을 추가하면이 점을 다시 생각하게되었습니다)

내 비즈니스 계층은 생성 된 스레드에서 이러한 프로세스를 실행하기 위해 GUI 버전과 명령 줄 버전 모두에서 작동해야합니다.

비즈니스 계층에서 Dispatcher를 잘못 사용하고 있습니까? 직렬 포트에서 이벤트를 처리 한 다음 주 스레드로 돌아가 데이터를 처리하는 더 좋은 방법이 있습니까?

업데이트 :

Private Delegate Sub NewDataRecieved(ByVal byteBuffer() As Byte) 
Private Sub DataReceived(ByVal byteBuffer() As Byte) Handles _serial.DataRecieved 
    If _dispatcher.CheckAccess() Then 
     ProcessTheData 
    Else 
     Dim dataReceivedDelegate As NewDataRecieved 
     dataReceivedDelegate = New NewDataRecieved(AddressOf DataReceived) 
     _dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, dataReceivedDelegate, byteBuffer) 
    End If 
End Sub 
+0

는 WPF 적절한 사용하지 않는 경우, SynchronizationContext에 안전하게 크로스 스레드를 보낼 수있는 기능을 제공하는 것보다 낮은 수준의 접근 방식을 사용하는 것이 좋습니다. –

답변

2

호출 아무것도하지 않는

지금, 당신의 문제는 Dispatcher를 호출하는 것입니다. Run을 사용하면 WPF가 스레드를 제어 할 수 있습니다. 이는 콘솔 응용 프로그램에서 수행하려는 작업이 아닐 수 있습니다.

귀하의 상황에 가장 적합한 옵션은 비즈니스 계층에서 래퍼 객체로 스레드 동기화 코드 (디스패처와 대화하는 모든 항목)를 제거하는 것입니다.

WPF 응용 프로그램은 래퍼 객체를 사용하여 이전처럼 계속 작동 할 수 있으며 콘솔 응용 프로그램은 "원시"비즈니스 계층을 직접 사용할 수 있습니다.

업데이트 : 다음은 래퍼의 예입니다. 스레딩 작업을 수행하고 원본 개체를 호출하는 원래 클래스의 모든 public 멤버에 대해 메서드/속성을 만들어야합니다.

public class BOWrapper : INotifyPropertyChanged 
{ 
    private BO _bo; 
    private Dispather _dispather; 

    public BOWrapper(BO bo, Dispatcher dispather) 
    { 
     _bo = bo; 
     _dispather = dispather; 
     _bo.PropertyChanged += BOPropertyChanged; 
    } 

    public string SomeValue 
    { 
     get { return _bo.SomeValue; } 
    } 

    private void BOPropertyChanged(object sender, PropertyChangedEventArgs ea) 
    { 
     _dispatcher.Invoke(
      new Action<PropertyChangedEventArgs>(
       e=> 
       { 
        var handler = PropertyChanged; 
        if(handler!=null) handler(this,e); 
       }),ea); 
    } 
} 

래퍼 클래스는 100 % 상용구 코드이며, 당신은 아마 어쩌면 런타임에 자동으로 생성 DynamicProxy (http://www.castleproject.org/dynamicproxy/index.html) 같은 것을 사용, 그것을 만들기 위해 몇 가지 코드 생성기를 사용할 수 있습니다.

+0

나는 invoke가 작동하지 않는 이유에 대한 설명을 좋아한다. "래퍼 객체"가 무엇을 의미하는지 파악하려고합니다. 유지해야하는 또 다른 계층을 만들면 두 응용 프로그램간에 하나의 비즈니스 계층을 공유함으로써 얻은 이점을 제거 할 수 있습니다. 또한 비즈니스 계층의 이벤트를 처리하기 위해이 래퍼를 작성한 다음 코드 양을 거의 두 배로 늘리지 않고도 올바른 스레드에서 더 많은 이벤트를 발생시키는 방법을 확신하지 못합니다. – zimmer62

+0

답변을 업데이트했습니다. 고객님의 질문에 대한 답변이 – Nir

+0

+1이 되었기를 바랍니다. 이것 같이 보이는 응답은 잘 보인다! –

0

WPF를 콘솔 응용 프로그램에서? 콘솔 응용 프로그램에서는 특정 스레드에서 기능을 실행하는 데 제한이 없습니다. 필요한 경우 호출자 스레드 컨텍스트에서 모든 이벤트를 처리하여 동기화를 제공 할 수 있습니다. 당신은 당신이 Dispatcher.Run (http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.run.aspx)를 호출해야 디스패처에서 모든 서비스를 얻기 위해, 디스패처 실행이 없기 때문에

+0

문제는 비즈니스 계층이 별도의 프로젝트이므로 WPF 응용 프로그램과 해당 프로젝트를 참조하는 콘솔 응용 프로그램 모두에서 작동하도록하고 싶습니다. 콘솔 응용 프로그램의 WPF가 아닙니다. 두 가지 응용 프로그램과 하나의 비즈니스 계층이 있습니다. WPF로 문제를 해결하기 위해 비즈니스 계층에 추가 한 후크는 콘솔 앱이 작동하지 않게합니다. – zimmer62

0

대안 : Dispatcher를 원하는 스레드에서 Dispatcher.CurrentDispatcher 정적 특성을 사용하십시오. 디스패처가 아직 스레드에 대해 생성되지 않은 경우 디스패처가 자동 생성됩니다 (가능한 경우). 다른 스레드가 디스패처를 사용할 수 있도록이 값을 어딘가에 저장할 수 있습니다.코드에서

:

public class BusinessLayerThread 
{ 
    public BusinessLayerThread() 
    { 
     Dispatcher = Dispatcher.CurrentDispatcher; 
    } 

    public static Dispatcher Dispatcher { get; private set; } 
} 
+0

이것은 실제로 내가 한 일이며 작동하지 않습니다. _dispatcher.CheckAccess()는 false를 반환하고 _dispatcher.Invoke (System.Windows.Threading.DispatcherPriority.Normal, dataReceivedDelegate, byteBuffer)가 호출 될 때 실행될 코드는 결코 발생하지 않습니다. – zimmer62

+0

디스패처가 저장된 중단 점과 호출이 완료된 위치를 사용해보십시오. 그런 다음 실제로 서로 다른 두 개의 스레드인지 확인하십시오. –