C# JSON数据结构-JSON到对象-最佳实践

C# JSON数据结构-JSON到对象-最佳实践,c#,javascript,asp.net,json,typescript,C#,Javascript,Asp.net,Json,Typescript,我正在努力使用JSON、ASP.NET、typescript/javascript和AngularJS设计web应用程序。 简而言之: 我需要一个通过JSON将数据从服务器发送到客户端的最佳实践,在客户端使用JSON字符串创建对象。 我有一个WebServerAPI项目(ASP.NET),其结构如下: 控制器 数据控制器(RESTAPI) 模型 A 类型 模型类: public class A { public property int Id {get; set;}

我正在努力使用JSON、ASP.NET、typescript/javascript和AngularJS设计web应用程序。

简而言之: 我需要一个通过JSON将数据从服务器发送到客户端的最佳实践,在客户端使用JSON字符串创建对象。

我有一个WebServerAPI项目(ASP.NET),其结构如下:

  • 控制器
    • 数据控制器(RESTAPI)
  • 模型
    • A
    • 类型
模型类:

public class A {
    public property int Id {get; set;}
    public property string Name {get; set;}
    public property Type Type {get; set;}
}

public class Type {
    public property int Id {get; set;}
    public property string Name {get; set;}
}
public class DataController : ApiController {
    // ...
    // GET api/a
    public IEnumerable<A> Get()
    {
        // fetches all As from the database
        // the facade creates instances of A and Type as required
        // (if A has a 'Type' the Type will also fetched an set)
        return facade.GetAll<A>();
    }
    // ...
}
[
    {"Id":1,
     "Type": {"Id":1, "Name":"name1"}
    },
    {"Id":2,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":3,
     "Type": {"Id":1, "Name":"name1"}
    }
    {"Id":4,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":5,
     "Type": {"Id":2, "Name":"name2"}
    },  
]
[
    {"Id":1,
     "TypeId": {"Id":1}
    },
    {"Id":2,
     "TypeId": {"Id":2}
    },
    {"Id":3,
     "TypeId": {"Id":1}
    }
    {"Id":4,
     "TypeId": {"Id":2}
    },
    {"Id":5,
     "TypeId": {"Id":2}
    },  
]
export class A {
    public Id: number;
    public Name: string;
    public Type: Type;

    public static fromData(data: any): A {

        var a = new A();
        a.Id = data.Id;
        a.Name = data.Name;
        a.Type = Type.fromData(data.Type);

        return a;
    }
}

export class Type {
        public Id: number;
        public Name: string;

        public static fromData(data: any) : Type {
            var type = new Type();
            type.Id = data.Id;
            type.Name = data.Name;

            return type;
        }
}

// AngularJS controller
export class AListCtrl {
    static $inject = ['$scope', '$http'];

    public As: A[] = [];

    constructor(private $scope, private $http) {
        $scope.AListCtrl = this;

        $http.get('http://localhost/api/a').success((data) => {
            var as: A[] = [];
            for (var i = 0; i < data.length; i++) {
                var aData = data[i];
                var a = A.fromData(aData);

                as.push(a);
            }

            this.As = as;
        });
    }
}
每个模型类的id属于数据库表中的一个id(主键)。

数据控制器结构:

public class A {
    public property int Id {get; set;}
    public property string Name {get; set;}
    public property Type Type {get; set;}
}

public class Type {
    public property int Id {get; set;}
    public property string Name {get; set;}
}
public class DataController : ApiController {
    // ...
    // GET api/a
    public IEnumerable<A> Get()
    {
        // fetches all As from the database
        // the facade creates instances of A and Type as required
        // (if A has a 'Type' the Type will also fetched an set)
        return facade.GetAll<A>();
    }
    // ...
}
[
    {"Id":1,
     "Type": {"Id":1, "Name":"name1"}
    },
    {"Id":2,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":3,
     "Type": {"Id":1, "Name":"name1"}
    }
    {"Id":4,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":5,
     "Type": {"Id":2, "Name":"name2"}
    },  
]
[
    {"Id":1,
     "TypeId": {"Id":1}
    },
    {"Id":2,
     "TypeId": {"Id":2}
    },
    {"Id":3,
     "TypeId": {"Id":1}
    }
    {"Id":4,
     "TypeId": {"Id":2}
    },
    {"Id":5,
     "TypeId": {"Id":2}
    },  
]
export class A {
    public Id: number;
    public Name: string;
    public Type: Type;

    public static fromData(data: any): A {

        var a = new A();
        a.Id = data.Id;
        a.Name = data.Name;
        a.Type = Type.fromData(data.Type);

        return a;
    }
}

export class Type {
        public Id: number;
        public Name: string;

        public static fromData(data: any) : Type {
            var type = new Type();
            type.Id = data.Id;
            type.Name = data.Name;

            return type;
        }
}

