C#泛型/接口 - 根据类返回不同的类型

时间:2013-03-28 17:09:26

标签: c# generics interface

对于工作,我们有特定类型的记录,但每个项目都有自己的实现。列等是不同的,但通常过程是相同的(记录分为批次,批次分配,批次完成,批次返回,批次发送等)。许多列也很常见,但有时会有名称更改(一个中的BatchId与另一个中的Id相比。[Column(“name”)]负责此问题)。

目前,这是我使用界面中给出的通用组件实现批量分配功能的方法:

public interface IAssignment
{
   // properties
   ...
   // methods
   T GetAssignmentRecord<T>(int UserId, int BatchId) where T : IAssignment;
   List<T> GetAssignmentRecords<T>(int UserId) where T : IAssignment;
}

现在我有两个批量分配的项目。由于这些是在EntityFramework中完成的,Namespace1中的Assignment和Namespace2中的Assignment是完全不同的东西,但是受某些公共组件(ID,分配的用户,签入等)的约束,这些组件驱动所有返回它们的方法。

我认为我的主要问题是如果我做错了,如果有更好的方法来实现这一点,我可以将数据传输到我的控制器中,让控制器看起来有点类似于项目,同时具有尽可能多的方法工作是自动处理的(主要是为了在我需要进行更新时发生“修复一个,修复所有”的情况)。

以下是我如何为namespace1执行实现的示例:

public class Assignment
{
    ...
    public T GetAssignmentRecord<T>(int UserId, int BatchId) where T : IAssignment
    {
        var db = new Database1Context();
        return (T) Convert.ChangeType(db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned).First(), typeof(T));
    }
}

在控制器中:

Assignment assignment = new Assignment();
var record = assignment.GetAssignmentRecord<Assignment>(userid, batchid);
// do stuff

控制器代码实际上是我假设它可以工作的方式。我已经通过作业课完成了,如果我以正确的方式做这件事,我现在感到很困惑。我觉得这可能是不正确的原因是我基本上说“接口正在寻找一个通用的,我使用实体框架从数据库中获取一个强类型对象,我将它转换为通用,当我我正在提出请求,我要求的是我最初转换为通用的同类强类型对象。“

有更好的方法吗?或者我应该采取完全不同的方向?

3 个答案:

答案 0 :(得分:3)

如果我理解了你的目标是什么,我会这样做,例如这样......

interface IAssignment
{
}

interface IRepo<out T> where T : IAssignment
{
    T GetAssignmentRecord(int UserId, int BatchId);
    IEnumerable<T> GetAssignmentRecords(int UserId);
}

class AssignmentRecord : IAssignment
{
}
class AssignmentWeb : IAssignment
{
}

class RepoDb : IRepo<AssignmentRecord>
{
    public AssignmentRecord GetAssignmentRecord(int UserId, int BatchId)
    {
        //using(var db = new MyDbContext())
        //{
        //    return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned).First();
        //}
        return new AssignmentRecord();
    }

    public IEnumerable<AssignmentRecord> GetAssignmentRecords(int UserId)
    {
        //using(var db = new MyDbContext())
        //{
        //    return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned);
        //}
        return new List<AssignmentRecord> 
            {
                new AssignmentRecord(),
                new AssignmentRecord(),
                new AssignmentRecord(),
                new AssignmentRecord(),
                new AssignmentRecord(),
                new AssignmentRecord(),
                new AssignmentRecord(),
                new AssignmentRecord(),
            };
    }
}

class RepoWeb : IRepo<AssignmentWeb>
{
    public AssignmentWeb GetAssignmentRecord(int UserId, int BatchId)
    {
        // fetch it from some web service...
        return new AssignmentWeb();
    }

    public IEnumerable<AssignmentWeb> GetAssignmentRecords(int UserId)
    {
        //using(var db = new MyDbContext())
        //{
        //    return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned);
        //}
        return new List<AssignmentWeb> 
            {
                new AssignmentWeb(),
                new AssignmentWeb(),
                new AssignmentWeb(),
            };
    }
}

class MYController
{
    public IRepo<IAssignment> Repository { get; set; } // you can inject this e.g. DI

    public IAssignment GetAssignment(int userid, int batchid)
    {
        return Repository.GetAssignmentRecord(userid, batchid);
    }

    public IEnumerable<IAssignment> GetAllAssignments(int userid)
    {
        return Repository.GetAssignmentRecords(userid);
    }
}

class ProgramAssignment
{
    static void Main(string[] args)
    {
        try
        {
            var controller = new MYController();
            controller.Repository = new RepoDb();

            IAssignment assignment = controller.GetAssignment(0, 0);
            IEnumerable<IAssignment> all = controller.GetAllAssignments(0);

            controller.Repository = new RepoWeb();

            assignment = controller.GetAssignment(0, 0);
            all = controller.GetAllAssignments(0);
        }
        catch
        {
            Console.WriteLine("");
        }
    }
}

至于为什么out - 在我的其他帖子中还有更多......

How to make generic class that contains a Set of only its own type or subtypes as Children?

答案 1 :(得分:1)

假设2 Assignment具有不同的属性(可能是一些额外的属性),但是一些属性是相同的,并且它们来自不同的数据库,有很多方法可以做到这一点。但“最好的”(对我来说)就是依赖注入。

您在Assignment类中的活动(方法)应该移动到一个单独的“服务”类。这增加了Assignment的模块性,因为它只是一个POCO。

对于数据访问,创建一个单独的类(存储库)以检索/插入/更新/删除您的数据。示例如下:

public AssignmentRepository: IAssignmentRepository{
  public Assignment GetAssignmentRecord(int userId, int batchId){

  }
}

public BatchAssignmentRepository: IAssignmentRepository{
  public Assignment GetAssignmentRecord(int userId, int batchId){

  }
}

如果你问为什么有2个存储库而不是1个,它会使代码多余吗?是的,但你也必须考虑它会增加模块性。如果您在BatchAssignment中更改了某些内容(可能更改了列名,添加了其他列等),那么您不需要在Assignment中应用相同的内容,并且避免使用“if batchAssignment else”逻辑。

来电者的使用方式如下:

IAssignmentService service = new AssignmentService();
IAssignmentRepository repository = new AssignmentRepository();
Assignment a = repository.GetAssignmentRecord(userId, batchId);
service.DoSomething(a);

答案 2 :(得分:1)

考虑适配器层。该层应该将输入数据转换为通用结构/类,然后可以一致地处理,尽管是泛型。当然,它也会在“出站”方面重新转换为特定数据库所期望的那一方。这假设没有数据源具有未在其他数据中定义的数据,或者您可以为所述缺失数据定义有效的默认值。

我想你需要为不同的项目使用不同的适配器。也许这是dependency injection的工作。基本上在运行时,您将获取所需的特定代码(适配器类)。

Introduction to Unity.