【问题标题】:class & method in C#, is this a good approach?C# 中的类和方法,这是一个好方法吗?
【发布时间】:2012-07-20 00:22:57
【问题描述】:

我正在尝试在我的 asp.net Web 应用程序中构建一个与数据库交互的类。我需要您对如何设计它的意见,这是我的想法的一个例子

public class Person
{
    int personId;
    string name;
    string lastName;

    public int PersonId
    {
        get { return personId; }
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }

    public Person()
    {

    }

    public static void Save(Person p)
    {
        //datalayer here
        //save the person class
    }

    public static Person GetPerson(int person_id)
    {
        //datalayer here
        //get the person from database and return a person class
        Person p = new Person();
        p.personId = 10;
        p.name = "Alex";
        return p;
    }
}

这样我就可以使用数据库方法而不必实例化类:

Person p = Person.GetPerson(19);
p.Name = "Alex...";
Person.Save(p);

感谢您的帮助。

【问题讨论】:

标签: c# asp.net class methods


【解决方案1】:

使用Automatic proerties,因为您的私有字段在您的代码中执行相同的操作。

我认为,Save 是一个操作,可以在 Person Entity 的对象上完成。所以我不会将其保留为静态方法。我会将您的Save 代码作为Person 对象的方法移动。所以我会称它为obj.Save()。要加载数据,我会使用我的类 constructor 的重载版本。

public class Person
{
    int personId;      

    public int PersonId
    {
        get { return personId; }
    }    
    public string Name { set;get;}   
    public string LastName { set;get;}        

    public Person() {}

    public Person(int person_id)
    {
        //call to datalayer here
        //get the person from database and return a person class          
        personId = 10;
        Name= "Alex";  // set the public property value here           
    }
    public bool Save()
    {
        //datalayer here
        //save the person class and return
      // true/false /or new ID (change return type)
    }    

}

而当调用时,

Person p = new Person(19);  //get existing person
p.Name = "New Name";
p.Save();

编辑: 另一种(更好的)方法是将您的实体类保持为简单的 POCO。这意味着那里没有数据访问/ BL代码。它看起来就像

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

并有一个Repository 为您执行数据操作。所以你的仓库可能有这样的方法

public interface IRepository
{ 
   Person GetPerson(int id);
   bool SavePerson(Person person);
}

您可以在一个类中实现这个Interface 来执行您的数据访问操作

public class Repository:IRepository
{
  //implementation of your DA methods here
}

现在你可以像这样从不同的层(业务层)调用它

IRepository repo = new Repository();

var person=repo.GetPerson(19);  
person.Name="Updated Name";
repo.Save(person);

【讨论】:

  • 感谢您的提示;不知何故,我忘记了自动道具!
  • -1:使持久层更接近 UI 并不是对存储库模式的正确使用。
  • @EsotericScreenName 你能解释一下“靠近 UI”吗?
  • @Shyju 让 UI 调用存储库直接将模型层(在本例中为 Person 对象)从 UI 和数据库之间的通信链中取出。删除这一抽象层使其“更接近”,因为在 UI 中的数据到达数据库之前,只有一层(存储库)可以通过,而不是两层(Person 类和存储库),所以它是一个违反模式。从实际的角度来看,您不希望 UI 能够直接调用持久性代码....
  • 我将实体 (POCO) 和数据访问层作为单独的项目,以便可以根据需要在不同的项目中使用相同的实体。
【解决方案2】:

你做得对,但你也可以为你的班级使用自动属性。它可能会节省您的一些时间。 例如。

public class Person
{

    public int PersonId { get; set;}    
    public string Name { get; set;}
    public string LastName { get; set;}

    public Person()
    {
    }
}

【讨论】:

    【解决方案3】:

    我喜欢持久无知的东西:What are the benefits of Persistence Ignorance?)

    在这种情况下,您应该将 Save 方法移动到另一个类,这样实体就不会包含任何应该如何持久化的信息。

    【讨论】:

      【解决方案4】:

      您所追求的是对象的factory method pattern 和数据访问代码的repository pattern。我无法像文章那样解释它,所以我将介绍基本思想并提供一些示例。

      目标是将您的代码库划分为处理一种特定类型的关注点的层,例如与用户 (UI) 通信、在应用程序中保存和验证数据(业务类/模型)或管理数据持久性(数据访问)。保持这些区域整齐划分可以更容易地维护和调试代码或并行开发。还有其他好处,例如促进跨多台物理机器的架构,但这超出了问题的范围。

      基本结构:

      获取概念进展:

      UI -> Person Factory -> Person class -> Repository -> Database
      

      保存概念进展:

      UI -> Person class -> Repository -> Database
      

      Person 类结构,内部带有解释性 cmets:

      public class Person
      {
         // various properties & methods
      
         // Constructor access is restricted to control how the class gets consumed.
         // All instance management must go through the factories.
         protected Person() { /* stuff */ }
      
         // Person factory implementation. It's done inside the Person class so that
         // tight control can be kept over constructor access.
         // The factory is what gives you your instances of Person.
         // It has defined inputs and outputs, as well as more descriptive
         // names than constructor overloads, so consumers know what to expect.
         // It's also a place to put scaffolding code, so you can avoid doing 
         // things like setting properties every time you fetch an instance.
         // The factory takes care of all the object initialization and returns
         // an instance that's ready for use.
         public static Person GetPerson(int id)
         {
             Person p = new Person();
      
             // here you call the repository. It should return either a native
             // data structure like DataReader or DataTable, or a simple DTO class
             // which is then used to populate the properties of Person.
             // the reason for this is to avoid a circular dependency between
             // the repository and Person classes, which will be a compile time error
             // if they're defined in separate libraries
             using(PersonRepository repo = new PersonRepository())
             {
                DataReader dr = repo.GetPerson(id);
                p.FillFromDataReader(dr);
             }
      
             return p;
         }
      
         protected void FillFromDataReader(DataReader dr)
         { /* populate properties in here */ }
      
         // Save should be an instance method, because you need an instance of person
         // in order to save. You don't call the dealership to drive your car,
         // only when you're getting a new one, so the factory doesn't do the saving.
         public void Save()
         {
            // Again, we call the repository here. You can pass a DTO class, or
            // simply pass the necessary properties as parameters
            using(PersonRepository repo = new PersonRepository())
            {
               this.Id = repo.SavePerson(name, address);
            }
         }
      }
      

      现在,存储库代码:

      // This class implements IDisposable for easy control over DB connection resources.
      // You could also design and implement an IRepository interface depending on your needs.
      public class PersonRepository : IDisposable
      {
         private SqlConnection conn;
      
         public PersonRepository()
         {
            // in here you initialize connection resources
            conn = new SqlConnection("someConnectionString");
         }
      
         public void IDisposable.Dispose()
         {
            // clean up the connection
            conn.Dispose();
         }
      
         // The instance methods talk to the database
         public int SavePerson(string name, string address)
         {
            // call your stored procedure (or whatever) and return the new ID
            using(SqlCommand cmd = conn.CreateCommand())
            {
               // stuff
               return (int)cmd.Parameters["myOutputIDParameter"].Value;
            }
         }
      
         public DataReader GetPerson(int id)
         {
            // call your stored procedure (or whatever) and return the fetched data
            using(SqlCommand cmd = conn.CreateCommand())
            {
               // stuff
               return cmd.ExecuteReader();
            }
         }
      }
      

      最后,您将在 UI 级别执行以下操作:

      Person joe = Person.GetPerson(joeId);
      // stuff
      joe.Save();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-21
        • 1970-01-01
        • 1970-01-01
        • 2011-05-16
        相关资源
        最近更新 更多