从 POCO 类生成 TypeScript 接口
从 .NET Framework 或 .NET Core 的 POCO 类生成 TypeScript 接口
引言
POCO2TS
是一个命令行程序,可以从基于 .NET Framework 或 .NET Core 构建的程序集的 POCO 类生成 TypeScript 接口。 您的 .NET 代码是用 C# 还是 VB 并不重要。
背景
这是 WebApiClientGen 的副产品。 如果您正在使用 jQuery 或 Angular 2+ 开发 Web 客户端程序,您可能需要比 POCO2TS
提供的更多功能,而这些功能实际上集成在 WebApiClientGen
中。 如果是这样,您可以跳过本文,而是阅读以下内容
Using the Code
您在程序集 DemoWebApi.DemoData.dll 中拥有 POCO 类,这些类由 DataContractAttribute
装饰,用于数据模型、WCF、Entity Framework Code First 以及使用 XML 或 JSON 进行序列化。 请查看 C# 中的此示例。
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;
namespace DemoWebApi.DemoData
{
public sealed class Constants
{
public const string DataNamespace = "http://fonlow.com/DemoData/2014/02";
}
[DataContract(Namespace = Constants.DataNamespace)]
public enum AddressType
{
[EnumMember]
Postal,
[EnumMember]
Residential,
};
public enum MyEnumType
{
[EnumMember]
First = 1,
[EnumMember]
Two = 2,
};
[DataContract(Namespace = Constants.DataNamespace)]
public enum Days
{
[EnumMember]
Sat = 1,
[EnumMember]
Sun,
[EnumMember]
Mon,
[EnumMember]
Tue,
[EnumMember]
Wed,
/// <summary>
/// Thursday
/// </summary>
[EnumMember]
Thu,
[EnumMember]
Fri
};
[DataContract(Namespace = Constants.DataNamespace)]
public class PhoneNumber
{
public PhoneNumber()
{
}
public Guid Id { get; set; }
[DataMember]
public string FullNumber { get; set; }
[DataMember]
public PhoneType PhoneType { get; set; }
public Guid EntityId { get; set; }
}
/// <summary>
/// Phone type
/// Tel, Mobile, Skyp and Fax
///
/// </summary>
[DataContract(Namespace = Constants.DataNamespace)]
public enum PhoneType
{
/// <summary>
/// Land line
/// </summary>
[EnumMember]
Tel = 0,
/// <summary>
/// Mobile phone
/// </summary>
[EnumMember]
Mobile = 1,
[EnumMember]
Skype = 2,
[EnumMember]
Fax = 3,
}
[DataContract(Namespace = Constants.DataNamespace)]
public class Address
{
[DataMember]
public Guid Id { get; set; }
public Entity Entity { get; set; }
/// <summary>
/// Foreign key to Entity
/// </summary>
public Guid EntityId { get; set; }
[DataMember]
public string Street1 { get; set; }
[DataMember]
public string Street2 { get; set; }
[DataMember]
public string City { get; set; }
[DataMember]
public string State { get; set; }
[DataMember]
public string PostalCode { get; set; }
[DataMember]
public string Country { get; set; }
[DataMember]
public AddressType Type { get; set; }
[DataMember]
public DemoWebApi.DemoData.Another.MyPoint Location;
}
/// <summary>
/// Base class of company and person
/// </summary>
[DataContract(Namespace = Constants.DataNamespace)]
public class Entity
{
public Entity()
{
Addresses = new List<Address>();
}
[DataMember]
public Guid Id { get; set; }
/// <summary>
/// Name of the entity.
/// </summary>
[DataMember(IsRequired =true)]//MVC and Web API does not care
[System.ComponentModel.DataAnnotations.Required]//MVC and Web API care about only this
public string Name { get; set; }
/// <summary>
/// Multiple addresses
/// </summary>
[DataMember]
public IList<Address> Addresses { get; set; }
[DataMember]
public virtual ObservableCollection<PhoneNumber> PhoneNumbers { get; set; }
public override string ToString()
{
return Name;
}
[DataMember]
public Uri Web { get; set; }
}
[DataContract(Namespace = Constants.DataNamespace)]
public class Person : Entity
{
[DataMember]
public string Surname { get; set; }
[DataMember]
public string GivenName { get; set; }
/// <summary>
/// Date of Birth.
/// This is optional.
/// </summary>
[DataMember]
public DateTime? DOB { get; set; }
public override string ToString()
{
return Surname + ", " + GivenName;
}
}
[DataContract(Namespace = Constants.DataNamespace)]
public class Company : Entity
{
/// <summary>
/// BusinessNumber to be serialized as BusinessNum
/// </summary>
[DataMember(Name ="BusinessNum")]
public string BusinessNumber { get; set; }
[DataMember]
public string BusinessNumberType { get; set; }
[DataMember]
public string[][] TextMatrix
{ get; set; }
[DataMember]
public int[][] Int2DJagged;
[DataMember]
public int[,] Int2D;
[DataMember]
public IEnumerable<string> Lines;
}
[DataContract(Namespace = Constants.DataNamespace)]
public class MyPeopleDic
{
[DataMember]
public IDictionary<string, Person> Dic { get; set; }
[DataMember]
public IDictionary<string, string> AnotherDic { get; set; }
[DataMember]
public IDictionary<int, string> IntDic { get; set; }
}
}
运行后
POCO2TS.exe demowebapi.demodata.dll TypeScriptDataModels.ts
或
dotnet POCO2TSCore.dll demowebapi.demodatacore.dll TypeScriptDataModels.ts
输出 TypeScriptDataModels.ts 将包含 TypeScript
接口
export namespace DemoWebApi_DemoData_Client {
export enum AddressType { Postal, Residential }
export enum Days {
Sat = 1,
Sun = 2,
Mon = 3,
Tue = 4,
Wed = 5,
/**
* Thursday
*/
Thu = 6,
Fri = 7
}
export interface PhoneNumber {
fullNumber?: string;
phoneType?: DemoWebApi_DemoData_Client.PhoneType;
}
/**
* Phone type
* Tel, Mobile, Skyp and Fax
*/
export enum PhoneType {
/**
* Land line
*/
Tel,
/**
* Mobile phone
*/
Mobile,
Skype,
Fax
}
export interface Address {
id?: string;
street1?: string;
street2?: string;
city?: string;
state?: string;
postalCode?: string;
country?: string;
type?: DemoWebApi_DemoData_Client.AddressType;
location?: any;
}
/**
* Base class of company and person
*/
export interface Entity {
id?: string;
/**
* Name of the entity.
*/
name: string;
/**
* Multiple addresses
*/
addresses?: Array<DemoWebApi_DemoData_Client.Address>;
phoneNumbers?: Array<DemoWebApi_DemoData_Client.PhoneNumber>;
web?: string;
}
export interface Person extends DemoWebApi_DemoData_Client.Entity {
surname?: string;
givenName?: string;
/**
* Date of Birth.
* This is optional.
*/
dob?: Date;
}
export interface Company extends DemoWebApi_DemoData_Client.Entity {
/**
* BusinessNumber to be serialized as BusinessNum
*/
BusinessNum?: string;
businessNumberType?: string;
textMatrix?: Array<Array<string>>;
int2DJagged?: Array<Array<number>>;
int2D?: number[][];
lines?: Array<string>;
}
export interface MyPeopleDic {
dic?: {[id: string]: DemoWebApi_DemoData_Client.Person };
anotherDic?: {[id: string]: string };
intDic?: {[id: number]: string };
}
}
POCO2TS.exe 作为命令行程序,默认情况下只会处理所有由 DataContractAttribute
装饰的类,并发布那些由 DataMemberAttribute
装饰的成员。 但是,enum
类型的成员都将被处理,无论是否使用 EnumMemberAttribute
。
Code First 中的外键通常不应暴露给客户端程序,并且它们没有用 DataMemberAttribute
装饰,因此它们不包含在生成的 TypeScript 接口中。
精选
如您所见,POCO2TS
主要使用属性进行精选。 有多种精选方法。 如果您在不带参数的情况下运行 POCO2TS.exe,您可能会看到以下内容
Poco2Ts.exe generates TypeScript data model interfaces from POCO classes.
Example:
For classes decorated by DataContractAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts
For classes decorated by Newtonsoft.Json.JsonObjectAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /2
For classes decorated by SerializableAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /4
For public classes, properties and properties,
and use System.ComponentModel.DataAnnotations.RequiredAttribute:
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /8
For all classes, properties and fields
Fonlow.Poco2Ts.exe MyAssemblyWithPOCO.dll MyOutputTS.ts /0
因此,如果您有一些类由 DataContractAttribute
装饰,另一些类由 JsonObjectAttribute
装饰,并且您想包含这两个集合,那么选项是 /3
。 并且 DataMemberAttribute
和 JsonPropertyAttribute
将相应地处理。
关注点
POCO2TS
实际上没有对 Newtonsoft.Json 的静态依赖。 只有在使用像 JsonObjectAttribute 这样的属性类来装饰您的 POCO 类,并且要使用这些属性类进行精选时,才需要 Newtonsoft.Json。 当您使用 NewtonsoftJson 选项时,.NET 运行时将尝试找到它。 如果运行时无法找到它,POCO2TS
将通过检查数据模型程序集所在的文件夹来解决。 这种解析对于您的数据模型程序集依赖于其他数据模型程序集时也很有用。
您可能遇到过 TypeLITE 和 TypeWriter,这里有一个简单的比较: 与 TypeLITE 和 TypeWriter 比较。