gpt4 book ai didi

How can I inject an object into a static method?(如何将对象注入到静态方法中?)

转载 作者:bug小助手 更新时间:2023-10-25 18:09:32 25 4
gpt4 key购买 nike



I have a .net core 5 application that has an appsettings.json file, in which I have listed connection strings to the databases.

我有一个.Net core 5应用程序,它有一个appsettings.json文件,我在其中列出了到数据库的连接字符串。


I have defined a Database class that has the responsibility of handing out IDbConnection objects.

我定义了一个负责分发IDbConnection对象的数据库类。


public class Database
{
public Database(IConfiguration config) // config is injected
{
Db1ConnectionString = config.GetConnectionString("db1");
Db2ConnectionString = config.GetConnectionString("db2");
}

public IDbConnection GetConnection()
{
// do some logic to decide, either Db1 or Db2 to use
return new SQLiteConnection(SelectedDbConnectionString);
}
}

Then I am injecting this Database in Startup.cs

然后我将这个数据库注入到Startup.cs中


services.AddScoped<IDatabase, Database>();    // IDatabase is interface of Database

Let's say I have a class of Person

假设我有一个班级的人


public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}

I am using Dapper to fetch IEnumerable<Person>. I am writing logic to fetch collections of objects in the same class itself. Meaning: Person class knows how to fetch a collection of its self. Animal class has its own logic of fetching a collection of its self.

我正在使用Dapper获取IEnumerable 。我正在编写逻辑来获取同一个类本身中的对象集合。含义:Person类知道如何获取其自身的集合。动物类有自己的逻辑,即获取自身的集合。


public class Person
{
public int Id { get; set; }
public string Name { get; set; }

public static IEnumerable<Person> GetAll()
{
string sql = "select id, name from person where ...... ";
using var con = Database.GetConnection(); // this does not work
// because the GetConnection() is not static.
return con.Query<Person>(sql);
}

}

The code above will not work because I need an instance of Database inside a static method. I though of injecting Database in Person constructor, but that will make things difficult for Dapper. So I guess I will need to write an empty constructor for Person. But then the other constructor (that acceptes IDatabase ) can't keep hold of the reference of that database object until the static method requires it.

上面的代码将不起作用,因为我需要静态方法中的一个数据库实例。我想在Person构造函数中注入数据库,但这会给Dapper带来困难。所以我想我需要为Person编写一个空的构造函数。但是,另一个构造函数(接受IDatabase)无法保持该数据库对象的引用,直到静态方法需要它。


I thought of converting Database into a static class, or changing GetConnection() into static method, but then I will not be able to inject it in Startup.cs.

我想过将数据库转换为静态类,或将GetConnection()更改为静态方法,但之后我将无法将其注入到Startup.cs中。


I don't want to hardcore the connection strings, as they may change at run time.

我不想硬核连接字符串,因为它们可能在运行时更改。


I feel like there is a straight forward solution to this problem, and it must be obvious too.

我觉得这个问题有一个直截了当的解决方案,而且必须是显而易见的。


更多回答

by not making it static ... just add IPersonDataService and PersonDataService (or whatever) implementation ... then inject IDatabase to the constructor

不让它成为静态的..。只需添加IPersonDataService和PersonDataService(或其他任何)实现...然后将IDatabase注入构造函数

How will this allow me to use the connection object in the static method? can you please elaborate? @Selvin

这将如何允许我在静态方法中使用Connection对象?“你能详细说明一下吗?”塞尔文

Do not use static method at all!

根本不要使用静态方法!

again ... there should be no such method it should be a part of IPersonDataService

再一次..。不应该有这样的方法它应该是IPersonDataService的一部分

I agree with @Selvin here. 1. such GetAll method should never be static, as it becomes impossible to properly loosely couple your code, and 2. GetAll should not be part of Person as person will be part of your domain layer, while the GetAll logic will reside in your data access layer. So as Selvin is mentioning, define a separate abstraction (e.g. IPersonRepository or IPersonDataService), define the GetAll method on it, create an implementation that gets the database injected, and inject that abstraction again in classes that need to call GetAll.

我同意这里的“塞尔文”。1.这样的getall方法永远不应该是静态的,因为不可能适当地松散地耦合您的代码;2.getall不应该是Person的一部分,因为Person将是您的域层的一部分,而getall逻辑将驻留在您的数据访问层中。因此,正如Selvin提到的,定义一个单独的抽象(例如IPersonRepository或IPersonDataService),在其上定义getall方法,创建一个注入数据库的实现,并将该抽象再次注入到需要调用getall的类中。

优秀答案推荐

If you require a dependency inside a static method, you'll end up with Method Injection, as this answer describes. This means that your Person class becomes the following:

如果您需要静态方法中的依赖项,则最终将使用方法注入,如本答案所述。这意味着您的Person类如下所示:


public class Person
{
public int Id { get; set; }
public string Name { get; set; }

// IDatabase is supplied using Method Injection
public static IEnumerable<Person> GetAll(IDatabase database)
{
string sql = "select id, name from person where ...... ";
using var con = database.GetConnection();
return con.Query<Person>(sql);
}
}

