如何将两个相似但不同的方法创建为通用方法?

7

我有两个类似的方法,基本上只是使用不同的对象来完成相同的事情。 如果可能的话,如何将它们制作成通用方法?

这两个对象:

public class StoreObject {
  int Key;
  string Address;
  string Country;
  int Latitude;
  int Longitude;
}

public class ProjectObject {
  int ProjectKey;
  string Address;
  string Description;
}

我希望将以下两种方法转化为通用方法:

public StoreObject GetStoreByKey(int key)
{
  using (DBEntities dbe = new DBEntities())
  {
    StoreObject so = new StoreObject();
    var storeObject = (from s in dbe.StoreTables
                       where s.Key == key
                       select s).First();

    so.Key = storeObject.key;
    so.Address = storeObject.address;
    so.Country = storeObject.country;
    so.Latitude = storeObject.latitude;
    so.Longitude = storeObject.longitude;

    return so;
  }
}

public ProjectObject GetProjectByKey(int projectKey)
{
  using (DBEntities dbe = new DBEntities())
  {
    ProjectObject po = new ProjectObject();
    var projectObject = (from p in dbe.ProjectTables
                       where p.ProjectKey == projectKey
                       select p).First();

    po.Key = projectObject.p_key;
    po.Address = projectObject.p_address;
    po.Description = projectObject.p_description;

    return po;
  }
}

我必须说明以下几点:
- 我无法控制表格字段的命名方式(例如p_description)。
- 数据库中的StoreTable可能有其他属性(例如电话、邮政编码等),但我只对代码中显示的内容感兴趣。
- ProjectTable也是如此。


1
这些方法并不相似。几乎每一行都不同。你可以将一个表达式作为where子句传递,但那只会让事情变得更加复杂。这些方法中的每一个都知道如何处理它们所处理的对象。我认为它已经抽象化到了极致。 - Bob Horn
嗯,我得说有一些相似之处,虽然我同意它已经被抽象化了,但我认为我可以尝试询问一下,以防其他人能够想出更好的方法。 - kei
4个回答

3

好的,棘手的部分在于您的实体具有不同的属性,因此使用泛型来填充一个方法中的不同属性是不值得的。但是您可以返回整个对象,然后只使用您感兴趣的属性。

public T GetEntityByKey<T>(int key)
{
  using (DBEntities dbe = new DBEntities())
  {
    return = dbe.StoreTables.Set<T>.Find(new object[] {key});
  }
}

并且要使用它

StoreObject so  = GetEntityByKey<StoreObject>(123);
if(so != null)
{
    int lat = so.Latitude;
} 

我感谢大家的意见,但是我将选择Steve的答案。不过,由于我不仅从StoreTables获取信息,所以我需要进行一些更改。 - kei

2

每个方法实际上具有两个不同的功能:

  1. 查询一个实体
  2. 将该实体映射到另一个类型

第一部分已由Steve Mallory解决。

对于第二部分,您可以使用映射框架来处理从一个实例到另一个实例的值的复制。由于每种类型的名称不匹配,您需要告诉它如何映射名称(在您的示例中,添加“p_”并将其小写)。其中一种可能性是Emit Mapper

如果您将所有共通点分解出来,它将类似于:

public TResult GetById<TResult, TEntity>(int id)
{
    using (DBEntities dbe = new DBEntities())      
    {        
        T result = dbe.StoreTables.Set<T>.Find(new object[] {key});
        var mapper = ObjectMapperManager.DefaultInstance
            .GetMapper<TEntity, TResult>(
               new DefaultMapConfig().MatchMembers((m1, m2) => "p_" + m1.ToLower() == m2));

        return mapper.Map(result);      
    }
}

嗯...我会考虑这个建议,但是手动映射这三个字段可能更简单。 - kei

2
你确实可以将返回的类型抽象出来,并因此使用using,但对于其余部分,你需要根据所请求的类型进行开关操作或者通过反射传递字段作为参数以及要使用的DB查询。
前者会是一种不良行为且对等式几乎没有影响,而后者则成本高昂且可能会变得混乱。
除非你有许多这样类似的方法,否则这并不是泛型的好选择,如果是这样,则我会选择反射方法。
希望能帮到你,
Bab.

我已经考虑过反射,但我想试试看是否可能将其作为通用方法来实现。 - kei

2

很有可能这不是你完整的“工作单元”,所以在每个方法中使用一个全新的DBEntities()上下文可能是你问题的根源。

创建一个Repository类,该类包括DBEntities类的一个实例,用于单个Web请求(或您应用程序中的其他请求单位),并且该类具有这些方法,将是消除此处重复代码的更好方法。 using() 的范围然后在这些方法之外,并希望与您的Web请求或其他时间单位相关联。

作为一种选择,您还可以扩展DBEntities部分类,以包括像这样的方法(假设这是生成的代码)。


好主意,但我仍然想尝试将它们转换为通用的。 - kei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接