将数据库上下文提供给对象工厂

Giving database context to object Factory

当我在我的代码(C#,但它适用于我想的任何语言)中使用工厂模式时,我总是问自己一个问题。

我有一个 "Service" 负责与我的数据库交互,处理对象并与我的对象模型交互。

此服务有时使用工厂来委托对象的实例化。 但是这个工厂显然需要自己与数据库交互才能正确实例化我的对象。 例如,将数据库上下文传递给 Create 方法是一种 good/bad 做法吗?

像这样:

var myNewObject = MyFactory.Create(myDatabaseContext);

另一种方法是让服务始终唯一与数据库对话。

var myNewObject = MyFactory.Create();
var extraProperty = myDatabaseContext.Get(something);
myNewObject.extraProp = extraProperty;

有什么建议吗?

在我正在进行的项目中,我们尝试将所有数据库访问保留在服务中。如果工厂需要必须从数据库加载的对象,服务应该加载它们并将它们传递给工厂。如果 Factory 返回的对象需要持久化,Service 应该将它添加到 DbContext。

这与您展示的第二种方式相对应。优点是无需模拟 DbContext 即可对工厂进行单元测试。

如果您无论如何都想在工厂内保留数据库访问权限,我会将 DbContext 注入到工厂的构造函数中,而不是将其传递给 Create() 方法。

Service依次获取Factory的实例注入(而不是访问Factory的静态方法)。同样,这将使模拟变得更加容易。

public class Service {

    private readonly IMyDbContext _myDatabaseContext;
    private readonly IMyFactory _myfactory;

    public Service (IMyDbContext myDbContext, IMyFactory myfactory) {
        _myDatabaseContext = myDbContext;
        _myfactory = myfactory
    }

    public void Create() {
        var extraProperty = myDatabaseContext.Get(something);
        var myNewObject = _myFactory.Create(extraProperty);
        _myDatabaseContext.Add(myNewObject);
        _myDatabaseContext.SaveChanges();
    }
}

将数据库上下文传递到工厂创建方法的想法称为方法注入。这是一种依赖注入的形式,所以你是在正确的轨道上。

您可以使用依赖注入通过构造函数在工厂内部管理数据库上下文。工厂可能看起来像这样:

public class MyFactory 
{
    private readonly IMyDbContext dbContext;

    public MyFactory(IMyDbContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public object Create()
    {
        // Use the dbContext, etc
    }
}

构造函数注入通常受到青睐,因为它使方法签名不那么混乱。我们也很可能拥有一种类型的数据库上下文,因此无需利用基于其他一些运行时信息的多态性。

您可以选择使用 Ninject or, my favorite, SimpleInjector 之类的依赖注入容器来为您管理依赖项。

DbContext 只供工厂使用是可以的。您可能需要注意的一件事是您工厂的用户可能没有意识到工厂正在调用数据库。这可能是一件坏事,会对性能产生负面影响。通常,构造信息被传递到工厂方法中,而不是从数据库初始化到工厂方法中。如果您认为有必要但您还没有,您甚至可以更进一步并使用 Repository Pattern 抽象出更多的数据访问逻辑。

要了解有关依赖注入的更多信息,以防您不熟悉,you can start here

我理想的结构可能是这样的:

public class MyFactory : IFactory
{  
    public object Create(object someProperty)
    {
        // build object
    }
}

public class MyService
{
    private readonly IMyDbContext dbContext;
    private readonly IFactory factory;

    public MyService(IMyDbContext dbContext, IFactory factory)
    {
        this.dbContext = dbContext;
        this.factory = factory;
    }

    public void DoWork()
    {
        var property = dbContext.Get(something);
        var newObj = factory.Create(property);
        // Use stuff
    }
}