FreeSql icon indicating copy to clipboard operation
FreeSql copied to clipboard

FluentApi能否实现自定义特性

Open lxh023 opened this issue 3 years ago • 10 comments

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,而不是实体特性,这样不对实体类产生太多的侵入性。

使用场景

针对各种个性化特性的场景

lxh023 avatar Sep 15 '22 22:09 lxh023

补充一下,是针对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; //字段名
};

lxh023 avatar Sep 16 '22 01:09 lxh023

freesql.Aop.ConfigEntity+=(s,e)=>{
    var name = e.EntityType.GetCustomAttribute<TableAttribute>()?.Name ?? e.EntityType.Name;
    if (name.Contains(".") == false)
        e.ModifyResult.Name="xxxxx." + name;
}

2881099 avatar Sep 16 '22 02:09 2881099

Snipaste_2022-09-16_12-30-38

lxh023 avatar Sep 16 '22 04:09 lxh023

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;
};

2881099 avatar Sep 16 '22 04:09 2881099

大佬牛逼,测试可以了,但还是有个问题 。

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的方法

lxh023 avatar Sep 17 '22 14:09 lxh023

ColumnAttribute 直接用反射获取

2881099 avatar Sep 17 '22 14:09 2881099

Snipaste_2022-09-18_01-46-58

   _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;
            };

看文档的方法,反射获取为空,大佬还有其他办法吗?

lxh023 avatar Sep 17 '22 17:09 lxh023

看来还是得发个版本,前面给你的方案有个私有成员

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 生效的内容
};

2881099 avatar Sep 18 '22 09:09 2881099

这个还是没办法出来。读了一下源码,并加到项目上打断点,发现有几个奇怪的地方: 1、\FreeSql\Internal\CommonUtils.cs,这个文件,_mappingPriorityTypes 似乎写死了,并没有应用FreeSqlBuilder.UseMappingPriority的传值 2、\FreeSql\DataAnnotations\TableAttribute.cs,这个TableAttribute类并没有提供_columns的方法,兄弟不才,加了一个函数getColumns() Snipaste_2022-09-19_01-11-44

  public ConcurrentDictionary getColumns() {   return _columns;  }

最终试验成功 Snipaste_2022-09-19_01-14-05 关键代码如下:

_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函数来获取列信息?

lxh023 avatar Sep 18 '22 17:09 lxh023

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> 又获取了一次元数据,再缓存起来。

2881099 avatar Sep 19 '22 00:09 2881099