我们设计数据库一般设计方法是横向设计(横向扩展),例如用户表,我们一般设计方法是主表用户表,附表是用户简介表:
那么这样设计对吗?我先不回答这个问题。首先主表的信息是我们经常用到的信息,附表的信息是偶尔用到的信息。那么假设,我们有一天需要增加需求通过第三方的平台登录,并且记录用户相关信息,怎么办?唯一的办吧是横向扩展数据库。。Oh MyGod!!!这个动作可不小!
那么有没有方法来避免这个问题呢?回答是肯定的——通用属性框架!什么叫做通用属性框架?就是说,我们把一些容易扩展(将来扩展)的对象放到一个容器中存储,扩展只需要纵向扩展就好了!数据库纵向扩展无非就是增加一条数据而已嘛!如下图的设计:
解释下:
Id :主键 int 自增
EntityId :实体主键,int 引用(其他属性实体的主键)
EntityGroupKey :实体类型名称(ToString的字符串就可以)
AttributeName :属性名称
AttributeValue :属性的值
这样表我们就设计好了。
那么如何进行关联呢,看看下图:
通用属性表被多张表引用,例如用户、类别等,如果用户有一个属性名为性别,那么通用属性的数据如下:
理论数据我们说通了,那么程序如何实现呢?
首先我们增加一个接口IGenericAttributeService
/// <summary>
/// 通用用户属性服务接口
/// </summary>
public interface IGenericAttributeService: IApplicationService
{
/// <summary>
/// 删除属性
/// </summary>
/// <param name="attribute">属性</param>
void DeleteAttribute(GenericAttribute attribute);
/// <summary>
/// 删除属性
/// </summary>
/// <param name="attributes">属性</param>
void DeleteAttributes(IList<GenericAttribute> attributes);
/// <summary>
/// 删除属性
/// </summary>
/// <param name="attributeId"></param>
void DeleteAttribute(int attributeId);
/// <summary>
/// 删除属性
/// </summary>
/// <param name="attributeIds"></param>
void DeleteAttribute(int[] attributeIds);
/// <summary>
/// 获取属性
/// </summary>
/// <param name="attributeId">主键</param>
/// <returns>一个属性</returns>
GenericAttribute GetAttributeById(int attributeId);
/// <summary>
/// 新增属性
/// </summary>
/// <param name="attribute">属性</param>
void InsertAttribute(GenericAttribute attribute);
/// <summary>
/// 更新属性
/// </summary>
/// <param name="attribute">属性</param>
void UpdateAttribute(GenericAttribute attribute);
/// <summary>
/// 获取属性
/// </summary>
/// <param name="entityId">实体主键</param>
/// <param name="keyGroup">群组</param>
/// <returns>属性群组</returns>
IList<GenericAttribute> GetAttributesForEntity(int entityId, string keyGroup);
/// <summary>
/// 保存属性
/// </summary>
/// <typeparam name="TPropType">属性</typeparam>
/// <param name="entity">实体</param>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
void SaveAttribute<TPropType>(Entity entity, string key, TPropType value);
}
这里我解释下,我用的abp框架,所以所有实体都是继承自Entity,建议大家使用CodeFirst开发的时候也有一个基类实体。
这样我们就可以保存实体对应的数据了,那么我要保存一个用户的性别怎么办?首先我把所有的扩展的属性名称,写到了一个静态类里面,在针对这个用户实体写一个扩展类GenericAttributeExtensions
/// <summary>
/// 获取实体的属性
/// </summary>
/// <typeparam name="TPropType">属性类型</typeparam>
/// <param name="entity">Entity</param>
/// <param name="key">Key</param>
/// <returns>Attribute</returns>
public static TPropType GetAttribute<TPropType>(this Entity entity, string key)
{
var genericAttributeService = IocManager.Instance.Resolve<IGenericAttributeAppService>();
return GetAttribute<TPropType>(entity, key, genericAttributeService);
}
/// <summary>
/// 获取实体的属性
/// </summary>
/// <typeparam name="TPropType">属性类型</typeparam>
/// <param name="entity">Entity</param>
/// <param name="key">Key</param>
/// <param name="genericAttributeService">通用属性服务</param>
/// <returns>Attribute</returns>
public static TPropType GetAttribute<TPropType>(this Entity entity,
string key, IGenericAttributeAppService genericAttributeService)
{
if (entity == null)
throw new ArgumentNullException("entity");
string keyGroup = entity.GetUnproxiedEntityType().Name;
var props = genericAttributeService.GetAttributesForEntity(entity.Id, keyGroup);
if (props == null)
return default(TPropType);
props = props.ToList();
if (!props.Any())
return default(TPropType);
var prop = props.FirstOrDefault(ga =>
ga.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));
if (prop == null || string.IsNullOrEmpty(prop.Value))
return default(TPropType);
return CommonHelper.To<TPropType>(prop.Value);
}
}
这样就所有的实体都有了进行读取通用属性的方法了,我没有写存储的方法,建议大家按照例子写一个存储的方法。
欢迎来到qq群进行讨论:68848430
扫一扫更多内容在历史记录