성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <br /> <div style='font-family: 맑은 고딕, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>WPF + WCF 환경에서는 DataContract를 권장</div><br /> <br /> 개인적으로 WPF로 프로젝트를 진행하면서 가장 마음에 들었던 부분이라면, 바로 "데이터 바인딩"을 들 수 있겠습니다. 물론, WinForm 시절에도 가능했지만 WPF에서는 한층 더 발전된 양상을 보여주고 있기 때문입니다.<br /> <br /> 그런데, WPF 응용 프로그램에서 데이터 바인딩이 된 인스턴스를 WCF 메서드에 실어 보내다 보면 예기치 않은 문제가 발생하게 됩니다. 어디 ... 한번 살펴볼까요? ^^<br /> <br /> 예를 들어, INotifyPropertyChanged를 구현한 아래와 같은 타입이 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > <b style='color: blue'>[System.SerializableAttribute()]</b> public class MyData : <b style='color: blue'>INotifyPropertyChanged</b> { private string name; public string Name { get { return this.name; } set { if (this.name == value) { return; } this.name = value; OnPropertyChanged("Name"); } } <b style='color: blue'>public event PropertyChangedEventHandler PropertyChanged</b>; protected void OnPropertyChanged(string propertyName) { if ((PropertyChanged != null)) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } </pre> <br /> 이렇게 정의된 MyData를 이용하는 WCF 메서드가 다음과 같이 구현되어,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > [ServiceContract] public interface IServiceA { [OperationContract] void SetData(MyData myData); } [ServiceBehavior] public class ServiceAImp : IServiceA { <b style='color: blue'>public void SetData(MyData myData)</b> { } } </pre> <br /> WPF 클라이언트에서는 다음과 같이 WCF 메서드를 호출하는 것이 가능하게 되지요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > public partial class Window1 : Window { public Window1() { InitializeComponent(); ServiceReference1.ServiceAClient svc = new WpfApplication1.ServiceReference1.ServiceAClient(); MyData data = new MyData(); data.Name = "Test"; <b style='color: blue'>svc.SetData(data);</b> } } </pre> <br /> 여기까지는 평범합니다. 하지만, 위의 코드에 WPF 데이터 바인딩이 더해지면 상황은 그리 녹녹치 않게 됩니다. 예를 들어 WPF 측의 코드에서 MyData data; 인스턴스를 아래와 같이 바인딩을 시켜 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > <TextBox Text="{Binding <b style='color: blue'>Path=Name</b>}" Height="23" /> <Button Name="button1" Width="57" Click="button1_Click">Button</Button> private void Window_Loaded(object sender, RoutedEventArgs e) { MyData data = new MyData(); data.Name = "Test"; <b style='color: blue'>this.DataContext = data;</b> } private void button1_Click(object sender, RoutedEventArgs e) { ServiceReference1.ServiceAClient svc = new WpfApplication1.ServiceReference1.ServiceAClient(); MyData data = this.DataContext as MyData; <b style='color: blue'>svc.SetData(data); <== 예외 발생</b> } </pre> <br /> 이번에는 svc.SetData 메서드 호출에서 아래와 같은 예외가 발생하게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > There was an error while trying to serialize parameter http://tempuri.org/:myData. The InnerException message was 'Type '<b style='color: blue'>System.DelegateSerializationHolder+DelegateEntry</b>' with data contract name 'DelegateSerializationHolder.DelegateEntry:http://schemas.datacontract.org/2004/07/System' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details. </pre> <br /> 문제가 뭘까요?<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그것은, [Serializable] 특성이 지정된 타입에 대한 DataContractSerializer 직렬화 방식에서 기인하게 됩니다. 이에 대한 자세한 설명은 다음의 토픽을 읽어보십시오.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > Service Station - Serialization in Windows Communication Foundation Serializing and Encoding ; <a target='_tab' href='https://docs.microsoft.com/en-us/archive/msdn-magazine/2006/august/service-station-serialization-in-windows-communication-foundation#S1'>https://docs.microsoft.com/en-us/archive/msdn-magazine/2006/august/service-station-serialization-in-windows-communication-foundation#S1</a> </pre> <br /> 위의 기사에 보면 첫 번째 표에 각종 직렬화 방식에 대한 정리를 잘해 두었는데요. 편의상, 아래에 붙여놓고 설명하겠습니다.<br /> <br /> <table border='1'> <tr valign="top"> <th>Feature</th> <th>XmlSerializer</th> <th>DataContractSerializer NetDataContractSerializer</th> </tr> <tr valign="top"> <td>Explicitness</td> <td>Opt-out</td> <td>Opt-in *<br /><b style='color: Blue;'>Opt-out **</b></td> </tr> <tr valign="top"> <td>Default mapping</td> <td>Public fields/props</td> <td>All [DataMember]s *<br />All fields **</td> </tr> <tr valign="top"> <td>Attribute required</td> <td>No</td> <td>Yes</td> </tr> <tr valign="top"> <td>Default order</td> <td>Same as class</td> <td>Alphabetical</td> </tr> <tr valign="top"> <td>XML Schema</td> <td>Extensive</td> <td>Constrained</td> </tr> <tr valign="top"> <td>Code generator</td> <td>Xsd.exe</td> <td>SvcUtil.exe</td> </tr> <tr valign="top"> <td>Override</td> <td>IXmlSerializable</td> <td>ISerializable</td> </tr> <tr valign="top"> <td>Type fidelity</td> <td>No</td> <td>NetDataContractSerializer</td> </tr> <tr valign="top"> <td>Versioning support</td> <td>No</td> <td>Yes</td> </tr> <tr valign="top"> <td>Initialization</td> <td>Constructor</td> <td>Callbacks</td> </tr> <tr valign="top"> <td>Compatibility</td> <td>ASMX</td> <td>.NET Remoting</td> </tr> <tr valign="top"> <td colspan="3">* Using [DataContract] <b style='color: Blue;'> ** Using [Serializable]</b></td> </tr> </table> <br /> 위에서 강조해 놓은 부분을 곰곰이 생각해 보시면, 왜 WPF에서 데이터바인딩을 한 개체를 WCF 메서드에 실어 보낼 때 예외가 발생했는지를 알 수 있습니다. 즉, DataContractSerializer 개체는 주어진 데이터를 직렬화 할 때, "Opt-out" 원칙에 기반해서 PropertyChanged 이벤트(타입)도 직렬화 하려고 시도를 하기 때문에 오류가 발생하는 것은 어찌 보면 당연한 것입니다.<br /> <br /> 처음에는 이것을 피해가기 위해 "PropertyChanged" 이벤트에 "[NonSerialized]" 특성을 주면 어떨까 싶었는데, 불행히도 그 특성은 AttributeUsage가 AttributeTargets.Field로만 정해져 있어서 가능하지 않았습니다.<br /> <br /> 이쯤 되면... 뭐... ^^; 어쩔 수 없지요. "Opt-in" 원칙이 적용되는 DataContract 특성을 주는 수밖에.<br /> <br /> <br /> <br /><br /><hr /><span style='color: Maroon'>[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1096
(왼쪽의 숫자를 입력해야 합니다.)