9.1充血模型和贫血模型

本文涉及的产品
模型在线服务 PAI-EAS,A10/V100等 500元 1个月
交互式建模 PAI-DSW,5000CU*H 3个月
模型训练 PAI-DLC,5000CU*H 3个月
简介: 贫血模型:一个类中只有属性或者成员变量充血模型:一个类中除了属性和成员变量,还有方法

9.1充血模型和贫血模型

贫血模型:一个类中只有属性或者成员变量

充血模型:一个类中除了属性和成员变量,还有方法

EF Core对实体类属性的操作

有些时候,EF Core可能会跳过属性的get,set方法,而是直接去操作存储属性值得成员变量,这是因为EF Core在读写实体类对象属性时,会查找类中是否有与属性名字(忽略大小写)一样的成员变量,如果有则EF Core会直接读写这个成员变量,而不通过get,set属性方法。

如果采用string Name{get;set}这种简写的形式,编译器会自动生成名字为<Name>k_BackingField的成员变量来保存属性的值,因此EF Core除了查找与属性名称相同的成员变量还会查找符合<Name>k_BackingField规则的成员变量。

EF Core中实现充血模型

充血模型中的实体类相较于贫血模型实体类相比,有以下特性:

  1. 属性是只读的,或者只能在类内部的代码修改(private)
  • 实现方法:将set属性定义为private或者init,通过构造函数来初始值
  1. 定义了有参的构造函数
  • 解决方法1:实体类中定义无参数构造函数,但要声明为private,EF Core从数据库加载数据到实体类的时候,会调用这个私有的构造方法,然后对各个属性进行赋值
  • 解决方法2:实体类中不定义无参构造函数,但是要求构造方法中的参数名字和属性名字必须一致。
  1. 有的成员变量没有定义属性,但是需要在数据库中有相应的列
  • 解决方法:在配置实体类的时候,使用builder.Property("成员变量名")来配置
  1. 有的属性是只读的,即它的值是从数据库中读取出来且不能修改
  • 解决方法:在配置实体类的时候,使用HasField("成员变量名")来配置
  1. 有的属性不需要映射到数据库
  • 解决方法:使用Ignore来配置

publicrecordUser//使用record,自动生成toString方法

{

    publicintId { get; init; }//特征一

    publicDateTimeCreatedDateTime { get; init; }//特征一

    publicstringUserName { get; privateset; }//特征一

    publicintCredit { get; privateset; }

    privatestring?passwordHash;//特征三

    privatestring?remark;

    publicstring?Remark//特征四

    {

        get { returnremark; }

    }

    publicstring?Tag { get; set; }//特征五

    privateUser()//特征二

    {

    }

    publicUser(stringyhm)//特征二

    {

        this.UserName=yhm;

        this.CreatedDateTime=DateTime.Now;

        this.Credit=10;

    }

    publicvoidChangeUserName(stringnewValue)

    {

        this.UserName=newValue;

    }

    publicvoidChangePassword(stringnewValue)

    {

        if (newValue.Length<6)

        {

            thrownewArgumentException("密码太短");

        }

        this.passwordHash=HashHelper.Hash(newValue);

    }

}

对User类进行配置

internalclassUserConfig : IEntityTypeConfiguration<User>

{

   publicvoidConfigure(EntityTypeBuilder<User>builder)

   {

       builder.Property("passwordHash");//特征三

       builder.Property(u=>u.Remark).HasField("remark");//特征四

       builder.Ignore(u=>u.Tag);//特征五

   }

}

EF Core中实现值对象

实体类中实现值对象,就是将类中相关的属性进行封装, 比如某公司类中有经度和维度两个属性,但是这两个属性非常相关,所以将这两个属性进行封装成一个独立的位置坐标类,则在公司类中只要使用位置坐标类即可。

recordRegion

