2013-09-05 68 views
6

Tôi có dịch vụ sử dụng loại Thời gian Noda (LocalDateZonedDateTime) trong thông số OperationContract, nhưng khi tôi thử gửi ví dụ LocalDate(1990,7,31) máy chủ sẽ nhận được đối tượng có giá trị mặc định (1970/1/1). Không có lỗi do khách hàng hoặc máy chủ đưa ra.Làm cách nào để truyền đối tượng Thời gian Noda (hoặc bất kỳ loại bên thứ ba) nào làm tham số trong WCF?

Trước đây nó hoạt động tốt với các loại BCL tương ứng (DateTimeOffset). Tôi hiểu các loại Thời gian Noda có thể không được "biết đến" bởi WCF, nhưng tôi không thấy làm thế nào tôi có nghĩa vụ phải thêm chúng. Tôi đã kiểm tra this page in the documentation about known types, nhưng nó không giúp đỡ.

Có cách nào để thực hiện việc này để tránh chuyển đổi/tuần tự thủ công (và có thể không đầy đủ) từ và sang loại BCL không?

Cảm ơn bạn.

+2

Âm thanh như Jon không bao gồm thuộc tính DataContract trong Noda. Bạn có thể cần sử dụng giao diện [IDataContractSurrogate] (http://msdn.microsoft.com/en-us/library/system.runtime.serialization.idatacontractsurrogate.aspx) – Aron

+1

Cảm ơn Aron! Điều này rất hữu ích. Tôi đã có thể tạo ra một đại diện trong thời gian không (da) bằng cách sử dụng http: //blogs.msdn.com/b/carlosfigueira/lưu trữ/2011/09/14/wcf-mở rộng-serialization-surrogates.aspx và http://stackoverflow.com/questions/4742225/using-a-datacontractsurrogate-with-wcf-rest –

+0

Chỉ NodaTime hiện đang hỗ trợ tuần tự hóa Json.NET, và chỉ sau đó thông qua 'NodaTime.Serialization.JsonNet' hiện không được biên dịch vào bản phát hành chính. Bạn sẽ phải tự mình xây dựng nó. Tôi rất vui khi biết bạn có thể làm việc này với những người thay thế DataContract. Tôi quan tâm để xem việc triển khai của bạn. Sẽ sẵn sàng để đăng nó một nơi nào đó (GitHub, GIST, vv)? –

Trả lời

3

Nhờ gợi ý của Aron, tôi đã có thể đưa ra một triển khai IDataContractSurrogate, rất hữu ích để truyền các đối tượng của các kiểu cơ sở không thông qua WCF (không chỉ thời gian Noda).

Đối với những người quan tâm, đây là mã hoàn chỉnh có giải thích, hỗ trợ LocalDate, LocalDateTime và ZonedDateTime. Tất nhiên, phương thức tuần tự hóa có thể được tùy chỉnh để đáp ứng các yêu cầu, ví dụ bằng cách sử dụng tuần tự hóa Json.NET, vì việc triển khai đơn giản của tôi sẽ không tuần tự hóa thông tin về thời gian/lịch.

Hoặc, tôi đã đăng toàn bộ mã trên Gist này: https://gist.github.com/mayerwin/6468178.

Thứ nhất, lớp helper mà sẽ chăm sóc của serializing/chuyển đổi các loại cơ sở:

public static class DatesExtensions { 
    public static DateTime ToDateTime(this LocalDate localDate) { 
     return new DateTime(localDate.Year, localDate.Month, localDate.Day); 
    } 

    public static LocalDate ToLocalDate(this DateTime dateTime) { 
     return new LocalDate(dateTime.Year, dateTime.Month, dateTime.Day); 
    } 

    public static string Serialize(this ZonedDateTime zonedDateTime) { 
     return LocalDateTimePattern.ExtendedIsoPattern.Format(zonedDateTime.LocalDateTime) + "@O=" + OffsetPattern.GeneralInvariantPattern.Format(zonedDateTime.Offset) + "@Z=" + zonedDateTime.Zone.Id; 
    } 

    public static ZonedDateTime DeserializeZonedDateTime(string value) { 
     var match = ZonedDateTimeRegex.Match(value); 
     if (!match.Success) throw new InvalidOperationException("Could not parse " + value); 
     var dtm = LocalDateTimePattern.ExtendedIsoPattern.Parse(match.Groups[1].Value).Value; 
     var offset = OffsetPattern.GeneralInvariantPattern.Parse(match.Groups[2].Value).Value; 
     var tz = DateTimeZoneProviders.Tzdb.GetZoneOrNull(match.Groups[3].Value); 
     return new ZonedDateTime(dtm, tz, offset); 
    } 

    public static readonly Regex ZonedDateTimeRegex = new Regex(@"^(.*)@O=(.*)@Z=(.*)$"); 
} 

Sau đó, một lớp ReplacementType mà chứa dữ liệu tuần tự (In nhiều chỉ nên loại cửa hàng được gọi bằng cái serializer WCF) và có thể được thông qua trên WCF: quy tắc

public class ReplacementType { 
    [DataMember(Name = "Serialized")] 
    public object Serialized { get; set; } 
    [DataMember(Name = "OriginalType")] 
    public string OriginalTypeFullName { get; set; } 
} 

các serialization/deserialization được bọc trong lớp Translator chung để dễ dàng thêm quy tắc để surrogate (chỉ có một đại diện được gán cho endpo dịch vụ int vì thế nên chứa tất cả các quy tắc cần thiết):

public abstract class Translator { 
    public abstract object Serialize(object obj); 
    public abstract object Deserialize(object obj); 
} 

public class Translator<TOriginal, TSerialized> : Translator { 
    private readonly Func<TOriginal, TSerialized> _Serialize; 

    private readonly Func<TSerialized, TOriginal> _Deserialize; 

    public Translator(Func<TOriginal, TSerialized> serialize, Func<TSerialized, TOriginal> deserialize) { 
     this._Serialize = serialize; 
     this._Deserialize = deserialize; 
    } 

    public override object Serialize(object obj) { 
     return new ReplacementType { Serialized = this._Serialize((TOriginal)obj), OriginalTypeFullName = typeof(TOriginal).FullName }; 
    } 

    public override object Deserialize(object obj) { 
     return this._Deserialize((TSerialized)obj); 
    } 
} 

Cuối cùng lớp thay thế, từng quy tắc dịch có thể dễ dàng thêm vào trong constructor tĩnh:

public class CustomSurrogate : IDataContractSurrogate { 
    /// Type.GetType only works for the current assembly or mscorlib.dll 
    private static readonly Dictionary<string, Type> AllLoadedTypesByFullName = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Distinct().GroupBy(t => t.FullName).ToDictionary(t => t.Key, t => t.First()); 

    public static Type GetTypeExt(string typeFullName) { 
     return Type.GetType(typeFullName) ?? AllLoadedTypesByFullName[typeFullName]; 
    } 

    private static readonly Dictionary<Type, Translator> Translators; 
    static CustomSurrogate() { 
     Translators = new Dictionary<Type, Translator> { 
      {typeof(LocalDate), new Translator<LocalDate, DateTime>(serialize: d => d.ToDateTime(), deserialize: d => d.ToLocalDate())}, 
      {typeof(LocalDateTime), new Translator<LocalDateTime, DateTime>(serialize: d => d.ToDateTimeUnspecified(), deserialize: LocalDateTime.FromDateTime)}, 
      {typeof(ZonedDateTime), new Translator<ZonedDateTime, string> (serialize: d => d.Serialize(), deserialize: DatesExtensions.DeserializeZonedDateTime)} 
     }; 
    } 

    public Type GetDataContractType(Type type) { 
     if (Translators.ContainsKey(type)) { 
      type = typeof(ReplacementType); 
     } 
     return type; 
    } 

    public object GetObjectToSerialize(object obj, Type targetType) { 
     Translator translator; 
     if (Translators.TryGetValue(obj.GetType(), out translator)) { 
      return translator.Serialize(obj); 
     } 
     return obj; 
    } 

    public object GetDeserializedObject(object obj, Type targetType) { 
     var replacementType = obj as ReplacementType; 
     if (replacementType != null) { 
      var originalType = GetTypeExt(replacementType.OriginalTypeFullName); 
      return Translators[originalType].Deserialize(replacementType.Serialized); 
     } 
     return obj; 
    } 

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) { 
     throw new NotImplementedException(); 
    } 

    public object GetCustomDataToExport(Type clrType, Type dataContractType) { 
     throw new NotImplementedException(); 
    } 

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) { 
     throw new NotImplementedException(); 
    } 

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) { 
     throw new NotImplementedException(); 
    } 

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) { 
     throw new NotImplementedException(); 
    } 
} 

