Как я могу динамически запрашивать разные таблицы с помощью LINQ?
Автор вопроса: TevisОтветов: 3
Поэтому я не уверен, можете ли вы это сделать или нет, но я бы хотел избежать использования строк 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()
Спасибо за помощь!
Грубо... Я не хочу этого делать, просто подумал, что должен быть способ сделать это, поскольку с SQL это так просто. Tevis 10.11.2014
Все ваши объекты EF имеют свойства Id и Status? Ваша проблема меньше связана с самим LINQ, а больше с тем фактом, что С# является статически типизированным языком - вы не можете (легко) делать то, что хотите, без некоторого базового типа, представляющего общие свойства, которые вы хотите использовать в своем универсальном запрос. rsenna 10.11.2014
Кроме того, наличие большого количества DbContext не помогает, даже учитывая, что есть способы получить текущий контекст из данного объекта. rsenna 10.11.2014
Вот фрагмент рабочего кода, который может обновлять разные таблицы во время выполнения из разных контекстов без использования отражения.
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 работает, потому что он использует свойства и методы, которые являются общими для всех типов (либо с базовыми типами/наследованием, либо с помощью реализации интерфейсов).
Ничто из этого на самом деле не является динамическим программированием (что возможно при использовании динамического типа), и я использовал термин "определяемое время выполнения", а не "динамическое", чтобы описать, как это работает.
Ответ 2 · Icaro Camelo
В функциональном программировании есть техника, называемая каррированием, когда вы можете передавать столько параметров, сколько хотите, чтобы иметь к ним доступ. Вот пример: http://blogs.msdn.com/b/sriram/archive/2005/08/07/448722.aspx
P.S: Вы можете использовать функцию каррирования для перебора ваших DBContexts.
Ответ 3 · noocyte
Я думаю, вам нужно использовать отражение, если вы хотите использовать "код", а не строки 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);
}
}
Очевидно, вы можете затем поместить строки в список и т. д., что вам нужно.