{

    publiclongId { get; init; }

    publicMultilingualStringName { get; init; } //值对象 中文名和英文名

    publicAreaArea { get; init; } //值对象

    publicRegionLevelLevel { get; privateset; }//值对象

    publiclong?Population { get; privateset; }

    publicGeoLocation { get; init; }//值对象,位置坐标经纬度

    privateRegion() { }

    publicRegion(MultilingualStringname, Areaarea, Geolocation,

        RegionLevellevel)

    {

        this.Name=name;

        this.Area=area;

        this.Location=location;

        this.Level=level;

    }

    publicvoidChangePopulation(longvalue)

    {

        this.Population=value;

    }

    publicvoidChangeLevel(RegionLevelvalue)

    {

        this.Level=value;

    }

}

值对象

enumAreaType { SquareKM, Hectare, CnMu }

enumRegionLevel { Province, City, County, Town }

 

recordArea(doubleValue, AreaTypeUnit);

recordMultilingualString(stringChinese, string?English);

recordGeo//经纬度

{

   publicdoubleLongitude { get; init; }

   publicdoubleLatitude { get; init; }

   publicGeo(doublelongitude, doublelatitude)

   {

       if (longitude<-180||longitude>180)

       {

           thrownewArgumentException("longitude invalid");

       }

       if (latitude<-90||latitude>90)

       {

           thrownewArgumentException("longitude invalid");

       }

       this.Longitude=longitude;

       this.Latitude=latitude;

   }

}

对Region进行配置

classRegionConfig : IEntityTypeConfiguration<Region>

{

   publicvoidConfigure(EntityTypeBuilder<Region>builder)

   {

       builder.OwnsOne(c=>c.Area, nb=> {//设置Area属性,

           nb.Property(e=>e.Unit).HasMaxLength(20)//Area中Unit属性最大长度20

           .IsUnicode(false).HasConversion<string>();//Area中Unit属性(枚举)在数据库中按照string存储

       });                                         //但操作实体类的时候仍然是枚举

       builder.OwnsOne(c=>c.Location);

       builder.Property(c=>c.Level).HasMaxLength(20)

           .IsUnicode(false).HasConversion<string>();

       builder.OwnsOne(c=>c.Name, nb=> {

           nb.Property(e=>e.English).HasMaxLength(20).IsUnicode(false);

           nb.Property(e=>e.Chinese).HasMaxLength(20).IsUnicode(true);

       });

   }

}

简化值对象的比较

在对含有值对象的实体进行筛选时,值对象的属性不能直接进行相等比较。比如不可以ctx.Cities.Where(c=>c.Name == new MulitilingualSting("北京")),这是错误的。我们需要把值对象的各个属性都进行比较,ctx.Cities.Where(c=>c.Name.Chinese == "北京"&& c.Name.English=="Beijing")如果属性值比较多的话就很麻烦,可以通过构建表达式树来生成一个进行相等比较的表达式,可以直接使用var cities = ctx.Cities.Where(ExpressionHelper.MakeEqual((Region c) => c.Name,      new MultilingualString("北京", "BeiJing")));来实现数据查询。

usingSystem.Linq.Expressions;

usingstaticSystem.Linq.Expressions.Expression;

 

classExpressionHelper