Và bây giờ sử dụng nó, chúng ta định nghĩa một dịch vụ có tên SurrogateService:

[ServiceContract] 
public interface ISurrogateService { 
    [OperationContract] 
    Tuple<LocalDate, LocalDateTime, ZonedDateTime> GetParams(LocalDate localDate, LocalDateTime localDateTime, ZonedDateTime zonedDateTime); 
} 

public class SurrogateService : ISurrogateService { 
    public Tuple<LocalDate, LocalDateTime, ZonedDateTime> GetParams(LocalDate localDate, LocalDateTime localDateTime, ZonedDateTime zonedDateTime) { 
     return Tuple.Create(localDate, localDateTime, zonedDateTime); 
    } 
} 

Để chạy trên một nền tảng hoàn toàn độc lập với các client và server trên cùng một máy (trong một ứng dụng Console), chúng tôi chỉ n eed để thêm mã sau vào một lớp tĩnh và gọi hàm Start():

public static class SurrogateServiceTest { 
    public static void DefineSurrogate(ServiceEndpoint endPoint, IDataContractSurrogate surrogate) { 
     foreach (var operation in endPoint.Contract.Operations) { 
      var ob = operation.Behaviors.Find<DataContractSerializerOperationBehavior>(); 
      ob.DataContractSurrogate = surrogate; 
     } 
    } 

    public static void Start() { 
     var baseAddress = "http://" + Environment.MachineName + ":8000/Service"; 
     var host = new ServiceHost(typeof(SurrogateService), new Uri(baseAddress)); 
     var endpoint = host.AddServiceEndpoint(typeof(ISurrogateService), new BasicHttpBinding(), ""); 
     host.Open(); 
     var surrogate = new CustomSurrogate(); 
     DefineSurrogate(endpoint, surrogate); 

     Console.WriteLine("Host opened"); 

     var factory = new ChannelFactory<ISurrogateService>(new BasicHttpBinding(), new EndpointAddress(baseAddress)); 
     DefineSurrogate(factory.Endpoint, surrogate); 
     var client = factory.CreateChannel(); 
     var now = SystemClock.Instance.Now.InUtc(); 
     var p = client.GetParams(localDate: now.Date, localDateTime: now.LocalDateTime, zonedDateTime: now); 

     if (p.Item1 == now.Date && p.Item2 == now.LocalDateTime && p.Item3 == now) { 
      Console.WriteLine("Success"); 
     } 
     else { 
      Console.WriteLine("Failure"); 
     } 
     ((IClientChannel)client).Close(); 
     factory.Close(); 

     Console.Write("Press ENTER to close the host"); 
     Console.ReadLine(); 
     host.Close(); 
    } 
} 

Voilà! :)