使用对象列表连接表

时间:2014-03-04 16:31:05

标签: c# entity-framework join linq-to-entities

我有一张桌子,让我们说tblCar包含所有相关栏目,如Id,Make,Model,Color等。

我有一个包含两个参数Id和Model的汽车搜索模型。

public class CarSearch
{
   public int Id { get; set; }
   public string Model { get; set; }
}

var carSearchObjets = new List<CarSearch>();

使用原始数据列表(如Id列表),可以获得那些我可以做的ID:

var idList = new List<int> { 1, 2, 3 };
var carsFromQuery = context.Cars.Where(x => idList.Contains(x.Id);

但是如果我必须从列表中获取所有带有Id和型号的汽车,我该怎么做?在内存对象和表之间无法进行简单连接。

我需要类似的东西,

from m in context.Cars
join n in carSearchObjets 
     on new { Id = n.Id, Model = n.Model } equals new { Id = m.Id, Model = m.Model }
select m;

这显然不起作用。

请忽略任何拼写错误。如果您需要更多信息或问题不明确,请与我们联系。

4 个答案:

答案 0 :(得分:1)

管理它的一种(丑陋但工作)的方法是使用串联与“从未使用过”的concat char。

我的意思是一个永远不会出现在数据中的字符。这总是危险的,因为......永远不会确定,但你已经有了这个想法。

例如,我们会说我们的“从未使用过”的concat char将是~

这对于perf来说并不好,但至少在工作:

var carSearchObjectsConcatenated = carSearchObjets.Select(m => new { m.Id + "~" + m.Model});

然后你可以再次使用Contains(在数据库上连接):如果你想连接数据库端的字符串和数字,你需要使用SqlFunctions.StringConvert

var result = context.Cars.Where(m => 
                carSearchObjectsConcatenated.Contains(SqlFunctions.StringConvert((double)m.Id) + "~" + m.Model);

修改

另一个解决方案是使用Sorax提到的PredicateBuilder,或者如果你不想要第三方lib(或者PredicateBuilder非常好),可以构建你自己的Filter方法。

静态类中的那样:

public static IQueryable<Car> FilterCars(this IQueryable<Car> cars, IEnumerable<SearchCar> searchCars)
        {
            var parameter = Expression.Parameter(typeof (Car), "m");

            var idExpression = Expression.Property(parameter, "Id");
            var modelExpression = Expression.Property(parameter, "Model");

            Expression body = null;
            foreach (var search in searchCars)
            {
                var idConstant = Expression.Constant(search.Id);
                var modelConstant = Expression.Constant(search.Model);

                Expression innerExpression = Expression.AndAlso(Expression.Equal(idExpression, idConstant), Expression.Equal(modelExpression, modelConstant));
                body = body == null
                    ? innerExpression
                    : Expression.OrElse(body, innerExpression);
            }
            var lambda = Expression.Lambda<Func<Car, bool>>(body, new[] {parameter});
            return cars.Where(lambda);
        }

使用

var result = context.Cars.FilterCars(carSearchObjets);

这将生成一个类似

的sql
select ...
from Car
where 
 (Id = 1 And Model = "ax") or
 (Id = 2 And Model = "az") or
 (Id = 3 And Model = "ft")

答案 1 :(得分:1)

'PredicateBuilder'可能会有所帮助。

var predicate = PredicateBuilder.False<Car>();
carSearchObjects
.ForEach(a => predicate = predicate.Or(p => p.Id == a.Id && p.Model == a.Model));

var carsFromQuery = context.Cars.AsExpandable().Where(predicate);

请注意有关EF的链接中的文字:

  

如果您正在使用Entity Framework,则需要完整的LINQKit -   用于AsExpandable功能。你可以参考   LINQKit.dll或将LINQKit的源代码复制到您的应用程序中。

答案 2 :(得分:0)

老派解决方案..

//in case you have a 
List<CarSearch> search_list; //already filled

List<Cars> cars_found = new List<Cars>();
foreach(CarSearch carSearch in search_list)
{
    List<Cars> carsFromQuery = context.Cars.Where(x => x.Id == carSearch.Id && x.Model == carSearch.Model).ToList();
    cars_found.AddRange(carsFromQuery);
}

不要担心for循环。

答案 3 :(得分:0)

我登陆了传递xml列表作为sql查询的参数,并加入了该列表:

var xml = new XElement("Cars", yourlist.Select(i => new XElement("Car", new XElement("Id", i.Id), new XElement("Model", i.Model))));
var results = Cars
        .FromSql("SELECT cars.*"
                + "FROM @xml.nodes('/Cars/Car') Nodes(Node)"
                + "JOIN Cars cars on cars.Id = Nodes.Node.value('Id[1]', 'int') and cars.Model = Nodes.Node.value('Model[1]', 'varchar(100)')",
            new SqlParameter("@xml", new SqlXml(xml.CreateReader())));

我为实体框架核心用户创建了一个nuget包扩展名:

SuperFast.Extensions