Consequence of method injection, however, is that the dependency (i.e. IDatabase) is no longer hidden to the consumers, but instead need to be supplied by them. e.g.:

然而,方法注入的结果是依赖项(即IDatabase)不再对使用者隐藏,而是需要由他们提供。例如:


public sealed class PersonService : IPersonService
{
private readonly IDatabase database;

public PersonService(IDatabase database)
{
this.database = database;
}

public void ChangeName(int id, string newName)
{
var persons = Person.GetAll(this.database);
var person = persons.Single(p => p.Id == id);
person.Name = newName;
person.Save(this.database);
}
}

In practice, this means that those consumers should either get this dependency injected into them either using Constructor Injection or Method Injection. The previous example with PersonService shows Constructor Injection.

在实践中,这意味着这些使用者应该使用构造函数注入或方法注入将这种依赖注入其中。前面的PersonService示例显示了构造函数注入。


Having to supply dependencies through Method Injection can be unpractical for many reasons: first of all, it complicates consumers with an extra dependency, as the example above shows.

必须通过方法注入提供依赖关系可能是不切实际的,原因有很多:首先,如上面的例子所示,这会使消费者使用额外的依赖关系复杂化。


The use of such static method is probably not the best solution. However, making the method an instance method, on the other hand, is also not practical, because:

使用这种静态方法可能不是最好的解决方案。但是,另一方面,使方法成为实例方法也是不切实际的,因为:



  • It means you'll have to inject IDatabase into Person, but that complicates the creation of Person instances, as they are typically created by your O/RM mapper (e.g. Entity Framework). Also, you are injecting a dependency that is only needed in the case that GetAll is being called.

  • It is very weird to have to instantiate a specific Person in order to get all persons. e.g.:
    var person = new Person(database);
    return person.GetAll(); // Get all of what? Persons of that person?


  • When you start injecting IDatabase into person, you'll soon end up with a dozen or more dependencies into Person, because you likely add many more person-related methods in this class. eg.:
    var person = new Person(database, timeProvider, userContext)
    {
    Id = 100,
    Name = "Steven"
    };
    // Save might require the current time and the user executing the operation
    return person.Save();



This might lead to the conclusion that having this GetAll method on Person method not be the best solution. Rather than placing this method (and many others) inside Person, it could be cleaner to separate the concerns of data access and your domain entity.

这可能会导致这样的结论:在Person方法上使用这种getall方法不是最好的解决方案。与其将这个方法(和许多其他方法)放在Person内部,将数据访问和域实体分离可能会更干净。


A very common pattern that is used for such situations is the Repository Pattern. There are several variations of this pattern, but in essence you define a separate interface for your data-access logic. This was already suggested in the comments by @Selvin and me. For instance, you can define an IPersonRepository interface:

在这种情况下使用的一种非常常见的模式是Repository模式。这种模式有几种变体,但本质上是为数据访问逻辑定义了一个单独的接口。@Selvin和我在评论中已经提出了这一点。例如,您可以定义IPersonRepository接口:


public interface IPersonRepository
{
IEnumerable<Person> GetAll();
void Save(Person person);
}

This allows moving the data access logic into separate class:

这允许将数据访问逻辑移动到单独的类中:


public sealed class SqlPersonRepository : IPersonRepository
{
private readonly IDatabase database;

public SqlPersonRepository(IDatabase database)
{
this.database = database;
}

public IEnumerable<Person> GetAll()
{
string sql = "select id, name from person where ...... ";
using var con = this.database.GetConnection();
return con.Query<Person>(sql);
}

public void Save(Person person) ...
}

Advantage of separating this logic from the Person class, is that allows you to place the Person class and the IPersonRepository inside your Domain layer project, while you keep the SqlPersonRepository implementation hidden from the domain layer by placing it inside your Data Access layer project (where this project takes a dependency on the Domain layer project).

将此逻辑与Person类分离的好处是,您可以将Person类和IPersonRepository放置在域层项目中,而通过将SqlPersonRepository实现放置在数据访问层项目中(该项目依赖于域层项目),从而使SqlPersonRepository实现对域层隐藏。


Obvious consequence of this design is that any class that requires getting all persons, needs to get IPersonRepository injected into it. But when you are practicing Dependency Injection, this will typically not be a problem, and actually something that is encouraged. This can be demonstrated in a changed version of the previous PersonService:

这种设计的明显结果是,任何需要获取所有Person的类都需要将IPersonRepository注入其中。但当您练习依赖项注入时,这通常不是问题,实际上是值得鼓励的事情。这可以在以前的PersonService的更改版本中演示:


public sealed class PersonService : IPersonService
{
private readonly IPersonRepository repository;

public PersonService(IPersonRepository repository)
{
this.repository = repository;
}

public void ChangeName(int id, string newName)
{
var persons = this.repository.GetAll();
var person = persons.Single(p => p.Id == id);
person.Name = newName;
this.repository.Save(person);
}
}

更多回答

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com