用于自己的数据访问的惰性运算符 C#

Posted

技术标签:

【中文标题】用于自己的数据访问的惰性运算符 C#【英文标题】:Lazy Operator C# for own Data Access 【发布时间】:2015-05-13 09:37:21 【问题描述】:

我目前正在 winforms 中构建医疗方案类型的应用程序。我已经实现了数据访问的延迟加载方法。我不能使用像 EF 或 nHibernate 这样的 ORM,所以我用自己的逻辑实现了它。我在一个类上有多个字段,例如。包含导航属性的包,例如List<Treatment>,它应该得到此包的所有处理。用惰性包装整个列表是不好的做法,它会被实例化如下:

        Package package = new Package()
        
            PackageID = record.GetInt32(0),
            Name = record.GetString(1),
            CoverageAmount = record.GetDecimal(2),
            PackageStatus = (Status)record.GetInt32(3),
            AvaliableFrom = record.GetDateTime(4),
            AvaliableTo = record.GetDateTime(5),
        ;

        package.Policies = new Lazy<List<Policy>>(() =>
        
            return GetPoliciesByPackageID(package.PackageID);
        );

        package.Treatments = new Lazy<List<Treatment>>(() =>
        
            return GetTreatmentsByPackage(package.PackageID);
        );

        return package; 

当我想要所有必须调用 package.Treatments.Value 的治疗时,这感觉有点不对劲。这种方法是否令人不悦。

【问题讨论】:

【参考方案1】:

延迟实例化本身并不是不好的做法,但是如果您通过属性公开昂贵的(就获取结果所花费的时间而言)值,那么这是不好的做法。属性旨在快速访问对象状态的一部分。如果您以昂贵的方式获取数据,请公开一个方法。

【讨论】:

当前方法 GetTreatmentsByPackage 和这样的方法调用一个查询数据库的存储库。所以它可能相当“昂贵”。 那么懒惰的行为是你想要的吗? Lazy 内部实例在评估后将永远不会更改,因此如果数据库发生更改,您将继续从惰性返回陈旧数据。 所以懒惰的() =&gt; return GetTreatmentsByPackage(package.PackageID); 内部只会被评估一次。然后在重复调用时,它有什么作用?数据存储在某个地方吗?我想我有不同的理解,可能是错误的。 是的,它只评估一次(警告:不是线程安全的 - 请参阅 AsyncLazy)。因此,一旦它被评估,它就会存储结果并在每次查询 Value 时返回它。 谢谢,我不知道。将查看 AsyncLazy。看来我的设计并没有我想象的那么坚如磐石。有几个对象使用相同的逻辑。【参考方案2】:

数据被延迟加载的事实似乎是一个实现细节。因此,我将其封装起来。

public class Package

    private readonly Lazy<List<Policy>> _lazyPolicies =  ...

    public List<Policy> Policies
    
        get  return _lazyPolicies.Value; 
    

如果您想模仿 NHibernate 的功能,可以使用 proxy

这是一个简单的(线程不安全的)代理:

public interface IPackage

    List<Policy> Policies  get; 


public class Package

    public List<Policy> Policies  get ; set; 


public class PackageProxy

    private readonly Package _package;
    private readonly Database _db;

    public PackageProxy(Package package, Database _db)
    
        _package = package;
        _db = db;
    

    public List<Policy> Policies
    
        get
        
            if(_package.Policies == null)
                _package.Policies = GetPoliciesByPackageID(package.PackageID);
            return _package.Policies;
        
    

    private List<Policy> GetPoliciesByPackageID(int id)
    
        _db...
    


// usage

IPackage package = new PackageProxy(new Package(), db)  /** set properties **/ ;

【讨论】:

我喜欢这种方法,我看到的唯一问题。据我所知 readonly 只允许在构造函数中赋值。我不使用它,因为这些对象纯粹是包含许多字段和属性的数据。构造函数会很长,个人喜好我不喜欢 @RiaanvanRooyen 好吧,如果他们的数据将被延迟加载,那么他们就不像你最初想象的那样“愚蠢”。另一种选择是通过代理(或动态代理)执行此操作,与 NHibernate 执行此操作的方式相同。 DTO 将具有“哑”属性,代理将包裹 DTO,公开相同的接口,但在调用属性获取器时调用数据库。 谢谢。我还在学习,目前正在学习软件工程是南非。我很欣赏我肯定会研究动态代理的建议。你有任何资源可以展示 Entity-Framework 或 NHibernate 的内部吗? @RiaanvanRooyen 我没有,我听说这是他们使用的技术,但我不记得在哪里。不过应该很容易找到。此外,NHibernate 使用Castle DynamicProxy 在运行时动态生成对象的代理。我添加了一个非动态手动编写的代理示例来演示幕后发生的事情。 谢谢,这真的很好。给出了一个好主意。非常感谢我会投票但没有足够的声誉。第一个问题

以上是关于用于自己的数据访问的惰性运算符 C#的主要内容,如果未能解决你的问题,请参考以下文章

C# 访问操作注册表整理

C#高级编程

C#—类库委托is和as运算符泛型集合

C# 数据库访问:DBNull 与 null

C# 主要运算符中的成员访问

Swift 常量默认是惰性的吗?