Nader가 말했듯이, 아마도 좀 더 느슨한 결합 디자인을 생각해보십시오. 그러나, 제 경우에는 느슨한 결합이 적절하지 않았습니다. 다음은 클래스 계층 구조와 사용자 지정 직렬화 또는 DTO를 사용하지 않고 문제를 해결하는 방법입니다.
내 프로젝트에서 나는 웹 서비스를 통해 제출 될 XML 문서의 조각을 표현하기 위해 전체 개체를 구성하고있다. 매우 많은 수의 조각이 있습니다. 모든 요청과 함께 모든 것이 전송되는 것은 아닙니다 (실제로이 예에서는 응답을 모델링하지만 개념은 동일합니다). 이 조각들은 요청을 모으는 빌딩 블록과 매우 비슷하게 사용됩니다 (또는이 경우에는 응답을 분해합니다). 그래서 여기에 계승/캡슐화를 사용하여 상속 계층 구조에도 불구하고 원하는 순서를 얻는 예제가 있습니다. 내가 포함 된 모든 클래스에 대한 속성을 만들 수있는 반면
[Serializable]
public abstract class ElementBase
{
// This constructor sets up the default namespace for all of my objects. Every
// Xml Element class will inherit from this class.
internal ElementBase()
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:my-default-namespace:XSD:1")
});
}
[XmlNamespacesDeclaration]
public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } }
private XmlSerializationNamespaces _namespaces;
}
[Serializable]
public abstract class ServiceBase : ElementBase
{
private ServiceBase() { }
public ServiceBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null)
{
this._requestId = requestId;
this._asyncRequestId = asyncRequestId;
this._name = name;
}
public Guid RequestId
{
get { return this._requestId; }
set { this._requestId = value; }
}
private Guid _requestId;
public Guid? AsyncRequestId
{
get { return this._asyncRequestId; }
set { this._asyncRequestId = value; }
}
private Guid? _asyncRequestId;
public bool AsyncRequestIdSpecified
{
get { return this._asyncRequestId == null && this._asyncRequestId.HasValue; }
set { /* XmlSerializer requires both a getter and a setter.*/ ; }
}
public Identifier Name
{
get { return this._name; }
set { this._name; }
}
private Identifier _name;
}
[Serializable]
public abstract class ServiceResponseBase : ServiceBase
{
private ServiceBase _serviceBase;
private ServiceResponseBase() { }
public ServiceResponseBase(Guid requestId, Guid? asyncRequestId = null, Identifier name = null, Status status = null)
{
this._serviceBase = new ServiceBase(requestId, asyncRequestId, name);
this._status = status;
}
public Guid RequestId
{
get { return this._serviceBase.RequestId; }
set { this._serviceBase.RequestId = value; }
}
public Guid? AsyncRequestId
{
get { return this._serviceBase.AsyncRequestId; }
set { this._serviceBase.AsyncRequestId = value; }
}
public bool AsynceRequestIdSpecified
{
get { return this._serviceBase.AsyncRequestIdSpecified; }
set { ; }
}
public Identifier Name
{
get { return this._serviceBase.Name; }
set { this._serviceBase.Name = value; }
}
public Status Status
{
get { return this._status; }
set { this._status = value; }
}
}
[Serializable]
[XmlRoot(Namespace = "urn:my-default-namespace:XSD:1")]
public class BankServiceResponse : ServiceResponseBase
{
// Determines if the class is being deserialized.
private bool _isDeserializing;
private ServiceResponseBase _serviceResponseBase;
// Constructor used by XmlSerializer.
// This is special because I require a non-null List<T> of items later on.
private BankServiceResponse()
{
this._isDeserializing = true;
this._serviceResponseBase = new ServiceResponseBase();
}
// Constructor used for unit testing
internal BankServiceResponse(bool isDeserializing = false)
{
this._isDeserializing = isDeserializing;
this._serviceResponseBase = new ServiceResponseBase();
}
public BankServiceResponse(Guid requestId, List<BankResponse> responses, Guid? asyncRequestId = null, Identifier name = null, Status status = null)
{
if (responses == null || responses.Count == 0)
throw new ArgumentNullException("The list cannot be null or empty", "responses");
this._serviceResponseBase = new ServiceResponseBase(requestId, asyncRequestId, name, status);
this._responses = responses;
}
[XmlElement(Order = 1)]
public Status Status
{
get { return this._serviceResponseBase.Status; }
set { this._serviceResponseBase.Status = value; }
}
[XmlElement(Order = 2)]
public Guid RequestId
{
get { return this._serviceResponseBase.RequestId; }
set { this._serviceResponseBase.RequestId = value; }
}
[XmlElement(Order = 3)]
public Guid? AsyncRequestId
{
get { return this._serviceResponseBase.AsyncRequestId; }
set { this._serviceResponseBase.AsyncRequestId = value; }
}
[XmlIgnore]
public bool AsyncRequestIdSpecified
{
get { return this._serviceResponseBase.AsyncRequestIdSpecified; }
set { ; } // Must have this for XmlSerializer.
}
[XmlElement(Order = 4)]
public Identifer Name
{
get { return this._serviceResponseBase.Name; }
set { this._serviceResponseBase.Name; }
}
[XmlElement(Order = 5)]
public List<BankResponse> Responses
{
get { return this._responses; }
set
{
if (this._isDeserializing && this._responses != null && this._responses.Count > 0)
this._isDeserializing = false;
if (!this._isDeserializing && (value == null || value.Count == 0))
throw new ArgumentNullException("List cannot be null or empty.", "value");
this._responses = value;
}
}
private List<BankResponse> _responses;
}
그래서, 나는 단순히 포함 된 클래스의 속성 때를 사용하여 I가 포함 된 클래스 (들) 속성 세터/게터 내에있을 수 있습니다 사용자 정의 로직을 위임 할 수 있습니다 리프 클래스의 속성에 액세스합니다. 상속이 없으므로 리프 클래스의 모든 속성을 XmlElementAttribute
특성으로 꾸밀 수 있으며 적합하다고 판단되는 순서를 사용할 수 있습니다.
UPDATE : 클래스 상속을 사용하는 방법에 대한 내 디자인 결정이 날 또 다시 물어 왔기 때문에이 글을 다시 돌아 왔어요
. 위의 솔루션이 작동하는 동안 나는 그것을 사용하고 있으며, 실제로 Nader의 솔루션이 최고라고 생각하고 내가 제시 한 솔루션보다 먼저 고려해야합니다. 사실, 나는 오늘 그를 +1하고있다! 나는 그의 대답을 정말로 좋아한다. 그리고 내가 현재 프로젝트를 리팩토링 할 기회가 있다면, 나는 코드를 단순화하고 더 쉽게 만들 수있는 상속에서 큰 이익을 얻을 수있는 객체에 대한 직렬화 로직과 비즈니스 객체를 분리 할 것이다. 다른 사람들이 사용하고 이해할 수 있도록
답장을 보내 주셔서 감사합니다. 많은 사람들이 매우 유익하고 유용하다고 생각합니다.
SOAP 메시지의 마지막 부분에 나타 내기 위해 파생 클래스의 속성을 필요로하는 것과 비슷한 문제가 발생했습니다. 내 솔루션은 기본 클래스의 내부 속성을 추가 한 다음 "새로운"키워드로 숨 깁니다. 파생 클래스에서. 내 대답 [여기] (http://stackoverflow.com/questions/22174311/wcf-serialization-order-issue/22177272#22177272)을 참조하십시오. 희망이 도움이됩니다. –