9.1充血模型和贫血模型

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

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 设计模式 数据库
领域模型:贫血模型与充血模型的深度解析
领域模型:贫血模型与充血模型的深度解析
|
5月前
|
机器学习/深度学习 自动驾驶
大模型概念问题之谷歌的MUM模型是什么
大模型概念问题之谷歌的MUM模型是什么
|
6月前
|
架构师 持续交付 微服务
探索软件架构设计的深层逻辑
【6月更文挑战第5天】在数字化浪潮中,软件架构设计如同搭建一座虚拟的巴别塔,它不仅需要承载技术的重量,还要预见未来的需求。本文将通过我的个人经验,探讨如何在变化莫测的技术海洋中,寻找到稳固的架构基石,以及如何让这座塔楼灵活地适应不断变化的环境。
49 1
|
机器学习/深度学习 数据采集 算法
模型设计
模型设计流程
127 0
|
数据管理 数据库
两种不同充血模型实现中的问题及解决办法
### 前言 关于贫血模型与充血模型,已经有大量的文章写到了,但大部分都只是写了两种模型的对比,其中的例子也相对比较简单。 当我们真正在使用充血模型的过程中,还会碰到很多问题,本文期望通过我们复杂的业务场景,深入的讲解我们在真正使用充血模型时,到底会碰到哪些问题,我们又要如何来解决。 ### 什么是充血模型 Martin Fowler在2003年发表的一篇文章中,第一次提出贫血模型的概念,他把贫
2160 0
|
人机交互
领域驱动设计总结——如何运用模型
本文为领域驱动设计系列总结的第二篇,主要对领域驱动设计概念做个介绍,本系列领域驱动设计总结主要是在Eric Evans 所编写的《领域驱动设计》 一书的基础上进行归纳和总结。本文主要介绍在领域驱动设计中如何运用模型
129 0
|
存储 开发框架 Java
深度解读DDD核心模型,贫血模型和充血模型
深度解读DDD核心模型,贫血模型和充血模型
1339 0
深度解读DDD核心模型,贫血模型和充血模型
|
机器学习/深度学习 计算机视觉 异构计算
CNN结构演变总结(三)设计原则
前两篇对一些经典模型和轻量化模型关于结构设计方面的一些创新进行了总结,在本文将对前面的一些结构设计的原则,作用进行总结。 本文将介绍两种提升模型的表示能力的结构或方式,模型的五条设计原则,轻量化模型的四个设计方式。
CNN结构演变总结(三)设计原则
|
机器学习/深度学习 人工智能 数据可视化
GraLSP | 考虑局部结构模式的GNN
GraLSP | 考虑局部结构模式的GNN
159 0
GraLSP | 考虑局部结构模式的GNN
|
存储 开发框架 Java
DDD领域驱动设计-充血模型、贫血领域模型(上)
DDD领域驱动设计-充血模型、贫血领域模型(上)
327 0

热门文章

最新文章

相关实验场景

更多