Orleans在Github上提供了不少sample代码,在StorageProvider方面有针对MongoDB,Redis,和SQL Server的,但是可能出于人所皆知的原因吧,没有给出MySQL的示例代码。我摸索了不少时间,尤其Orleans 1.2版本发布后代码写法又有变化(其实变化不大),自己又把代码搞乱掉了,折腾了不少时间,总算通过。
其实Orleans用Mysql做持久化的意义不是很大,就好像ASP.NET大多也是和SQL Server搭配,但是看在$$的份上,我觉得还是用处的。
另外orleans给的SQL Server的例子,是把对象数据都序列化JSON或者BSON再储存到SQL,这我就困惑了,难道现在SQL已经能够做到MongoDB这样的NoSQL数据库了?我记得看过文章好像SQL 2016好像是支持NoSQL了,但真的用起来这么简单么?因为很少用SQL,对SQL很不了解,希望高人指点。
关于Orleans的StorageProvider可以参考微软Orleans官方文档:http://dotnet.github.io/orleans/Getting-Started-With-Orleans/Grain-Persistence
1. 基于Orleans 1.1.3的时候,如果要实现持久化状态,用户的grain首先需要扩展自Grain
Grain state will automatically be read when the grain is activated, but grains are responsible for explicitly triggering the write for any changed grain state as and when necessary.
以Person类为例,
Task IPerson.SetPersonalAttributes(PersonalAttributes props)
{
State.FirstName = props.FirstName;
State.LastName = props.LastName;
State.Gender = props.Gender;
return WriteStateAsync();
}
在设定Person的属性的时候,需要调用一下WriteStateAsync()。
Orleans 1.2版本发布后,不再要求从GrainState扩展,而是可以基于POCO类(就是简单得不能再简单 没有任何引用的class),例如:
namespace QModels.Foundation
{
///
/// QUser是系统所有用户的基础模型
///
public class QUser
{
public int ID { get; set; }
public string Mobile { get; set; }
public string Name { get; set; }
}
}
这个解决了我的一个大问题,因为我喜欢把所有的模型都放在一个工程项目里面,然后dll给其它工程引用。按照原来的方式,这个模型库就需要引用orleans,并让所有的类都继承GrainState,而现在就不用这么复杂了,整个结构就干净多了。
2. Grain的实现:
GrainInterface上没有什么变化。这里就掠过了。
实现Grain的时候,在Grain(以Person类为例)的定义之前添加[StorageProvider(ProviderName = "TestStore")]标签,用以指明Person的存储提供器的名称。Orleans 1.2版本后,如果要实现带状态持久化,Grain就要从Grain
namespace QGrains
{
[StorageProvider(ProviderName = "mySqlStorage")]
public class QUserGrain : Grain, IQUserGrain
{
......
providerName的值为“MySql.Data.MySqlClient”,不确定是否可以随意写,但是MySQL的我就是这么写的,通过了。
3. Storage Provider的实现:
首先,在这个工程项目中要安装Mysql.Data, Mysql.Data.Entity.EF6, MySQL.Web等几个插件,可以用NuGet安装。
Provider需要添加IStorageProvider接口,并实现包含ReadStateAsync、WriteStateAsync、ClearStateAsync等接口成员,以及Init、Close等初始化和收尾的方法。
namespace QMysqlStorage{
public class StorageBase : IStorageProvider
{
.....
}}
Orleans 1.2版本以后,ReadStateAsync等接口有所变化,原来是Task ReadStateAsync(string grainType, GrainReference grainReference, GrainState grainState),现在第三个参数改为接口注入Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState),这样grainState参数就带了一个State字段,其实就是模型的实例数据了。
public async Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
{
int primaryKey =(int) grainReference.GetPrimaryKeyLong();
using (var db= new QDbContext(connectionString))
{
grainState.State = db.qusers.Find(primaryKey);
grainState.ETag = Guid.NewGuid().ToString();
} }
注意我这里把原来Orleans官方例子中KeyValueStore的方式删除了,还是使用表方式实现存储,为的是后续通过我比较熟悉的Sql Query做查询。
4. 如果用EF实现ORM,则要给StorageProvider中dBContext加上标签:
[DbConfigurationType(typeof(MySqlEFConfiguration))]
namespace QMysqlStorage
{
//[DbConfigurationType(typeof(KeyValueDbConfiguration))] //这是原来针对SQL Server存储层的标签
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class QDbContext : System.Data.Entity.DbContext
{
public QDbContext(string connString) : base(connString)
{
Database.SetInitializer(new MysqlInitializer(connString));
}
public DbSet qusers { get; set; }
}
}
DbContext可以指定一个初始化器,这里是MysqlInitializer。
基本上这样就可以实现Orleans基于MySQL的持久化了。