FluentApi能否实现自定义特性
Feature 特性
能否在FluentApi中加入自定义特性支持
简要描述原因
受#407 回复启发,在https://github.com/dotnetcore/FreeSql/wiki/FluentApi 中,可以引用默认的特性,而目前因为Table属性会发生失效的情况,通过折中方式实现了。
freesql.Aop.ConfigEntity += (s, e) =>
{
//修改默认的模式名称,默认为public
var attr=e.EntityType.GetCustomAttributes(typeof(MyTableAttribute), false).FirstOrDefault() as MyTableAttribute;
if (attr != null)
e.ModifyResult.Name = "xxxxx."+attr.TableName; //自定义特性表名
//if (!e.ModifyResult.Name.Contains("."))
// e.ModifyResult.Name = "xxxxx." + e.ModifyResult.Name;
//System.Console.WriteLine(e.ModifyResult.Name);
};
----------------------------------------------------------
[MyTable(TableName = "flow_model_cfg")]
[Table(Name="flow_model_cfg")]
public class FlowModelCfg {...}
----------------------------------------------------------
public class MyTableAttribute:System.Attribute
{
public string TableName { get; set; }
}
但个人更倾向于FluentApi,而不是实体特性,这样不对实体类产生太多的侵入性。
使用场景
针对各种个性化特性的场景
补充一下,是针对postgresql场景,发现不但表名Name失效,连列名也失效了, 还要增加
fsql.Aop.ConfigEntityProperty += (s, e) => {
var attr = e.Property.GetCustomAttributes(typeof(MyColumnAttribute), false).FirstOrDefault() as MyColumnAttribute;
if (attr != null && e.ModifyResult.Name != attr.Name)
e.ModifyResult.Name = attr.Name; //字段名
};
freesql.Aop.ConfigEntity+=(s,e)=>{
var name = e.EntityType.GetCustomAttribute<TableAttribute>()?.Name ?? e.EntityType.Name;
if (name.Contains(".") == false)
e.ModifyResult.Name="xxxxx." + name;
}

freesql.Aop.ConfigEntity+=(s,e)=>{
var name = e.EntityType.GetCustomAttribute<TableAttribute>()?.Name ?? //特性
freesql.CodeFirst.GetConfigEntity(e.EntityType))?.Name ?? //FluentApi
e.EntityType.Name;
if (name.Contains(".") == false)
e.ModifyResult.Name="xxxxx." + name;
};
大佬牛逼,测试可以了,但还是有个问题 。
fsql.Aop.ConfigEntityProperty += (s, e) => {
var attr = e.Property.GetCustomAttributes(typeof(MyColumnAttribute), false).FirstOrDefault() as MyColumnAttribute;
if (attr != null && e.ModifyResult.Name != attr.Name)
e.ModifyResult.Name = attr.Name; //字段名
};
这部分FluentApi代码,不知道要怎么实现,查了一下,ICodeFirst好像没有获取ColumnAttributer的方法
ColumnAttribute 直接用反射获取
_PostgreSqlFreeSql.Aop.ConfigEntityProperty += (s, e) =>
{
var attr = e.Property.GetCustomAttributes(typeof(ColumnAttribute), false).FirstOrDefault() as ColumnAttribute;
if (attr != null
&& (string.IsNullOrEmpty(e.ModifyResult.Name)
|| (!string.IsNullOrEmpty(e.ModifyResult.Name) && !e.ModifyResult.Name.Equals(attr.Name))))
e.ModifyResult.Name = attr.Name;
};
看文档的方法,反射获取为空,大佬还有其他办法吗?
看来还是得发个版本,前面给你的方案有个私有成员
v3.2.670-preview22020918 最终方案:
freesql.Aop.ConfigEntity += (s, e) =>
{
var name = e.ModifyResult.Name; //可直接获取 FluentApi/Attirubte 生效的内容
if (name.Contains(".") == false)
e.ModifyResult.Name="xxxxx." + name;
};
freesql.Aop.ConfigEntityProperty += (s, e) =>
{
var name = e.ModifyResult.Name; //可直接获取 FluentApi/Attirubte 生效的内容
};
这个还是没办法出来。读了一下源码,并加到项目上打断点,发现有几个奇怪的地方:
1、\FreeSql\Internal\CommonUtils.cs,这个文件,_mappingPriorityTypes 似乎写死了,并没有应用FreeSqlBuilder.UseMappingPriority的传值
2、\FreeSql\DataAnnotations\TableAttribute.cs,这个TableAttribute类并没有提供_columns的方法,兄弟不才,加了一个函数getColumns()

