【问题标题】:Generics & static classes. Implement query helper method泛型和静态类。实现查询助手方法
【发布时间】:2014-02-21 08:58:33
【问题描述】:

目前我们实现了一个这样的映射服务(该服务使用automapper,我们在这部分使用了它的投影功能)

// Injected
// IGenericRepository<Entity> entityRepo

 var query = this.entityRepo
                 .FindAll(a => a.Id == someId)
                 .Take(1);

 var result = this.mappingService
                  .Map<Entity, EntityDto>(query)
                  .FirstOrDefault();

我想创建一个允许我执行以下操作的扩展程序

var result = this.entityRepo
                 .FindAll(a => a.Id == someId)
                 .Take(1).Map<EntityDto>()   <--- Entity inferred from repo type
                 .FirstOrDefault();

我目前的尝试:

 public static class IQueryableExtensions
 {
     private static IMappingService mappingService;

     // will need to be called in app initialization
     public static void InitialiseMapper(IMappingService service)
     {
         mappingService = service;
     }

     public static IEnumerable<TDto> Map<TAttribute, TDto>(this IQueryable<TAttribute> value)
            where TDto : class
            where TAttribute : IEntity
     {
        return mappingService.Map<TAttribute, TDto>(value);
     }
 }

因此目前我的实现看起来像这样。

var result = this.entityRepo
                     .FindAll(a => a.Id == someId)
                     .Take(1).Map<Entity,EntityDto>()
                     .FirstOrDefault();

问题:

1) 我将如何从 IQueryable 对象推断实体类型

2) 我意识到在创建静态类时我无法创建带参数的构造函数。我初始化映射器的方式是最好/唯一的方式吗?

【问题讨论】:

  • 我不知道是不是只有我,如果我读错了。你到底为什么要使用.Take(1).FirstOrDefault(),你不能直接去.FirstOrDefault(a =&gt; a.Id == someId).Map&lt;Entity, EntityDto&gt; 吗?你也不能改变你的 Map() 方法并将其更改为 MapFirstOrDefault() 并在你的帮助类中执行第一个或默认值,因为你已经完成了编写扩展方法的工作。
  • Take(1),仍将返回一个集合和 FirstOrDefault(),获取第一个项目并将其映射到您的实体类型。但是在这个例子中 Take(1), & FirstOrDefault() 与问题无关,只是作为查询的一个例子......因为我的问题与映射部分 EG 有关。 IQueryable 到 IEnumerable
  • 那么让我看看这次我做对了吗?所以你是说这段代码return mappingService.Map&lt;TAttribute, TDto&gt;(value); 没有返回IEnumerable&lt;EntityDto&gt;,但你希望它返回IEnuerable&lt;EntityDto&gt;
  • 我想将调用语法从:.Map(query) 更改为 .Map()

标签: c# generics extension-methods automapper


【解决方案1】:

我尝试过反思。约束仅用于演示。如果要多次调用反射代码,请务必缓存最终的methodinfo。

void Main()
{
    var a = new Entity[] {new Entity { name = "a"},new Entity { name = "b"}};

    Console.WriteLine(a.Take(1).Map<EntityDto>());
}

public class Entity
{
    public string name;
}

public class EntityDto
{
    public string dtoname;

}

public static class EntityExtensions
{
    public static IEnumerable<U> Map<T,U>(this IEnumerable<T> e) where T: Entity where U: EntityDto, new()
    {
        foreach(var a in e)
        {
            yield return new U() { dtoname = a.name };
        }
    }


    public static IEnumerable<U> Map<U>(this IEnumerable<object> e)
    {
        var method = typeof(EntityExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public)     
        .Where(m => m.Name == "Map" && m.GetGenericArguments().Length == 2)
        .Single();
        method = method.MakeGenericMethod(e.GetType().GetGenericArguments()[0], typeof(U));

        return method.Invoke(null, new object[] { e}) as IEnumerable<U>;
    }
}

【讨论】:

    【解决方案2】:

    1) 目前,您在 C# 中根本无法做到这一点。类型推断不够好。您可以指定所有类型参数,也可以不指定。

    编辑:如果你真的想要带有单个参数的版本,你必须删除第二个类型参数,将参数输入为非通用IQueryable并处理它。一种方法是在运行时确定通用的IQueryable&lt;T&gt; 类型。然而,这需要反思。对于IQueryable,您还可以使用查询提供程序来绕过反射。

    2) 可以使用静态类型构造函数。

    public static class MyExtensions {
        static MyExtensions() {
            //initialization goes here
        }
    }
    

    这种类型的构造函数甚至被称为线程安全的。但是,如果您设法在此处抛出异常,则无法访问 MyExtensionsclass!

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 2012-12-04
    • 2010-10-16
    • 2012-02-10
    • 2010-10-30
    • 1970-01-01
    相关资源
    最近更新 更多