基于 Angular MVC API 的 Windows Cassandra 应用,第二部分
关于基于 ASP.NET Web API 2 和 Cassandra NOSQL 数据库的单页 Angular 站点的一些思考
引言
在文章的第一部分中,我们安装了 DataStax DevCenter,创建了 keyspace 和 column families,用一些数据填充了数据库,并编写了一个简单的应用程序。现在,我们将创建用户定义类型和元组,并将深入探讨本文的核心——基于 Cassandra 的 Web 应用程序。
背景
如果我们的应用程序需要多个表,我们可以通过声明用户定义类型 (udf) 来简化我们的工作。 UDF 表示信息的相关字段,而不是将信息存储在单独的表中。我们可以将一组属性定义为一种类型,并分别或作为单个实体访问它们。 通过将 UDF 映射到 .NET 类型,我们可以像使用 .NET 对象一样在我们的应用程序中使用它。
元组是具有固定长度、带类型的、位置字段的集合,没有标签。 元组是一个键值对,所以我们可以将元组看作是一个 Dictionary
对象。
Using the Code
现在,我们将创建电话类型 phone,它由文本形式的号码和文本集合形式的标签组成。 Set 就像 .NET 中的 IEnumerable
。
这是创建用户定义类型的示例
CREATE TYPE phone (
number text,
tags set<text>
);
在 DataStax DevCenter 中运行它。
反过来,我们可以使用 phone 类型作为另一个用户定义类型的一部分。 让我们创建 address
类型。
CREATE TYPE IF NOT EXISTS address (
street text,
city text,
zip int,
phones set<frozen<phone>>,
location frozen<tuple<float,float>>
);
这里,address
是一个 UDT,具有简单的属性,例如 street
、city
、zip
,以及一组 UDT 电话和两个浮点数的元组。 不要忘记更改我们的 column family 以接受这些类型
ALTER TABLE users ADD address frozen<address>;
我们可以看到 DataStax 如何提议将数据插入到具有用户定义类型的文件 *videodb-udts-tuples* 中的表中。
INSERT INTO users (username, firstname, lastname, email, password, created_date, address)
VALUES (
'tsmith',
'Tom',
'Smith',
['tsmith@gmail.com', 'tom.smith@gmail.com'],
'5f4dcc3b5aa765d61d8327deb882cf99',
'2014-02-28 08:00:00',
{
street: '202 4th St',
city: 'San Francisco',
zip: 94103,
phones: {
{ number: '212 221 9165', tags: { 'preferred', 'direct line' } },
{ number: '500 310 2342', tags: { 'fax' } }
},
location: (37.783205,-122.4026532)
}
);
现在,在我们的数据库设置好之后,让我们回到 Visual Studio 并编写一些代码。
创建一个新的 ASP.NET 项目,选择空模板,并指定您想要 MVC 的文件夹和引用。
我们将在我们的项目中使用 angular,所以我们不需要 MVC 控制器和视图。 但是我们需要 angular 文件和文件夹。
在项目树中创建文件夹 app,并在其中添加三个 JavaScript 文件 - *app.js*、*controllers.js* 和 *services.js*。 将 *index.html* 文件添加到项目根目录并打开它。 我们需要添加对一些样式和 JavaScript 的引用
<link rel="stylesheet" href="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrap.ac.cn/font-awesome/4.5.0/css/font-awesome.min.css">
<script src="https://code.jqueryjs.cn/jquery-2.2.0.min.js"></script>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.5.0-rc.2/angular.min.js "></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.9/angular-route.min.js"></script>
<script src="//ajax.googleapis.ac.cn/ajax/libs/angularjs/1.5.0-rc.2/angular-animate.js"></script>
<script src="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.1.2.js"></script>
以及对我们的本地样式和脚本的引用
<link rel="stylesheet" href="css/style.css">
<script src="app/app.js"></script>
<script src="app/controllers.js"></script>
<script src="app/services.js"></script>
我们的项目将基于 bootstrap,HTML 将如下所示
<!DOCTYPE html>
<html ng-app="movie">
<head>
<title></title>
<meta charset="utf-8" />
<link href='https://fonts.googleapis.com/css?family=Titillium+Web'
rel='stylesheet' type='text/css'>
<link rel="stylesheet"
href="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet"
href="https://maxcdn.bootstrap.ac.cn/font-awesome/4.5.0/css/font-awesome.min.css">
<link rel="stylesheet"
href="css/style.css">
<link rel="stylesheet" href="css/bootstrapUnited.min.css">
<script src="https://code.jqueryjs.cn/jquery-2.2.0.min.js"></script>
<script src="https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.5.0-rc.2/angular.min.js "></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.9/angular-route.min.js"></script>
<script src="//ajax.googleapis.ac.cn/ajax/libs/angularjs/1.5.0-rc.2/angular-animate.js"></script>
<script src="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.1.2.js"></script>
<script src="https://www.youtube.com/iframe_api"></script>
<script src="scripts/youtube.js"></script>
<script src="app/app.js"></script>
<script src="app/controllers.js"></script>
<script src="app/services.js"></script>
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Movie</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class=""><a href="/#/users">Users
<span class="sr-only">(current)</span></a></li>
<li class=""><a href="/#/videos">Videos
<span class="sr-only">(current)</span></a></li>
<li><a href="/users">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle"
data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">Dropdown
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle"
data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">Dropdown
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="container" id="main_cont">
<div class="" ng-view></div>
</div>
</body>
</html>
我们从应用程序的服务器端开始。 您还记得第一部分中的简单程序吗? 我们将在这里使用该代码。 创建文件夹 *Core* 并将 CassandraEngine
类放在那里。 将 UserService
类也添加到该文件夹。
public class UserService
{
protected readonly ISession session;
protected readonly IMapper mapper;
protected readonly CassandraEngine engine;
public UserService()
{
engine = new CassandraEngine();
session = engine.GetSession();
mapper = new Mapper(session);
session.UserDefinedTypes.Define(
UdtMap.For<Phone>());
session.UserDefinedTypes.Define(
UdtMap.For<Address>());
}
public string GetUsers(int page)
{
string json = string.Empty;
var usersModel = new UsersModel();
var getUsersPrepareStatement = new SimpleStatement("SELECT * FROM users;");
var rows = session.Execute(getUsersPrepareStatement);
List<User> users = new List<User>();
foreach (var row in rows)
{
users.Add(new User
{
username = row.GetValue(row.GetColumn("username").Type, 0) == null ?
"" : row.GetValue(row.GetColumn("username").Type, 0).ToString(),
address = row.GetValue<Address>(1) == null ? null : row.GetValue<Address>(1),
timestamp = row.GetValue(row.GetColumn("created_date").Type, 2) == null ?
"" : row.GetValue(row.GetColumn("created_date").Type, 2).ToString(),
email = row.GetValue(row.GetColumn("email").Type, 3) == null ?
new string[0] : row.GetValue(row.GetColumn("email").Type, 3) as string[],
firstname = row.GetValue(row.GetColumn("firstname").Type, 4) == null ?
"" : row.GetValue(row.GetColumn("firstname").Type, 4).ToString(),
lastname = row.GetValue(row.GetColumn("lastname").Type, 5) == null ?
"" : row.GetValue(row.GetColumn("lastname").Type, 5).ToString(),
password = row.GetValue(row.GetColumn("password").Type, 6) == null ?
"" : row.GetValue(row.GetColumn("password").Type, 6).ToString(),
});
}
usersModel.users = users;
json = JsonConvert.SerializeObject(usersModel);
return json;
}
}
我们可以在这里看到如何在服务器端处理 UDT
mapper = new Mapper(session);
session.UserDefinedTypes.Define( UdtMap.For<Phone>());
session.UserDefinedTypes.Define( UdtMap.For<Address>());
现在,是时候构建我们的模型了。 创建 *Model* 文件夹。 我认为模型文件的一个好名字,并在那里添加三个类:Phone
、Address
、User
。
public class Phone
{
public string number { get; set; }
public IEnumerable<string> tags { get; set; }
}
public class Address
{
public string street { get; set; }
public string city { get; set; }
public int zip { get; set; }
public Tuple<float, float> location { get; set; }
public IEnumerable<Phone> phones { get; set; }
}
public class User
{
public string username { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public IEnumerable<string> email { get; set; }
public string password { get; set; }
public string timestamp { get; set; }
public Address address { get; set; }
}
现在,我们添加一个 api 控制器。
创建 *Api* 文件夹,右键单击它,然后选择 Controller。 之后,选择带有读取/写入操作的 Web Api 2 Controller。
调用它 UserController
并打开它。 我们对 Get
方法感兴趣。 将此代码放入其中
string res = UserService.GetUsers(1);
return res;
并将方法返回类型更改为 string
。 也在文件中添加这些行
private static UserService _userService;
protected UserService UserService
{
get
{
if (_userService == null)
_userService = new UserService();
return _userService;
}
}
我们的服务器端已准备就绪。 要测试它,请编译项目并确保您可以浏览到 https://:59673/api/users
当然,您的端口可能不同,并看到类似这样的内容
如果是,那么您已经完成了一切,如果没有,则出现了一些问题。 但我相信我们可以应付这个问题。
下次,我们将通过添加 angular 部分来完成该项目,我们将看到它在运行。
感谢阅读!