{

   //第一个参数为待比较属性的表达式,第二个参数为待比较的值对象

    publicstaticExpression<Func<TItem, bool>>MakeEqual<TItem, TProp>

(Expression<Func<TItem, TProp>>propAccessor, TProp?other)

    whereTItem : classwhereTProp : class

    {

        vare1=propAccessor.Parameters.Single();

        BinaryExpression?conditionalExpr=null;

        foreach (varpropintypeof(TProp).GetProperties())//遍历对象的每个属性

        {

            BinaryExpressionequalExpr;

            object?otherValue=null;

            if (other!=null)

            {

                otherValue=prop.GetValue(other);//通过反射获取待比较值对象中对应属性的表达式

            }

            TypepropType=prop.PropertyType;

            varleftExpr=MakeMemberAccess(propAccessor.Body, prop);//获取待比较属性的表达式

            ExpressionrightExpr=Convert(Constant(otherValue), propType);//获取对应属性值的常量表达式

            if (propType.IsPrimitive)//基本类型和对象的比较方式不同

            {

                equalExpr=Equal(leftExpr, rightExpr);

            }

            else

            {

                equalExpr=MakeBinary(ExpressionType.Equal,

                    leftExpr, rightExpr, false,

                    prop.PropertyType.GetMethod("op_Equality")

                );

            }

            if (conditionalExpr==null)

            {

                conditionalExpr=equalExpr;

            }

            else

            {

                conditionalExpr=AndAlso(conditionalExpr, equalExpr);

            }

        }

        if (conditionalExpr==null)

        {

            thrownewArgumentException("There should be at least one property.");

        }

        returnLambda<Func<TItem, bool>>(conditionalExpr, e1);

    }

}


相关文章
|
SQL 设计模式 数据库
领域模型:贫血模型与充血模型的深度解析
领域模型:贫血模型与充血模型的深度解析
|
2月前
|
机器学习/深度学习 存储 算法
Transformer、RNN和SSM的相似性探究:揭示看似不相关的LLM架构之间的联系
通过探索大语言模型(LLM)架构之间的潜在联系,我们可能开辟新途径,促进不同模型间的知识交流并提高整体效率。尽管Transformer仍是主流,但Mamba等线性循环神经网络(RNN)和状态空间模型(SSM)展现出巨大潜力。近期研究揭示了Transformer、RNN、SSM和矩阵混合器之间的深层联系,为跨架构的思想迁移提供了可能。本文深入探讨了这些架构间的相似性和差异,包括Transformer与RNN的关系、状态空间模型在自注意力机制中的隐含作用以及Mamba在特定条件下的重写方式。
100 7
Transformer、RNN和SSM的相似性探究:揭示看似不相关的LLM架构之间的联系
|
3月前
|
机器学习/深度学习 自然语言处理 PyTorch
模型训练的通用性
模型训练的通用性指在多样任务与数据集上的适用能力。关键在于利用预训练模型(如BERT、GPT)、迁移学习、多任务学习、提取通用特征、选择强大模型架构(如Transformer)、应用正则化、数据增强、精细调整超参数、跨领域适应、提升鲁棒性与解释性、持续学习、考虑伦理偏见、遵循社区标准并有效利用计算资源。这些策略共同促进模型的广泛适应性与泛化能力,但需针对具体任务需求进行选择与优化。
|
4月前
|
机器学习/深度学习 自然语言处理 前端开发
大模型问题之神经语言模型与大模型的关系是什么
大模型问题之神经语言模型与大模型的关系是什么
|
4月前
|
机器学习/深度学习 自动驾驶
大模型概念问题之谷歌的MUM模型是什么
大模型概念问题之谷歌的MUM模型是什么
|
5月前
|
存储 XML SQL
数据库建模之EAV模型
数据库建模之EAV模型
203 1
|
算法
揭秘Transformer基于上下文学习的能力:看它如何学到正确的线性模型?
揭秘Transformer基于上下文学习的能力:看它如何学到正确的线性模型?
120 0
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
大模型与大模型的幻觉问题
大模型与大模型的幻觉问题
|
机器学习/深度学习 数据采集 算法
模型设计
模型设计流程
122 0
|
数据管理 数据库
两种不同充血模型实现中的问题及解决办法
### 前言 关于贫血模型与充血模型,已经有大量的文章写到了,但大部分都只是写了两种模型的对比,其中的例子也相对比较简单。 当我们真正在使用充血模型的过程中,还会碰到很多问题,本文期望通过我们复杂的业务场景,深入的讲解我们在真正使用充血模型时,到底会碰到哪些问题,我们又要如何来解决。 ### 什么是充血模型 Martin Fowler在2003年发表的一篇文章中,第一次提出贫血模型的概念,他把贫
2109 0