【问题标题】:How can I retrieve many entities by composite key?如何通过复合键检索多个实体?
【发布时间】:2013-01-14 11:34:09
【问题描述】:

我正在使用实体框架(如果重要,则为 4.2),并且我有一个具有复合主键(两个 int 列)的模型。我有一个整数对列表,它们代表我想从数据库中提取的对象列表。有没有一种简单的方法可以使用单个查询来提取所有这些对象?

我已尝试将查询与我的列表一起加入,如下所示:

List<Tuple<int, int>> ids = GetIds();

var data =
    from e in ctx.CompositeEntities
    join i in ids on new {e.KeyA, e.KeyB} equals new {KeyA = i.Item1, KeyB = i.Item2}
    select e;

但是,这总是会导致异常“无法创建类型为 'System.Tuple`2' 的常量值。在此上下文中仅支持原始类型('例如 Int32、String 和 Guid')。”

【问题讨论】:

    标签: sql-server entity-framework entity-framework-4 composite-key


    【解决方案1】:

    我希望看到一种更简洁的方法来做到这一点,但是......至少它应该可以工作。

    解决方案1

    您需要什么:连接您的元组列表,然后使用SqlFunctions.StringConvert 连接您的实体中的“int 键”。由于它只需要 double 或 decimal 作为参数,因此您必须先将 int 转换为 double。

    List<Tuple<int, int>> ids = GetIds();
    
    var concatenatedIds = ids.Select(m => m.Item1 + "," + m.Item2).ToList();
    
    var result = ctx.CompositeEntities
                    .Where(conatenatedIds.Contains(
                           SqlFunctions.StringConvert((double)m.KeyA) + 
                           "," + 
                           SqlFunctions.StringConvert((double)m.KeyB)));
    

    解决方案2

    使用PredicateBuilder

    List<Tuple<int, int>> ids = GetIds();
    
    var predicate = Predicate.False<CompositeEntity>();
    foreach (var tuple in ids) 
       predicate = predicate.Or(m => m.KeyA == tuple.Item1 && m.KeyB == tuple.Item2);
    
    var result = ctx.CompositeEntities.Where(predicate);
    

    解决方案3

    先枚举,再比较。 当然,这将从数据库中获取所有 CompositeEntities。

    【讨论】:

    • 我找不到 Predicate.False() 和 predicate.Or() - 我应该参考什么来获得这些?
    • @Rattle 查看 PredicateBuilder 的链接,所有内容都在网站上进行了解释。
    【解决方案2】:

    我无法尝试这个,但尝试使用类似这样的 where 子句。

    from e in ctx.CompositeEntities
    where ids.Where(i => e.KeyA == i.Item1  && e.KeyB == i.Item2).Count() > 0
    select e;
    

    ===== 编辑 =====

    它失败了,但同样的例外,谢谢你的鼓励。

    假设你的 key 是 int 而不是 bigint

    public class e1 {
        public int KeyA { get; set; }
        public int KeyB { get; set; }
    }
    
    public class e1Configuration : EntityTypeConfiguration<e1> {
        public e1Configuration()
            : base() {
                HasKey(x => new { x.KeyA, x.KeyB });
        }
    }
    
    public class TestEFContext : DbContext {
        public IDbSet<e1> es { get; set; }
    
        public TestEFContext(String cs)
            : base(cs) {
            Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Configurations.Add(new e1Configuration());            
        }
    }
    
    class Program {
        static void Main(String[] args) {
    
    
            using (TestEFContext c = new TestEFContext(@"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True")) {
                c.es.Add(new e1 { KeyA = 1, KeyB = 1 });
                c.es.Add(new e1 { KeyA = 1, KeyB = 2 });
                c.es.Add(new e1 { KeyA = Int32.MaxValue, KeyB = 2 });
                c.SaveChanges();
            }
    
            List<Tuple<int, int>> ids = new List<Tuple<int, int>> { 
                new Tuple<int, int>(1, 1), 
                new Tuple<int, int>(Int32.MaxValue, 2),};
    
            List<Int64> lis = (from t in ids select (Int64)t.Item1 * 2^ 32 + t.Item2).ToList();
    
            using (TestEFContext c0 = new TestEFContext(@"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True")) {
                var v = from e in c0.es
                        where lis.Contains((Int64)e.KeyA * 2 ^ 32 + e.KeyB)
                select e;
    
                Console.WriteLine(v.Count());
            }
    
        }
    }
    

    【讨论】:

      【解决方案3】:

      我正在处理一个几乎相同的场景,但我会尝试将其映射到上面的场景 - 如果它无法编译,请告诉我。

      这与 Raphaël 的示例 2 的基本想法相同,但我无法让它运行。我将 Predicate 构建为单个实体的查询联合,每个元组一个。

        List<Tuple<int, int>> ids = GetIds();
      
        ids.Aggregate<Tuple<int,int>,IQueryable<CompositeEntity>>(
            null,
            (current, id) => 
              current == null ? SingleCompositeEntity(ctx.CompositeEntities, id)
              : current.Union(SingleCompositeEntity(ctx.CompositeEntities, id)));
      }
      
      
      private static IQueryable<CompositeEntity> 
              SingleCompositeEntity(IQueryable source, Tuple<int,int> tuple)
      {
         return source.Where(m => m.KeyA == tuple.Item1 && m.KeyB == tuple.Item2)
      
      }
      

      【讨论】:

        【解决方案4】:

        未经测试,但我认为可行。

        向您的实体对象添加一个属性,以将键公开为元组。

        public Tuple<int, int> TupleKey
        {
            get { return new Tuple<int, int>(_keyA, _keyB);}
        }
        

        然后更改您的查询以使用此新属性。

        List<Tuple<int, int>> ids = GetIds();
        
        var data = from e in ctx.CompositeEntities
                    where ids.Contains(e.TupleKey)
                    select e;
        

        【讨论】:

        • 我遇到同样的异常,无法创建类型为“System.Tuple`2”的常量值。
        • 未映射的属性(以及没有 setter 的属性无法在 db 中映射)不能在 linq to entity 查询中使用。
        猜你喜欢
        • 2021-05-14
        • 2013-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-09
        • 1970-01-01
        相关资源
        最近更新 更多