public ConcurrentDictionarygetColumns() { return _columns; }
最终试验成功
关键代码如下:
_PostgreSqlFreeSql.Aop.ConfigEntity += (s, e) =>
{
var attr = e.EntityType.GetCustomAttribute()?? //特性
_PostgreSqlFreeSql.CodeFirst.GetConfigEntity(e.EntityType); ////FluentApi
if (attr != null
&& (string.IsNullOrEmpty(e.ModifyResult.Name)
|| (!string.IsNullOrEmpty(e.ModifyResult.Name) && !e.ModifyResult.Name.Equals(attr.Name))))
e.ModifyResult.Name = "qzhalinxh." + attr.Name;
}
_PostgreSqlFreeSql.Aop.ConfigEntityProperty += (s, e) =>
{
var attr = _PostgreSqlFreeSql.CodeFirst.GetConfigEntity(e.EntityType);
if (attr == null) return;
var columns = attr.getColumns().Where(p => p.Key.ToLower() ==
e.ModifyResult.Name.ToLower()).FirstOrDefault().Value;
if (attr != null && columns!=null && columns.Name != e.ModifyResult.Name)
e.ModifyResult.Name = columns.Name;
}
请评估一下能否在TableAttribute类增加一个 getColumns函数来获取列信息?
1、FreeSqlBuilder Build 时会重新设置 _commonUtils._mappingPriorityTypes 2、昨天回复正是不愿意增加 getColumns 类似的方法才回复的
刚才测试结果:
[Table(Name = "AAA_attr")]
public class AAA
{
[Column(Name = "aa_attr")]
public int aa { get; set; }
}
var fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, "data source=:memory:")
.UseAutoSyncStructure(true)
.UseNoneCommandParameter(true)
.UseMappingPriority(MappingPriorityType.Attribute, MappingPriorityType.FluentApi, MappingPriorityType.Aop)
.Build();
fsql.Aop.ConfigEntity += (_, e) =>
{
Console.WriteLine("Aop.ConfigEntity: " + e.ModifyResult.Name);
};
fsql.Aop.ConfigEntityProperty += (_, e) =>
{
Console.WriteLine("Aop.ConfigEntityProperty: " + e.ModifyResult.Name);
};
fsql.Select<AAA>();
fsql.CodeFirst.ConfigEntity<AAA>(t =>
{
t.Name("AAA_fluentapi");
t.Property(a => a.aa).Name("AA_fluentapi");
});
fsql.Select<AAA>();
打印:
Aop.ConfigEntity: AAA_attr
Aop.ConfigEntityProperty: aa_attr
Aop.ConfigEntity:
Aop.ConfigEntity: AAA_fluentapi
Aop.ConfigEntityProperty: AA_fluentapi
Aop.ConfigEntity:
UseMappingPriority 优先级是 Attribute 最小,其次 FluentApi,最大 Aop。
FluentApi 可以复盖 Attribute 设置,Aop 可以复盖 FluentApi 设置。
上面两个为空,是获取 IndexAttribute 造成的,对 TableAttribute 结果没有影响。
第一次 .Select<T> 获取元数据,缓存起来
fluentapi 会移除元数据缓存,因此第二次 .Select<T> 又获取了一次元数据,再缓存起来。
