给大家说说我们公司的一个稀奇的需求吧
数据库设计时,只给自己的内部公司使用,所以里面的所有实体都是使用的 int 类型的 Id ;
后来,突然要求接入外部公司了,老板于是要求所有对外的 Id 均不可使用数字 Id; 免得什么什么什么的 (涉及大约 200 个接口),悲催的是数据库里面一堆的关联,要改也不好改。。
好消息是对外的接口暂时未发布,所以不存在对外的兼容性问题;
解决方案1.
修改实体定义,
public int PartnerId { get; set; } ===>
[Column(TypeName = "int")]
public IdAndXid PartnerId { get; set; }
改为另外一个类; 这个类中 Id 为 JsonIgnor, Xid 则利用一个稀奇古怪的加解密算法计算为加密后的Id字符串;
貌似一切正常,就这么办。。
但是好景不长,过了一会儿,
发现这样的定义了,
public int? PartnerId { get; set; } ===>
[Column(TypeName = "int")]
public IdAndXid PartnerId { get; set; }
这儿 EF 非常坚定的要求 PartnerId 必须不为 null, 貌似解决不了;
于是尝试另外一个解决方法:
解决方法2。 (Json序列化和反序列化,我们使用的是Json.Net)
1. 定义 CryptIdAttribute, 然后在需要加密的 Property 上增加此 Attribute
/// <summary>
/// 加密的Id标注
/// </summary>
public class CryptIdAttribute : Attribute
{
/// <summary>
/// 构造函数
/// </summary>
public CryptIdAttribute() { }
/// <summary>
/// 加密的类型
/// </summary>
public Type Type { get; set; }
}
2. 再定义一个 Newtonsoft.Json.Serialization.DefaultContractResolver 的子类,
覆盖其 CreateProperties 方法,
将标记了 CryptIdAttribute 了的 Property
单独使用自定义的 CryptIdJsonProperty 进行处理;
注意,我这儿增加另外的一个处理,即对于 TypeSciptIgnore 标记的属性,也不进行Json序列化,和本文无关;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace hongbao.Json
{
/// <summary>
/// 输出到json字符串时,包含有TypeScriptIgnore标记的属性不进行序列化
/// </summary>
public class IgnoreTypeScriptResolver : DefaultContractResolver
{
/// <summary>
/// TypeScriptIgnoreAttribute 标记类;
/// </summary>
static Type TypeSciptIgnoreType = typeof(TypeScriptIgnoreAttribute);
/// <summary>
/// TypeScriptIgnoreAttribute 标记类;
/// </summary>
static Type CryptIdType = typeof(CryptIdAttribute);
[ThreadStatic]
PropertyInfo[] ignoreProperty;
[ThreadStatic]
Tuple<PropertyInfo,CryptIdAttribute>[] cryptIdProperty;
/// <summary>
/// Json序列化时,将传入 type, 以解决数据协议;
/// 此处查询所有包含有 TypeSciptIgnoreType 标注 的属性出来;
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public override JsonContract ResolveContract(Type type)
{
//cryptIdProperty = new Tuple<PropertyInfo, CryptIdAttribute>[] { };
cryptIdProperty = type.GetProperties() // 异常。。。Newtonsoft中抛出
.Select(a => new { Prop = a, Attr = a.GetCustomAttributes(CryptIdType, true).FirstOrDefault() as CryptIdAttribute })
.Where(b => b.Attr != null)
.Select(c => Tuple.Create(c.Prop, c.Attr))
.ToArray();
ignoreProperty = type.GetProperties().Where(a => a.GetCustomAttributes(TypeSciptIgnoreType, true).Length > 0).ToArray();
return base.ResolveContract(type);
}
/// <summary>
/// Json序列化时,将传入 type 以获取需要序列化的属性;
/// 获取需要序列化的属性, 但是忽略掉 包含有 TypeSciptIgnoreType 标注 的属性
/// </summary>
/// <param name="type"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override IList<JsonProperty> CreateProperties(Type type,
MemberSerialization memberSerialization)
{
IList<JsonProperty> list = base.CreateProperties(type, memberSerialization);
if (ignoreProperty == null) return list;
var result = list.Where(jsonProperty => !ignoreProperty.Any(c => c.Name == jsonProperty.PropertyName) &&
!cryptIdProperty.Any(a=> a.Item1.Name == jsonProperty.PropertyName)
)
.Union(cryptIdProperty.Select(a => new CryptIdJsonProperty(a.Item1, a.Item2.Type)))
.ToList();
return result;
}
}
}
3. CryptIdJsonProperty 大致构造函数如下:
/// <summary>
/// 对于 CryptIdAttribute 标记的属性,使用 CryptIdJsonPropertyp 进行进行处理
/// </summary>
public class CryptIdJsonProperty : JsonProperty
{
/// <summary>
///
/// </summary>
/// <param name="property"></param>
/// <param name="cryptType"></param>
public CryptIdJsonProperty(PropertyInfo property, Type cryptType)
{
this.PropertyName = property.Name;
this.ValueProvider = new ReflectionValueProvider(property);
this.Converter = new CryptIdConvert(false, cryptType); //必须提供;
this.Writable = true;
this.Readable = true;
this.PropertyType = property.PropertyType;
this.ShouldSerialize = new Predicate<object>((xx) => true);
this.GetIsSpecified = null; // new Predicate<object>((xx) => true);
this.SetIsSpecified = null; // new Predicate<object, object>((xx, yy) => true);
}
}
4. 再定义一个 CryptIdConvert 类继承 JsonConverter ,
/// <summary>
/// CryptIdAttribute标记的属性,使用 CryptIdConvert 进行转换
/// </summary>
public class CryptIdConvert : JsonConverter
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="allowNull"></param>
/// <param name="cryptType"></param>
public CryptIdConvert(bool allowNull, Type cryptType)
{
this.allowNull = allowNull;
this.cryptType = cryptType;
}
private Type cryptType;
private bool allowNull = false;
/// <summary>
/// 能否转换
/// </summary>
/// <param name="objectType">对象的类型;</param>
public override bool CanConvert(Type objectType)
{
return true;
}
/// <summary>
/// 从字符串转换会 int? 或者 int, 解密成id
/// </summary>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = (string)reader.Value;
if (value == null || value == "") return null;
return SecurityUtil.DecryptIdInGuid(value, objectType.Name);
}
/// <summary>
/// 从字符串转换会 int? 或者 int, 加密成id
/// </summary>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null) { writer.WriteValue(value); return; }
string val = "";
if (allowNull)
{
var id = (int?)(value);
if (id.HasValue) val = SecurityUtil.CryptIdInGuid(id, cryptType);
else
{
writer.WriteValue((object)null);
return;
}
}
else
{
var id = (int)(value);
val = SecurityUtil.CryptIdInGuid(id, cryptType);
}
writer.WriteValue(val);
}
}
5. 定义Json序列化方法 , 一般是在你自己的 JsonResult 的子类中覆盖其 ExecuteResult 方法;
#region 实现JsonResult的输出方法
/// <summary>
/// 覆盖 JsonResult 的输出方法;
/// </summary>
/// <param name="context"></param>
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if ((this.JsonRequestBehavior == JsonRequestBehavior.DenyGet)
&& string.Equals(context.HttpContext.Request.HttpMethod,
"GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("不允许进行GET操作");
}
HttpResponseBase response = context.HttpContext.Response;
if (!string.IsNullOrEmpty(this.ContentType))
{
response.ContentType = this.ContentType;
}
else
{
response.ContentType = "application/json";
}
if (this.ContentEncoding != null)
{
response.ContentEncoding = this.ContentEncoding;
}
var obj = new
{
code,
message,
data = this.Data,
token
};
IsoDateTimeConverter timeConvert = new IsoDateTimeConverter()
{
DateTimeFormat = "yyyy-MM-dd HH:mm:ss"
};
var JsonSetting = new JsonSerializerSettings
{
Converters = new JsonConverter[] { timeConvert },
ContractResolver = new IgnoreTypeScriptResolver(), //不输出 IgnoreTypeScript的标记属性;
};
string jsonContent = JsonConvert.SerializeObject(obj, Formatting.None, JsonSetting);
response.Write(jsonContent);
}
#endregion
不得不说, Json.Net 非常灵活;