Как я могу динамически запрашивать разные таблицы с помощью LINQ?

Поэтому я не уверен, можете ли вы это сделать или нет, но я бы хотел избежать использования строк SQL, если смогу. То, что я хотел бы сделать с Linq/DbContexts, это следующее, что можно легко сделать с помощью SQL:

string sql = "UPDATE " + tableName + " SET Status=0 WHERE Id=" + formId.ToString();

Я могу легко поместить это в цикл, где tableName и formId задаются динамически и выполняются без проблем.

У меня есть несколько DbContexts, поэтому я не знаю, как сделать что-то вроде:

var db = new *dynamicallyChosenContext*()

var query = from p in db.*dynamicallyChosenAlso*
            where p.Id == formId
            select p;

foreach (var result in query)
{
    result.Status = 0;
}

db.SaveChanges()

Спасибо за помощь!


person Tevis    schedule 10.11.2014    source источник
comment
Грубо... Я не хочу этого делать, просто подумал, что должен быть способ сделать это, поскольку с SQL это так просто.   -  person Tevis    schedule 10.11.2014
comment
Все ваши объекты EF имеют свойства Id и Status? Ваша проблема меньше связана с самим LINQ, а больше с тем фактом, что С# является статически типизированным языком - вы не можете (легко) делать то, что хотите, без некоторого базового типа, представляющего общие свойства, которые вы хотите использовать в своем универсальном запрос.   -  person rsenna    schedule 10.11.2014
comment
Кроме того, наличие большого количества DbContext не помогает, даже учитывая, что есть способы получить текущий контекст из данного объекта.   -  person rsenna    schedule 10.11.2014


Ответы (3)


arrow_upward
1
arrow_downward

Вот фрагмент рабочего кода, который может обновлять разные таблицы во время выполнения из разных контекстов без использования отражения.

namespace DemoContexts
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;


    public interface IThing
    {
        int Id { get; set; }
        int Status { get; set; }
    }

    public class FirstPersonThing : IThing
    {
        [System.ComponentModel.DataAnnotations.Key]
        public int Id { get; set; }
        public int Status { get; set; }
        public string Foo { get; set; }
    }

    public class SecondPersonThing : IThing
    {
        [System.ComponentModel.DataAnnotations.Key]
        public int Id { get; set; }
        public int Status { get; set; }
        public string Bar { get; set; }
    }

    public class FirstContext : DbContext
    {
        public FirstContext() : base("FirstContext") { }
        public DbSet<FirstPersonThing> MyThings { get; set; }
        public DbSet<SecondPersonThing> YourThings { get; set; }
    }

    public class SecondContext : DbContext
    {
        public SecondContext() : base("SecondContext") { }
        public DbSet<FirstPersonThing> MyThings { get; set; }
        public DbSet<SecondPersonThing> YourThings { get; set; }
    }


    class Program
    {
        static void Main(string[] args)
        {
            int contextType = 1;
            int thingType = 1;

            DbContext db = RunTimeCreatedContext(contextType);
            IQueryable<IThing> collection = RunTimeCreatedCollection(db, thingType);

            UpdateRuntimeDeterminedThings(db, collection, 1);

            Console.ReadLine();
        }

        public static void UpdateRuntimeDeterminedThings(DbContext db,
                                               IQueryable<IThing> collection,
                                               int formId)
        {
            var querySet = collection.Where(p => p.Id == formId).ToList();
            foreach (var result in querySet)
            {
                result.Status = 0;
            }

            db.SaveChanges();
        }

        static DbContext RunTimeCreatedContext(int contextType)
        {
            if (contextType == 0)
            {
                return new FirstContext();
            }
            else
            {
                return new SecondContext();
            }
        }

        static IQueryable<IThing> RunTimeCreatedCollection(DbContext db, int thingType)
        {
            if (thingType == 0)
            {
                return db.Set(typeof(FirstPersonThing)) as IQueryable<IThing>;
            }
            else
            {
                return db.Set<SecondPersonThing>();
            }
        }
    }
}

Первое, что нужно отметить, это то, что все это статически типизировано, поэтому для выполнения общего запроса к объектам разных типов эти объекты должны иметь общие сигнатуры свойств, и это концептуально выражается в интерфейсе IThing.

Второе, на что следует обратить внимание, это то, как генерируется IQueryable. Он создается с помощью DbContext.Set Method (Type) в первый экземпляр (для FirstPersonThings), он создается с помощью DbContext.Set<TEntity> Method во втором экземпляре. Первый использует тип, определенный во время выполнения, и требует приведения (но может быть полезно использовать проходящие типы во время выполнения), второй использует дженерики, а типы определяются во время компиляции. Очевидно, существует ряд других способов, которыми эта функция может работать.

Наконец, метод UpdateRuntimeDeterminedThings работает, потому что он использует свойства и методы, которые являются общими для всех типов (либо с базовыми типами/наследованием, либо с помощью реализации интерфейсов).

Ничто из этого на самом деле не является динамическим программированием (что возможно при использовании динамического типа), и я использовал термин «определяемое время выполнения», а не «динамическое», чтобы описать, как это работает.

person AlexC    schedule 11.11.2014

arrow_upward
0
arrow_downward

В функциональном программировании есть техника, называемая каррированием, когда вы можете передавать столько параметров, сколько хотите, чтобы иметь к ним доступ. Вот пример: http://blogs.msdn.com/b/sriram/archive/2005/08/07/448722.aspx

P.S: Вы можете использовать функцию каррирования для перебора ваших DBContexts.

person Icaro Camelo    schedule 10.11.2014

arrow_upward
0
arrow_downward

Я думаю, вам нужно использовать отражение, если вы хотите использовать «код», а не строки sql. Именно так работает С#... Вот как вы можете это сделать:

using System;
using System.Data.Entity;
using System.Linq;

public class TestContext : DbContext
{
    public DbSet<Box> Boxes { get; set; }
    public DbSet<Thing> Things { get; set; }
}

public abstract class Base
{
    public int Id { get; set; }
}

public class Box : Base
{
    public string Title { get; set; }
}

public class Thing : Base
{
    public string Name { get; set; }
}

internal class Program
{
    private static void Main(string[] args)
    {
        var db = new TestContext();

        DoIt(GetPropValue(db, "Boxes") as IQueryable<Base>);
        DoIt(GetPropValue(db, "Things") as IQueryable<Base>);
    }

    private static void DoIt(IQueryable<Base> b)
    {
        Console.WriteLine(
            b.Single(t => t.Id == 1).Id);
    }

    private static object GetPropValue(object src, string propName)
    {
        return src.GetType().GetProperty(propName).GetValue(src, null);
    }
}

Очевидно, вы можете затем поместить строки в список и т. д., что вам нужно.

person noocyte    schedule 10.11.2014