我开发了一个我认为会有很多用户的系统。每个用户都有一个在应用程序内表示的配置文件作为记录。为了存储用户的个人资料,我执行以下base64:encode_to_string(term_to_binary(Profile))
,所以基本上是以序列化的方式存储的个人资料。
到目前为止一切都很好。现在问题是:
我不时会计划通过添加和删除其中的某些字段来扩展配置文件功能。我的问题是在代码中处理这些变化的最佳策略是什么?
我现在看到的方法是做这样的事情:
Profile = get_profile(UserName),
case is_record(Profile, #profile1) of
true ->
% do stuff with Profile#profile1
ok;
_ ->
next
end,
case is_record(Profile, #profile2) of
true ->
% do stuff with Profile#profile2
ok;
_ ->
next
end,
我想知道我的任务是否有更好的解决方案?
其他信息:我使用的是简单的KV存储。它无法存储Erlang类型,这就是我使用State#state.player#player.chips#chips.br
答案 0 :(得分:1)
它在很大程度上取决于记录数量的比例,变化的频率和可接受的中断。由于可维护性,我希望首先将配置文件升级到最新版本。你也可以制作像mnesia一样即时升级的系统。最后有可能保留所有版本的代码,我绝对不会喜欢。这是维护的噩梦。
无论如何,当警卫允许is_record/2
时,我更愿意
case Profile of
X when is_record(X, profile1) ->
% do stuff with Profile#profile1
ok;
X when is_record(X, profile2) ->
% do stuff with Profile#profile2
ok
end
注意没有catch all子句,因为你对未知记录类型会做什么?这是错误所以快速失败!
您还有许多其他选择,例如像黑客一样:
case element(1,Profile) of
profile1 ->
% do stuff with Profile#profile1
ok;
profile2 ->
% do stuff with Profile#profile2
ok
end
或类似
{_, F} = lists:keyfind({element(1,Profile), size(Profile)},
[{{profile1, record_info(size, profile1)}, fun foo:bar/1},
{{profile2, record_info(size, profile2)}, fun foo:baz/1}]),
F(Profile).
以及许多其他可能性。
答案 1 :(得分:1)
最好的方法是获得序列化(配置文件)的副本以及相同但以记录形式的副本。然后,每次对记录表单配置文件进行更改时,也会对同一用户的序列化配置文件进行更改(在同一事务中!)。 修改用户记录配置文件的代码应始终重新计算新的序列化表单,对于您来说,该表单是用户记录的外部表示
-record(record_prof,{name,age,sex}). -record(myuser,{ username, record_profile = #record_prof{}, serialized_profile }).所以无论序列化函数在做什么,就是这样。但是这总是会留下额外的自由配置文件更改。因此,我们始终将序列化配置文件保持为记录配置文件的正确表示。当记录配置文件发生更改时,还必须重新计算序列化表单(事务性)以便具有完整性。
change_profile(Username,age,NewValue)-> %% transaction starts here.... [MyUser] = mnesia:read({myuser,Username}), Rec = MyUser#myuser.record_profile, NewRec = Rec#record_prof{age = NewValue}, NewSerialised = serialise_profile(NewRec), NewUser = MyUser#myuser{ record_profile = NewRec, serialized_profile = NewSerialised }, write_back(NewUser), %% transaction ends here..... ok.
答案 2 :(得分:1)
也许,你可以使用支持者。
假设您已存储了一些用户个人资料。
User = [{name,"John"},{surname,"Dow"}].
store_profile(User).
然后,几年后,您决定使用用户年龄扩展用户个人资料。
User = [{name,"John"},{surname,"Dow"},{age,23}].
store_profile(User).
现在您需要从DB
获取用户个人资料get_val(Key,Profile) ->
V = lists:keyfind(Key,1,Profile),
case V of
{_,Val} -> Val;
_ -> undefined
end.
User = get_profile().
UserName = get_val(name,User).
UserAge = get_val(age,User).
如果您获得“版本2”的用户个人资料,您将获得实际年龄(在此特定情况下为23)。
如果您获得“版本1”(“旧版”)的用户个人资料,您将获得“未定义”作为年龄,然后您可以更新配置文件并将其与新值一起存储,这样它就会成为'新版'实体。
所以,没有版本冲突。
可能这不是最好的方法,但在某些情况下它可能是一种解决方案。
答案 3 :(得分:0)
您可以使用某些可扩展数据序列化格式,例如JSON或Google Protocol Buffers。
这两种格式都支持在不破坏向后兼容性的情况下添加新字段。通过使用它们,您无需为序列化数据结构引入显式版本控制。
在两种格式之间进行选择取决于您的使用案例。例如,使用Protocol Buffers更可靠,而JSON更容易上手。