// AngularJS controller
export class AListCtrl {
    static $inject = ['$scope', '$http'];

    public As: A[] = [];

    constructor(private $scope, private $http) {
        $scope.AListCtrl = this;

        $http.get('http://localhost/api/a').success((data) => {
            var as: A[] = [];
            for (var i = 0; i < data.length; i++) {
                var aData = data[i];
                var a = A.fromData(aData);

                as.push(a);
            }

            this.As = as;
        });
    }
}
正如您在JSON数据中看到的,一些As共享相同的类型。虽然这是有效的JSON,但我想知道这是否是发送数据的最佳实践

发送这样的邮件不是更好吗:

public class A {
    public property int Id {get; set;}
    public property string Name {get; set;}
    public property Type Type {get; set;}
}

public class Type {
    public property int Id {get; set;}
    public property string Name {get; set;}
}
public class DataController : ApiController {
    // ...
    // GET api/a
    public IEnumerable<A> Get()
    {
        // fetches all As from the database
        // the facade creates instances of A and Type as required
        // (if A has a 'Type' the Type will also fetched an set)
        return facade.GetAll<A>();
    }
    // ...
}
[
    {"Id":1,
     "Type": {"Id":1, "Name":"name1"}
    },
    {"Id":2,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":3,
     "Type": {"Id":1, "Name":"name1"}
    }
    {"Id":4,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":5,
     "Type": {"Id":2, "Name":"name2"}
    },  
]
[
    {"Id":1,
     "TypeId": {"Id":1}
    },
    {"Id":2,
     "TypeId": {"Id":2}
    },
    {"Id":3,
     "TypeId": {"Id":1}
    }
    {"Id":4,
     "TypeId": {"Id":2}
    },
    {"Id":5,
     "TypeId": {"Id":2}
    },  
]
export class A {
    public Id: number;
    public Name: string;
    public Type: Type;

    public static fromData(data: any): A {

        var a = new A();
        a.Id = data.Id;
        a.Name = data.Name;
        a.Type = Type.fromData(data.Type);

        return a;
    }
}

export class Type {
        public Id: number;
        public Name: string;

        public static fromData(data: any) : Type {
            var type = new Type();
            type.Id = data.Id;
            type.Name = data.Name;

            return type;
        }
}

// AngularJS controller
export class AListCtrl {
    static $inject = ['$scope', '$http'];

    public As: A[] = [];

    constructor(private $scope, private $http) {
        $scope.AListCtrl = this;

        $http.get('http://localhost/api/a').success((data) => {
            var as: A[] = [];
            for (var i = 0; i < data.length; i++) {
                var aData = data[i];
                var a = A.fromData(aData);

                as.push(a);
            }

            this.As = as;
        });
    }
}
所以我们只得到类型的Id。但是,我们必须请求所有可用的类型,以确定在相应的As中必须设置哪种类型。哪一个可能不好?我想这可能会很慢,因为我必须发送两个查询。

第三个选项可能是在同一个JSON结果中发送所有可用类型和As。

[
    {"Types": 
        [
                {"Id":1, "Name":"name1"},
                {"Id":2, "Name":"name2"},
        ]
    },
    {"As":
        [
            {"Id":1,
            "TypeId": {"Id":1}
            },
            {"Id":2,
             "TypeId": {"Id":2}
            },
            {"Id":3,
             "TypeId": {"Id":1}
            }
            {"Id":4,
             "TypeId": {"Id":2}
            },
            {"Id":5,
             "TypeId": {"Id":2}
            }
        ]
    }
]
所以我想知道是否有一个最佳的做法。一次又一次地发送相同的对象(类型)作为一个嵌套的对象,这看起来相当“愚蠢”

特别是如果我将JSON字符串转换为Typescript对象。

没有任何“存储/缓存”逻辑,我一遍又一遍地创建“相同”对象:

public class A {
    public property int Id {get; set;}
    public property string Name {get; set;}
    public property Type Type {get; set;}
}

public class Type {
    public property int Id {get; set;}
    public property string Name {get; set;}
}
public class DataController : ApiController {
    // ...
    // GET api/a
    public IEnumerable<A> Get()
    {
        // fetches all As from the database
        // the facade creates instances of A and Type as required
        // (if A has a 'Type' the Type will also fetched an set)
        return facade.GetAll<A>();
    }
    // ...
}
[
    {"Id":1,
     "Type": {"Id":1, "Name":"name1"}
    },
    {"Id":2,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":3,
     "Type": {"Id":1, "Name":"name1"}
    }
    {"Id":4,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":5,
     "Type": {"Id":2, "Name":"name2"}
    },  
]
[
    {"Id":1,
     "TypeId": {"Id":1}
    },
    {"Id":2,
     "TypeId": {"Id":2}
    },
    {"Id":3,
     "TypeId": {"Id":1}
    }
    {"Id":4,
     "TypeId": {"Id":2}
    },
    {"Id":5,
     "TypeId": {"Id":2}
    },  
]
export class A {
    public Id: number;
    public Name: string;
    public Type: Type;

