When the DoTranslation method is run, the output is the same hash code for
all of the TranslatedObjects. Why is this happening as opposed to having a new List for each TranslatedObject?
public class TranslatedObject
{
public static Expression<Func<OriginalObject, TranslatedObject>> ObjectTranslator = o => new TranslatedObject
{
id = o.id
translatedList = new List<String>()
};
public int id { get; set; }
public List<String> translatedList { get; set; }
}
public class Translator
{
public void DoTranslation()
{
//3 objects returned with ids 1, 2, and 3 respectively
IQueryable<OriginalObject> originalObjects = dataContext.OriginalObjects.Where(o => o.id == 1 || o.id == 2 || o.id == 3);
var translatedObjects = originalObjects.Select(TranslatedObject.ObjectTranslator).ToArray();
foreach(TranslatedObject translated in translatedObjects)
{
Console.WriteLine(translated.translatedList.GetHashCode());
}
}
}
UPDATE: Changed service call to the following linq-to-sql call: dataContext.OriginalObjects.Where(o => o.id == 1 || o.id == 2 || o.id == 3).
Related
I have two tables tblFriends and tblUsers where I store user's id and friend's id.
Now I would like to find mutual friends of user1 and user2 in table 'tblFriends' AND details of them e.q. nickname,age ...
tblUsers:
Username nvarchar
Avatar nvarchar
Age int
tblFriends:
IdUser1 nvarchar
IdUser2 nvarchar
FriendStatus int
I find the bellow solution in sql and it works fine but I need the equivalent of this query in LINQ or Lambda
the solution that I find is here ( https://www.codeproject.com/Questions/280296/query-to-display-mutual-friends )
SELECT P1.Name
FROM dbo.Friendship AS F1
JOIN dbo.Person AS P1 ON P1.ID = F1.FriendID
WHERE F1.PersonID = 1 AND
F1.FriendID IN (SELECT F2.FriendID
FROM dbo.Friendship AS F2
WHERE F2.PersonID = 2)
No need for Join:
var mutualFriendsNames = db.Users
.Where(u =>
db.FriendShips.Any(f => f.FriendId == u.Id && f.UserId == 1) &&
db.FriendShips.Any(f => f.FriendId == u.Id && f.UserId == 2))
.Select(p => p.Name);
Just notice that this will work only if friendship relation is one way relationship (if user1 is friend with user2 then user2 is not necessary friend with user1).
If the relation is two ways, then you have two options:
Define the relationship as two rows in DB (each one describes a way).
The relationship is implicitly a two-ways relationship, then you should refactor the LINQ query above:
var mutualFriendsNames = db.Users
.Where(u =>
db.FriendShips.Any(f =>
(f.FriendId == u.Id && f.UserId == 1) ||
(f.FriendId == 1 && f.UserId == u.Id)) &&
db.FriendShips.Any(f =>
(f.FriendId == u.Id && f.UserId == 2) ||
(f.FriendId == 2 && f.UserId == u.Id)))
.Select(p => p.Name);
This example is equivalent to the given SQL query
using System;
using System.Collections.Generic;
using System.Linq;
namespace Friends
{
class Program
{
static void Main(string[] args)
{
var data = new GenerateExampleData();
var lstFriendId = (from f in data.lstFriend
where f.PersonId == 2
select f.FriendId
);
var lstMutualFriend = (from f in data.lstFriend
join p in data.lstPerson on f.FriendId equals p.Id
where lstFriendId.Contains(f.FriendId) && f.PersonId == 1
select p.Name
);
foreach (var item in lstMutualFriend)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
public class GenerateExampleData
{
public List<Person> lstPerson;
public List<Friendship> lstFriend;
public GenerateExampleData()
{
lstPerson = new List<Person>
{
new Person
{
Id = 1,
Name ="Person1"
},
new Person
{
Id = 2,
Name ="Person2"
},
new Person
{
Id = 3,
Name ="Person3"
},
new Person
{
Id = 4,
Name ="Person4"
},
};
lstFriend = new List<Friendship>
{
new Friendship
{
PersonId = 1,
FriendId = 2
},
new Friendship
{
PersonId = 1,
FriendId = 4
},
new Friendship
{
PersonId = 2,
FriendId = 4
},
};
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Friendship
{
public int PersonId { get; set; }
public int FriendId { get; set; }
}
}
How can I convert expression tree to Dictionary?
For example:
class Dummy
{
public int Id { get; set; }
public string Name { get; set; }
}
Example 1:
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2)
//Result is dictionary with item:
<key, value>Id,2
Example 2:
var s = "Foo";
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2 && t.Name == s)
//Result is dictionary with items:
<key, value>Id,2
<key, value>Name,"Foo"
I know EF Core does this, but don't know how, and source code is to complicated for me to parse it.
I should say expression doesn't contain || and ().
For example:
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2 || t.Id == 3)
or
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2 && (Name == "Foo" || Id Name == "Test")
If you are sure that expressions will be in provided format only - you can do something like this:
public class Dummy
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class ExpressionConverter
{
public static Dictionary<string, string> Convert<T>(Expression<Func<T,bool>> expression)
{
var result = new Dictionary<string,string>();
var current = (BinaryExpression)expression.Body;
while (current.NodeType != ExpressionType.Equal)
{
ParseEquals((BinaryExpression)current.Right);
current = (BinaryExpression)current.Left;
}
ParseEquals(current);
void ParseEquals(BinaryExpression e)
{
var key = (MemberExpression) e.Left;
var value = (ConstantExpression) e.Right;
result.Add(key.Member.Name, value.Value.ToString());
}
return result;
}
}
Usage:
var test = ExpressionConverter.Convert<Dummy>(x => x.Id == 5 && x.Name == "dummy" && x.Age == 11);
Or replace ParseEquals:
void ParseEquals(BinaryExpression e)
{
var key = (MemberExpression) e.Left;
object value;
switch (e.Right)
{
case ConstantExpression constantExpression:
value = constantExpression.Value;
break;
case MemberExpression memberExpression:
var obj = ((ConstantExpression)memberExpression.Expression).Value;
value = obj.GetType().GetField(memberExpression.Member.Name).GetValue(obj);
break;
default:
throw new UnknownSwitchValueException(e.Right.Type);
}
result.Add(key.Member.Name, value);
}
To support:
var myVar = "dummy";
var test = ExpressionConverter.Convert<Dummy>(x => x.Id == 5 && x.Name == myVar && x.Age == 11);
I have a linq query like
var tmp = (from t in db.sometable
where (bunch of conditions)
group t by new { t.somefield} into g
select new
{
Name = g.Key.someobject.Name,
xColor = g.Sum(a => (int?)a.LineItems.Where(m => m.Product == "x" && m.Color == true)
.Sum(m => m.Quantity)),
xBW = g.Sum(a => (int?)a.LineItems.Where(m => m.Product == "x" && m.Color == false)
.Sum(m => m.Quantity))
})
Now I want to optimize this query. If I had to write a stored procedure I would have written some thing like:
if(a.LineItems.Product == 'x')
{
if(m.Color == true)
xColor++;
else
xBW++;
}
so that I get aggregated value in one scan. Basically I wanted to optimize this query to avoid multiple scans.
check out this simple code snippet in Linqpad, which does the way you expect, for running in visual studio, remove the Dump method call. I have simplified the class structure a bit for a simple demo, it is just a POCO, not a complex type containing another list inside
void Main()
{
var testList = Test.CreateList();
var tmp = (from t in testList
group t by new { t.Name } into g
select new
{
Name = g.Key.Name,
xColor = g.Sum(a =>
{
if (a.Product == "x" && a.Color == true)
return a.Quantity;
return 0;
}),
xBW = g.Sum(a =>
{
if (a.Product == "x" && a.Color == false)
return a.Quantity;
return 0;
})
});
tmp.Dump();
}
public class Test
{
public string Name { get; set; }
public string Product { get; set;}
public bool Color { get; set;}
public int? Quantity { get; set;}
public static List<Test> CreateList()
{
return new List<Test>()
{
new Test {Name="A",Color = true,Product="x",Quantity=5},
new Test {Name="A",Color = true,Product="x",Quantity=5},
new Test {Name="A",Color = true,Product="x",Quantity=5},
new Test {Name="B",Color = true,Product="x",Quantity=5},
new Test {Name="B",Color = true,Product="x",Quantity=5},
new Test {Name="B",Color = true,Product="x",Quantity=5}
};
}
}
However whether this is efficient or not can still be debated
You can do the same thing that would work in SQL. If there's a subset of data (lines matching particular criteria) then select those first into an array. Then you can execute your larger query against that subset.
I have to next 2 entities in my project
public class Product
{
public Product()
{
this.ProductImages = new HashSet<ProductImage>();
this.ProductParams = new HashSet<ProductParam>();
}
public int ID { get; set; }
public int BrandID { get; set; }
public int CodeProductTypeID { get; set; }
public string SeriaNumber { get; set; }
public string ModelNumber { get; set; }
public decimal Price { get; set; }
public bool AvailableInStock { get; set; }
public virtual Brand Brand { get; set; }
public virtual CodeProductType CodeProductType { get; set; }
public virtual ICollection<ProductImage> ProductImages { get; set; }
public virtual ICollection<ProductParam> ProductParams { get; set; }
}
public class ProductParam
{
public int Id { get; set; }
public int ProductId { get; set; }
public int CodeProductParamId { get; set; }
public string Value { get; set; }
public virtual Product Product { get; set; }
public virtual CodeProductParam CodeProductParam { get; set; }
}
and I want to get list of Products which has list of specified parameters
var prodParamCritria = new List<ProductParam>()
{
new ProductParam(){CodeProductParamId =1, Value="Black" },
new ProductParam(){CodeProductParamId =2, Value="Steal"}
};
in sql I can do it by using EXISTS clause twise
SELECT *
FROM Products p
WHERE EXISTS (
SELECT *
FROM ProductParams pp
WHERE pp.ProductId = p.ID
AND (pp.CodeProductParamId = 1 AND pp.[Value] = N'Black')
)
AND EXISTS (
SELECT *
FROM ProductParams pp
WHERE pp.ProductId = p.ID
AND pp.CodeProductParamId = 2
AND pp.[Value] = N'Steal'
)
How can i get same result by EF methods or linq
Try this:
var products= db.Products.Where(p=>p.ProductParams.Any(pp=>pp.CodeProductParamId == 1 && pp.Value == "Black") &&
p.ProductParams.Any(pp=>pp.CodeProductParamId == 2 && pp.Value == "Steal"));
Update
The problem in work with that list of ProductParam to use it as a filter is that EF doesn't know how to translate a PodructParam object to SQL, that's way if you execute a query like this:
var products2 = db.Products.Where(p => prodParamCritria.All(pp => p.ProductParams.Any(e => pp.CodeProductParamId == e.CodeProductParamId && pp.Value == e.Value)));
You will get an NotSupportedException as you comment in the answer of #BostjanKodre.
I have a solution for you but probably you will not like it. To resolve that issue you could call the ToList method before call the Where. This way you will bring all products to memory and you would work with Linq to Object instead Linq to Entities, but this is extremely inefficient because you are filtering in memory and not in DB.
var products3 = db.Products.ToList().Where(p => prodParamCritria.All(pp => p.ProductParams.Any(e => pp.CodeProductParamId == e.CodeProductParamId && pp.Value == e.Value)));
If you want filter by one criteria then this could be more simple and you are going to be able filtering using a list of a particular primitive type. If you, for example, want to filter the products only by CodeProductParamId, then you could do this:
var ids = new List<int> {1, 2};
var products = db.Products.Where(p => ids.All(i=>p.ProductParams.Any(pp=>pp.CodeProductParamId==i))).ToList();
This is because you are working with a primitive type and not with a custom object.
I suppose something like that should work
db.Product.Where(x => x.ProductParams.FirstOrDefault(y => y.CodeProductParamId == 1) != null && x.ProductParams.FirstOrDefault(y => y.CodeProductParamId == 2) != null).ToList();
or better
db.Product.Where(x => x.ProductParams.Any(y => y.CodeProductParamId == 1) && x.ProductParams.Any(y => y.CodeProductParamId == 2)).ToList();
Ok, if you need to make query on parameters in list prodParamCriteria it will look like this:
db.Product.Where(x => prodParamCritria.All(c=> x.ProductParams.Any(p=>p.CodeProductParamId == c.CodeProductParamId && p.Value== c.Value))).ToList();
I forgot that complex types cannot be used in query database, so i propose you to convert your prodParamCriteria to dictionary and use it in query
Dictionary<int, string> dctParams = prodParamCritria.ToDictionary(x => x.CodeProductParamId , y=>y.Value);
db.Product.Where(x => dctParams.All(c => x.ProductParams.Any(p=> p.CodeProductParamId == c.Key && p.Value== c.Value))).ToList();
another variation:
IEnumerable<Int32> lis = prodParamCritria.Select(x => x.CodeProductParamId).ToList();
var q = Products.Select(
x => new {
p = x,
cs = x.ProductParams.Where(y => lis.Contains(y.Id))
}
).Where(y => y.cs.Count() == lis.Count()).
ToList();
with a named class like (or maybe without, but not in linqpad)
public class daoClass {
public Product p {get; set;}
public Int32 cs {get; set;}
}
IEnumerable<Int32> lis = prodParamCritria.Select(x => x.CodeProductParamId).ToList();
var q = Products.Select(
x => new daoClass {
p = x,
cs = x.ProductParams.Where(y => lis.Contains(y.Id))
}
).Where(y => y.cs.Count() == lis.Count()).
SelectMany(y => y.p).
ToList();
Hello I have just started to try and understand Parallel Linq and with my first attempt I am having no success. I am using EF 4.0 and a repository pattern class that I created to query the data. I don't believe that the repository pattern is the problem but I could be mistaken.
The database that I have isn't setup the way that I would like it but hey I inherited the system. The code that I am having the problem with is below:
var gId = Sql.ToGuid(Request["ID"]);
var lOrdersGridList = new OrdersGridList(); //Class that only contains properties
var lOrdersForContact = new BaseRepository<ORDER>()
.Find(i => i.ORDERS_CONTACTS.Where(b => b.CONTACT_ID == gId).Count() > 0).AsParallel()
.Select(i =>
new OrdersGridList
{
ORDER_ID = i.ID,
ORDER_NUM = i.ORDER_NUM,
SHIPPING_ACCOUNT_ID = i.ORDERS_ACCOUNTS.Where(b => b.ORDER_ID == i.ID && b.ACCOUNT_ROLE == "Ship To").First().ACCOUNT_ID,
SHIPPING_ACCOUNT_NAME = i.ORDERS_ACCOUNTS.Where(b => b.ORDER_ID == i.ID && b.ACCOUNT_ROLE == "Ship To").First().ACCOUNT.NAME,
SHIPPING_CONTACT_ID = i.ORDERS_CONTACTS.Where(b => b.ORDER_ID == i.ID && b.CONTACT_ROLE == "Ship To").First().CONTACT_ID,
SHIPPING_CONTACT_NAME = i.ORDERS_CONTACTS.Where(b => b.ORDER_ID == i.ID && b.CONTACT_ROLE == "Ship To")
.Select(b => new { SHIPPING_CONTACT_NAME = (b.CONTACT.FIRST_NAME + ' ' + b.CONTACT.LAST_NAME) }).First().SHIPPING_CONTACT_NAME,
NAME = i.NAME
}).DefaultIfEmpty(lOrdersGridList).ToList<OrdersGridList>();
grdMain.DataSource = lOrdersForContact.ToDataTable().DefaultView; //ToDataTable extension function converts the List Object to a datatable.
If I run the Code without AsParallel the code executes with no problem however, once I add AsParallel I receive the following error:
Also just in case you wanted to see this is the class that I am declaring as new for the Select Above:
public class OrdersGridList : EntityObject
{
public string ORDER_NUM { get; set; }
public Guid ORDER_ID { get; set; }
public Guid SHIPPING_ACCOUNT_ID { get; set; }
public string SHIPPING_ACCOUNT_NAME { get; set; }
public Guid SHIPPING_CONTACT_ID { get; set; }
public string SHIPPING_CONTACT_NAME { get; set; }
public string NAME { get; set; }
}
If I remove all of the relationships that are used to retrieve data in the select I don't receive any errors:
var lOrdersForContact = new BaseRepository<ORDER>()
.Find(i => i.ORDERS_CONTACTS.Where(b => b.CONTACT_ID == gId).Count() > 0).AsParallel()
.Select(i =>
new OrdersGridList
{
ORDER_ID = i.ID,
ORDER_NUM = i.ORDER_NUM,
//SHIPPING_ACCOUNT_ID = i.ORDERS_ACCOUNTS.Where(b => b.ORDER_ID == i.ID && b.ACCOUNT_ROLE == "Ship To").First().ACCOUNT_ID,
//SHIPPING_ACCOUNT_NAME = i.ORDERS_ACCOUNTS.Where(b => b.ORDER_ID == i.ID && b.ACCOUNT_ROLE == "Ship To").First().ACCOUNT.NAME,
//SHIPPING_CONTACT_ID = i.ORDERS_CONTACTS.Where(b => b.ORDER_ID == i.ID && b.CONTACT_ROLE == "Ship To").First().CONTACT_ID,
//SHIPPING_CONTACT_NAME = i.ORDERS_CONTACTS.Where(b => b.ORDER_ID == i.ID && b.CONTACT_ROLE == "Ship To")
// .Select(b => new { SHIPPING_CONTACT_NAME = (b.CONTACT.FIRST_NAME + ' ' + b.CONTACT.LAST_NAME) }).First().SHIPPING_CONTACT_NAME,
NAME = i.NAME
}).DefaultIfEmpty(lOrdersGridList).ToList<OrdersGridList>();
I would be more than happy to give more information if required. Any help you can provide on using PLinq I would appreciate it.
To me it appears like the BaseRepository class creates some kind of LINQ to Entities query using the Find and Select parameters.
Using AsParellel is made for LINQ to Objects, where your code actually evaluates the expressions you pass. Other LINQ dialects, including LINQ to Entities, translate it into a different query language like SQL. The SQL server may do reasonable parallelisation itself.