1

我正在构建一个资产跟踪应用程序。使用 SQL Server 2008、C# .NET 和实体框架。这是我创建存储库的第一次体验,根据我的研究,存储库旨在抽象数据访问过程。我一直在尝试一些设计,我很好奇它们中的任何一个是好的,还是会带来任何严重的开发风险。存储库应支持按序列号、条形码或主机名查询资产。

为了使示例简短,我不包括更新、删除、插入方法。此外,这些示例忽略了可能的泛型实现,因为泛型实现总是可以在以后制定,而现在只会使示例更加混乱。请阅读以下 3 种设计,并让我知道我是否在正确的轨道上使用其中任何一种:

设计一

我的第一个设计是这样的:

public interface IAssetRepository
{
    public Asset fetchBySerialNumber(String serialNumber);
    public Asset fetchByBarcode(String barcode);
    public ICollection<Asset> fetchByHostname(String hostname);
    public Asset fetchActiveByHostname(String hostname);
}

public class AssetRepository : IAssetRepository
{
    private InventoryEntites entities;

    public Asset fetchBySerialNumber(String serialNumber)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.SerialNumber == serialNumber
                                  select a;

        return query.FirstOrDefault();
    }

    public Asset fetchByBarcode(String barcode)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Barcode == barcode
                                  select a;

        return query.FirstOrDefault();
    }

    public Asset fetchActiveByHostname(String hostname)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Hostname == hostname && a.IsDeployed == true
                                  select a;

        return query.FirstOrDefault();
    }

    public ICollection<Asset> fetchByHostname(String hostname)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Hostname == hostname
                                  select a;

        return query.ToList();
    }
}

设计二

在我的第二次尝试中,我认为我可以通过包装用作参数的原始类型来利用临时多态性:

public interface IAssetRepository
{
    public Asset fetch(SerialNumber serialNumber);
    public Asset fetch(Barcode barcode);
    public ICollection<Asset> fetch(Hostname hostname);
    public Asset fetchActive(Hostname hostname);
}

public class SerialNumber
{
    private String value;

    public SerialNumber(String value)
    { this.value = value; }

    public String Value
    {
        get { return this.value; }
    }
}

// Barcode and Hostname classes are similar to SerialNumber

public class AssetRepository : IAssetRepository
{
    private InventoryEntites entities;

    public Asset fetch(SerialNumber serialNumber)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.SerialNumber == serialNumber.Value
                                  select a;

        return query.FirstOrDefault();
    }

    public Asset fetch(Barcode barcode)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Barcode == barcode.Value
                                  select a;

        return query.FirstOrDefault();
    }

    public ICollection<Asset> fetch(Hostname hostname)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Hostname == hostname.Value && a.IsDeployed == true
                                  select a;

        return query.FirstOrDefault();
    }

    public Asset fetchActive(Hostname hostname)
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Hostname == hostname.Value
                                  select a;

        return query.ToList();
    }
}

设计 3

本着“告诉,不问”的精神,我的最后一个设计将实际查询移至 SerialNumber、Hostname 和 Barcode 类,而不是询问它们的值。SerialNumber 等类现在必须包含对数据源的引用。这些可能会受益于接口,因此它们可以支持不同的数据源。我不知道这是否是一个好的设计,因为它们每个都有对实体的单独引用。客户端必须在将对象(序列号等)发送到存储库之前构造它们。由于客户端不会引用实体,因此在构造期间无法注入对实体的相同引用:

// Same interface as last
public interface IAssetRepository
{
    public Asset fetch(SerialNumber serialNumber);
    public Asset fetch(Barcode barcode);
    public ICollection<Asset> fetch(Hostname hostname);
    public Asset fetchActive(Hostname hostname);
}

// Could include other methods like, findStartingWith(), findContains(), etc.
public class SerialNumber
{
    private InventoryEntites entities;
    private String value;

    public SerialNumber(String value)
    { 
        this.value = value;
        this.entities = new InventoryEntities();
    }

    public Asset find()
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.SerialNumber == this.value
                                  select a;

        return query.FirstOrDefault();
    }
}

// Barcode classes is similar to SerialNumber

public class Hostname
{
    private InventoryEntites entities;
    private String value;

    public Hostname(String value)
    { 
        this.value = value;
        this.entities = new InventoryEntities();
    }

    public ICollection<Asset> find()
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Hostname == this.value
                                  select a;

        return query.ToList();
    }

    public Asset findActive()
    {
        IQueryable<Asset> query = from a in this.entities.Assets
                                  where a.Hostname == this.value && a.IsDeployed == true
                                  select a;

        return query.FirstOrDefault();
    }
}

public class AssetRepository : IAssetRepository
{
    private InventoryEntites entities;

    public Asset fetch(SerialNumber serialNumber)
    {
        return serialNumber.find();
    }

    public Asset fetch(Barcode barcode)
    {
        return barcode.find();
    }

    public ICollection<Asset> fetch(Hostname hostname)
    {
        return hostname.find();
    }

    public Asset fetchActive(Hostname hostname)
    {
        return hostname.findActive();
    }

    // Other methods could include

    public ICollection<Asset> fetch(Location location)
    {
        return location.find();
    }

    public ICollection<Asset> fetchActive(Location location)
    {
        return location.findActive();
    }
}

更新

做了一些研究,发现了这篇文章:

MSDN:实践中的模式:内聚和耦合

这句话让我想到了设计 3。也许应该将这一小组类整合到一个更像设计 2 的设计中?

霰弹枪手术系统中某种类型的更改反复导致对一组类进行大量小的更改。霰弹枪手术通常意味着单个逻辑想法或功能分布在多个类别中。尝试通过将必须更改的所有代码部分合并到一个单一的内聚类中来解决此问题。

4

1 回答 1

0

我总是使用设计#1,因为它更容易分辨存储库的作用。它确实传达了意图。

如果您在设计二中对实体(等)进行某种验证Barcode以验证 ID,那么我更愿意这样做。如果你不这样做,它不会增加任何价值,因为你可以获取一个hostNameid 并用它创建一个BarCode

设计三更多的是与存储库混合的数据访问对象。这是一个不不。

(旁注:请遵循 .NET 命名约定。方法名称应为PascalCase

于 2013-04-05T06:14:21.667 回答