泛型方法调用数据访问层

3

我正在创建一个三层体系结构的应用程序。

  • UI(Web)
  • BLL(ClassLibrary)
  • DAL(ClassLibrary)

我的需求是像角色、城市、国家、州等一样绑定下拉列表。

我为所有实体创建了具有相同结构的属性。

public class City
{
    public string Id { get; set; }
    public string Name { get; set; }
}

考虑下面这个控制台应用程序的示例,它复制了我的需求。
假设这个调用是来自我的用户界面。
static void Main( string[] args )
{

    List<City> cities = GetDataList<City>();
    List<Role> roles = GetDataList<Role>();

}

这是我的业务逻辑层(BLL)

public static  List<T> GetDataList<T>() where T:class ,new()
{
    List<T> list = new List<T>();
 (A)   List<T> data = ** I am not getting the way how to call different methods of data acess here?
    foreach (var temp in data.AsEnumerable())
    {
        var obj = new T();
  ( B)     foreach (var prop in obj.GetType().GetProperties())
        {
            var propertyInfo = obj.GetType().GetProperty(prop.Name);
            propertyInfo.SetValue(obj , Convert.ChangeType(temp. , propertyInfo.PropertyType) , null);
        }
        list.Add(obj);
    }

    return list;
}

这是我的数据访问层,我在这里使用EF

public static IList<City> FetchCityData()
{
    List<City> dict = new List<City>();           
    dict.Add(new City{Id="1",Name="hello"});
   dict.Add(new City{Id="2",Name="world"});
    return dict;
}

public static List<Role> FetchStateData()
{
    List<Role> dict = new List<Role>();
    dict.Add(new Role { Id = "1" , Name = "hello" });
    dict.Add(new Role { Id = "2" , Name = "world" });
    return dict;
}

我有以下问题:

A) 如何从BLL访问不同的数据访问方法?

B) 我应该如何为我的类属性分配值?

A和B是代码中的标签。


只需简单地分支代码即可。你有两种不同的方法FetchCityDataFetchStateData,无法概括它们。你唯一的输入是类型 T,从那里分支。 - Hopeless
你有修改 CityRole 类的访问权限吗?如果可能的话,你可以创建一个基类或接口来暴露一个像 FetchData 这样的方法。这意味着你将所有的 FetchCityDataFetchStateDate 方法替换为详细实现。 - Hopeless
是的,我有修改“城市”和所有内容的权限。 - Shekhar Pankaj
我不相信你说“无法概括它”的说法。 - Shekhar Pankaj
我会在方法中添加一个属性(带有相应的类名)。然后添加条件:“if typeof(T).Name == City”。然后使用“City”的属性调用方法。很棒,通用而干净。 - Tez Wingfield
4个回答

1
首先,你可以轻松地将Repository模式引入你的应用程序中。请记住,Repository是领域层(BLL)中的一个接口,在基础设施层(DAL)中实现。通常,通用存储库都有一个基类,封装了所有实体和标识(键)类型的基本CRUD操作。
然后,您可以使用依赖注入库(在域层中的存储库接口)将接口绑定到实现,或者更好地创建应用程序层,其合同(接口)将是您的用例,而实现将是粘合剂代码,将域和基础架构代码绑定以执行所述用例。请阅读有关领域驱动设计的更多信息。

话虽如此,有一种更现代的与存储交互的方法- 实现命令和查询分离。在这种方式中,所有的数据读取都是查询,所有的写入都是命令。它们两者也将具有通用基础,封装了许多逻辑。您将获得许多独立的命令和查询类,而不是可能充斥着许多方法的存储库,这些命令和查询类(希望)会更容易管理。

看看提供的链接,其实这并不是太高深的科学,所有的部分都可以随着时间轻松地拼凑成一个完整的大局。


1
你最好的选择是分支代码,就像我之前说的那样。然而,你可以通过一些在帮助类内声明的Dictionary<Type, Func<object>>使其更整洁,如下所示:
public static class DataFetcher {
    static Dictionary<Type, Func<object>> _fetchers;
    static Dictionary<Type, Func<object>> Fetchers {
      get {
          if(_fetchers == null) _fetchers = new Dictionary<Type, Func<object>>();
          return _fetchers;
      }
    }
    static DataFetcher(){
       Fetchers[typeof(City)] = FetchCityData;
       Fetchers[typeof(State)] = FetchStateData;
    }
    public static IList<T> FetchData<T>(){
       Func<object> f;
       if(Fetchers.TryGetValue(typeof(T), out f)){
           return (IList<T>) f();
       }
       return null;
    }
}

使用方法:

public static  List<T> GetDataList<T>() where T:class ,new()
{
    var data = DataFetcher.FetchData<T>();
    //...
}

仍然不理解您的第二个要求。

我有一个接口,我的数据访问方法在应用程序中不是静态的,我只是创建了一个控制台应用程序来复制我的需求 :)...现在你有什么建议! - Shekhar Pankaj
@ShekharPankaj,很难确定你现在具体拥有什么。也许你应该发布你的实际代码(尽可能缩短但不改变其设计结构)。 - Hopeless
请建议如果是这样的话!我想学习这种方式,其他的我会自己处理。 - Shekhar Pankaj
1
@ShekharPankaj 你的意思是什么?我在这里发布的代码很好地适配了你当前的代码。如果你想重新设计你的代码,那么这里的代码可能就没用了,否则它应该可以工作。 - Hopeless

1

实际上,你的DAL设计是错误的。

如果你的业务层中有很多类似的通用调用,你应该在你的DAL中公开通用数据访问器(如果你使用EF,那就是简单明了的)。

这意味着

  1. You should establish a common interface for such items, such as

    public interface IReferenceData
    {
        string Id { get; set; }
        string Name { get; set; }
    }
    
  2. You should expose generic method to fetch it

    public IEnumerable<T> GetReferenceData<T>() where T: IReferenceData
    
这样,你的 BLL 就会变得简单。
public static List<T> GetDataList<T>() where T: IReferenceData
{
    return Dal.GetReferenceData<T>().ToList()
}

(虽然在这种情况下你不需要 BLL)

更新:实际上,你不需要接口,我只是习惯以这种方式处理事情,这样我就可以向 BLL 添加一些处理。问题并不需要它。


2
“IReferenceData” 如何用于“City”和“State”?它们是完全不同类型的项目,可以包含完全不同类型的信息。“City”和“State”可能有一些共同属性,但当涉及到其他实体类型时,我们不能仅使用接口来处理它们。 - Hopeless
实际上,除非您想在BLL方法内部执行某些操作,否则您不需要此接口。我会进行更新。 - Sergei Rogovtcev

0
你可以使用反射来获取数据层类型,并使用MethodInfo.Invoke通过字符串名称调用方法,利用返回类型的类型名称。我已经编写了一个快速示例,但无法测试它,因为我在手机上!
Type dalType = Type.GetType("dalnamespace")

MethodInfo methodInfo = dalType.GetMethod(string.Format("Fetch{0}Data", objectTypeStringHere)); 
var returnValue = (List<T>)methodInfo.Invoke(dalType, null);

太棒了,我也在想类似的事情! - Tez Wingfield
虽然我很高兴你喜欢我的回答,但我要指出其他人所说的,即利用接口和像Serg建议的通用方法可能会有益处 :-) - Steven Brookes
1
我谨慎地拒绝...该解决方案无法扩展,并且被锁定到特定属性。 - Tez Wingfield
好的,这是基于现有信息做出的回应,这表明如果使用这种方法,范围内的项目具有相同的属性。无论你选择哪条路线,至少你有了一个答案! - Steven Brookes

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