    public static fromData(data: any): A {

        var a = new A();
        a.Id = data.Id;
        a.Name = data.Name;
        a.Type = Type.fromData(data.Type);

        return a;
    }
}

export class Type {
        public Id: number;
        public Name: string;

        public static fromData(data: any) : Type {
            var type = new Type();
            type.Id = data.Id;
            type.Name = data.Name;

            return type;
        }
}

// AngularJS controller
export class AListCtrl {
    static $inject = ['$scope', '$http'];

    public As: A[] = [];

    constructor(private $scope, private $http) {
        $scope.AListCtrl = this;

        $http.get('http://localhost/api/a').success((data) => {
            var as: A[] = [];
            for (var i = 0; i < data.length; i++) {
                var aData = data[i];
                var a = A.fromData(aData);

                as.push(a);
            }

            this.As = as;
        });
    }
}
导出A类{
公众Id:号码;
公共名称:字符串;
公共类型:类型;
公共静态fromData(数据:任意):A{
var a=新的a();
a、 Id=data.Id;
a、 Name=data.Name;
a、 Type=Type.fromData(data.Type);
返回a;
}
}
导出类类型{
公众Id:号码;
公共名称:字符串;
公共静态fromData(数据:任意):类型{
变量类型=新类型();
type.Id=data.Id;
type.Name=data.Name;
返回类型;
}
}
//AngularJS控制器
导出类AListCtrl{
静态$inject=['$scope','$http'];
公共As:A[]=[];
构造函数(私有$scope,私有$http){
$scope.AListCtrl=this;
$http.get('http://localhost/api/a)。成功((数据)=>{
var为:A[]=[];
对于(变量i=0;i


创建代表相同类型的不同对象似乎是错误的。因为我经常使用其他语言(C#、Java、C++)的引用。使用这种反序列化数据和创建对象的方法根本不允许使用引用(这在web应用程序中可能是错误的?)。我还认为,生成大量无用对象而不是每种类型生成一个是对内存和CPU时间的浪费

这篇文章很长,但我希望它能很好地解释我的问题。


总结一下:我需要一个最佳实践,通过JSON将数据从服务器发送到客户端,使用客户端的JSON字符串创建对象。

我认为您需要定义最适合您的客户端应用程序的JSON表示,然后确保数据以该格式发送。我将创建一个自定义序列化程序,可以使用内置的序列化程序,也可以使用提供的序列化程序。仅仅使用内置的序列化程序似乎不会产生理想的结果。

我个人的观点是,JSON是由数据驱动的。从这个意义上讲,它应该独立于客户机或您正在使用的数据库结构

例如,如果您检索一些
Foo
,JSON应该包含客户机
可能想要使用的所有相关信息,否则您将重写为每个潜在客户机提供API的数据。我倾向于从第三方的角度来看待它

  • 我需要知道一些关于备份数据库的信息吗:不需要
  • 我是否希望API能够轻松地为我提供大量相关信息:是的
总而言之:

[
    {"Id":1,
     "Type": {"Id":1, "Name":"name1"}
    },
    {"Id":2,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":3,
     "Type": {"Id":1, "Name":"name1"}
    }
    {"Id":4,
     "Type": {"Id":2, "Name":"name2"}
    },
    {"Id":5,
     "Type": {"Id":2, "Name":"name2"}
    },  
]

在您的情况下似乎是有效的。

API设计中的一条经验法则是精心设计json响应,以满足您的客户端需求。我相信一些REST纯粹主义者不会同意,但在现实世界的应用程序中,减少XHR请求和客户端处理比坚持教条式的资源建模方法更可取


让您的数据库/服务器端对象模型抽象泄漏到您的客户机中,最终将耗费您的时间和精力。如果您序列化数据以满足客户的需求,那么即使后端实现发生更改,您也可以保留该接口。

这是一个有趣的问题,我认为没有一个正确的答案适合所有可能的情况

一般来说,第三种选择是我首先选择的。这是正确的,因为它允许您不复制相同的数据(类型),并且它是有效的,因为所有数据都可以通过一个请求获得,这有助于减少发送给服务器的XHR请求的开销

现在,其他两种选择对于不同的场景可能都有一些优点。例如,如果数据集很小,您可能不想复制类型数据

我要记住的一点是选择适合前端代码的最佳表示。除非这是一个您希望与其他客户端共享的通用API,否则我建议让后端和前端协同工作,以获得最佳速度,同时让您的代码在客户端更简单

希望能有所帮助

我通常会让c#视图模型与javascript完全匹配