I have seen here
How to insert an IEnumerable<T> collection with dapper-dot-net
how dapper is able to handle IEnumerable as an input param and dispatch multiple commands for each member of the collection.
In my case I have an IEnumerable<int> Categories and a int SurveyId and I want to insert this one-to-many relationship into a separate mapping table called SurveyCategories
Is there a LINQ extension that I could use to concatenate these Categories with the same SurveyId, similar to .Concat()?
Or should I loop through the collection and build up a new list of objects with SurveyId and CategoryId properties?
You could do one insert for the surveys, and then insert all the survey categories at once using the following linq query as the parameter:
var allSurveyCategories = surveys.SelectMany(s =>
s.Categories.Select(c => new{SurveyId = s.SurveyId, CategoryId = c}));
Here is what I have done so far, I changed the categories slightly to an int array called CategoryIds just due to how its being used in my system, but I could have done survey.Categories.Select(c => c.Id).Zip(...
// insert using source data from form post matching a ISurvey interface, where .Id will be 0
var surveyId = conn.Query<int>("INSERT ... " +
"SELECT CAST(SCOPE_IDENTITY() AS INT)")
.First();
if(source.CategoryIds != null && source.CategoryIds.Count() > 0) {
var surveyCategories = source.CategoryIds
.Zip(
Enumerable.Repeat<int>(
surveyId,
source.CategoryIds.Count()
),
(c, s) => new { SurveyID = s, CategoryID = c }
);
conn.Execute(#"INSERT INTO [SurveyCategories] " +
"VALUES (#SurveyID, #CategoryID)",
surveyCategories);
}
UPDATE:
Here is my new approach using SelectMany based on Eren's answer, the use of Enumerable.Repeat(..) is a bit of a hack but this is the only way I have been able to do the same thing so far.
...
var surveyCategories = source.CategoryIds.SelectMany(
s => Enumerable.Repeat(surveyId, 1),
(c, s) => new { SurveyID = s, CategoryID = c });
...
Related
I'm currently using Entity Framework for my db access but want to have a look at Dapper. I have classes like this:
public class Course{
public string Title{get;set;}
public IList<Location> Locations {get;set;}
...
}
public class Location{
public string Name {get;set;}
...
}
So one course can be taught at several locations. Entity Framework does the mapping for me so my Course object is populated with a list of locations. How would I go about this with Dapper, is it even possible or do I have to do it in several query steps?
Alternatively, you can use one query with a lookup:
var lookup = new Dictionary<int, Course>();
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course))
lookup.Add(c.Id, course = c);
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(l); /* Add locations to course */
return course;
}).AsQueryable();
var resultList = lookup.Values;
See here https://www.tritac.com/blog/dappernet-by-example/
Dapper is not a full blown ORM it does not handle magic generation of queries and such.
For your particular example the following would probably work:
Grab the courses:
var courses = cnn.Query<Course>("select * from Courses where Category = 1 Order by CreationDate");
Grab the relevant mapping:
var mappings = cnn.Query<CourseLocation>(
"select * from CourseLocations where CourseId in #Ids",
new {Ids = courses.Select(c => c.Id).Distinct()});
Grab the relevant locations
var locations = cnn.Query<Location>(
"select * from Locations where Id in #Ids",
new {Ids = mappings.Select(m => m.LocationId).Distinct()}
);
Map it all up
Leaving this to the reader, you create a few maps and iterate through your courses populating with the locations.
Caveat the in trick will work if you have less than 2100 lookups (Sql Server), if you have more you probably want to amend the query to select * from CourseLocations where CourseId in (select Id from Courses ... ) if that is the case you may as well yank all the results in one go using QueryMultiple
No need for lookup Dictionary
var coursesWithLocations =
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (course, location) => {
course.Locations = course.Locations ?? new List<Location>();
course.Locations.Add(location);
return course;
}).AsQueryable();
I know I'm really late to this, but there is another option. You can use QueryMultiple here. Something like this:
var results = cnn.QueryMultiple(#"
SELECT *
FROM Courses
WHERE Category = 1
ORDER BY CreationDate
;
SELECT A.*
,B.CourseId
FROM Locations A
INNER JOIN CourseLocations B
ON A.LocationId = B.LocationId
INNER JOIN Course C
ON B.CourseId = B.CourseId
AND C.Category = 1
");
var courses = results.Read<Course>();
var locations = results.Read<Location>(); //(Location will have that extra CourseId on it for the next part)
foreach (var course in courses) {
course.Locations = locations.Where(a => a.CourseId == course.CourseId).ToList();
}
Sorry to be late to the party (like always). For me, it's easier to use a Dictionary, like Jeroen K did, in terms of performance and readability. Also, to avoid header multiplication across locations, I use Distinct() to remove potential dups:
string query = #"SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id";
using (SqlConnection conn = DB.getConnection())
{
conn.Open();
var courseDictionary = new Dictionary<Guid, Course>();
var list = conn.Query<Course, Location, Course>(
query,
(course, location) =>
{
if (!courseDictionary.TryGetValue(course.Id, out Course courseEntry))
{
courseEntry = course;
courseEntry.Locations = courseEntry.Locations ?? new List<Location>();
courseDictionary.Add(courseEntry.Id, courseEntry);
}
courseEntry.Locations.Add(location);
return courseEntry;
},
splitOn: "Id")
.Distinct()
.ToList();
return list;
}
Something is missing. If you do not specify each field from Locations in the SQL query, the object Location cannot be filled. Take a look:
var lookup = new Dictionary<int, Course>()
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.Name, l.otherField, l.secondField
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course)) {
lookup.Add(c.Id, course = c);
}
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(a);
return course;
},
).AsQueryable();
var resultList = lookup.Values;
Using l.* in the query, I had the list of locations but without data.
Not sure if anybody needs it, but I have dynamic version of it without Model for quick & flexible coding.
var lookup = new Dictionary<int, dynamic>();
conn.Query<dynamic, dynamic, dynamic>(#"
SELECT A.*, B.*
FROM Client A
INNER JOIN Instance B ON A.ClientID = B.ClientID
", (A, B) => {
// If dict has no key, allocate new obj
// with another level of array
if (!lookup.ContainsKey(A.ClientID)) {
lookup[A.ClientID] = new {
ClientID = A.ClientID,
ClientName = A.Name,
Instances = new List<dynamic>()
};
}
// Add each instance
lookup[A.ClientID].Instances.Add(new {
InstanceName = B.Name,
BaseURL = B.BaseURL,
WebAppPath = B.WebAppPath
});
return lookup[A.ClientID];
}, splitOn: "ClientID,InstanceID").AsQueryable();
var resultList = lookup.Values;
return resultList;
There is another approach using the JSON result. Even though the accepted answer and others are well explained, I just thought about an another approach to get the result.
Create a stored procedure or a select qry to return the result in json format. then Deserialize the the result object to required class format. please go through the sample code.
using (var db = connection.OpenConnection())
{
var results = await db.QueryAsync("your_sp_name",..);
var result = results.FirstOrDefault();
string Json = result?.your_result_json_row;
if (!string.IsNullOrEmpty(Json))
{
List<Course> Courses= JsonConvert.DeserializeObject<List<Course>>(Json);
}
//map to your custom class and dto then return the result
}
This is an another thought process. Please review the same.
I have a situation where I have a LINQ query. It has two joins (one to many) but it is bringing back all of the columns in the joined tables. I'm not sure how to create the LINQ query to only being back a few fields from the joined tables.
var data = from mc in ctx.MembershipChapters
where mc.PartitionKey == controllerStateManager.PartitionKey && mc.MembershipId == membershipId
join prd in ctx.Products on mc.ProductId
equals prd.Id into prods
from prd in prods.DefaultIfEmpty()
join oli in ctx.OrderLineItems on mc.OrderLineItemId equals oli.OrderLineItemId into olis
from oli in olis.DefaultIfEmpty()
select new
{
MembershipName = mc.Membership.Name,
Products = prods.Select(p => new {
ProductName = p.Name, ProductId = p.Id }),
OrderLineItems = olis.Select(o => new { OrderLineItemName = o.Description, OrderLineItemId = o.OrderLineItemId })
};
controllerStateManager.Data = data.ToList();
This does not work...I get an error: "o" is not in scope.
Basically the output should follow this:
MembershipChapter
---> OrderLineItems
----------> Products
I'm new to LINQ and I have been struggling on this for far too long.
If you have a one-to-many relationship, and you want the "one" items, each one with its zero or more subitems, like Schools with their zero or more Students; Customers with their zero or more Orders, or, as in your case: MembershipChapters with their OrderLineItems, consider to use one of the overloads of Queryable.GroupJoin.
If you start on the "many" side, and you want each item with its one parent item, so you want the Student with the School he attends, or the Order with the one and only Customer who placed the order, use one of the overloads of Queryable.Join.
I almost always use the overload that has a parameter resultSelector, so you can exactly define what should be in the result.
Requirement: given tables MembershipChapters, OrderLineItems and Products. There is a one-to-many relationship between MembershipChapters and OrderLineItems. Every MembershipChapter has zero or more OrderLineItems, every OrderLineItem belongs to exactly one MembershipChapter, namely the MembershipChapter that the foreign key refers to. There is a similar one to many relation between OrderLineItems and Products. Give me all (or some) MembershipChapters, each MembershipChapter with its zero or more OrderlineItems, and each OrderLineItem with its zero or more Products.
var result = dbContext.MemberShipChapters
.Where(membershipChapter => ...) // only if you don't want all MembershipChapters
.GroupJoin(dbContext.OrderLineItems,
membershipChapter => membershipChapter.Id, // from every membershipChapter get the primary key
orderlineItem => orderLineItem.MembershipChapterId, // from every OrderlineItem get the foreign key
// parameter resultSelector: from every MembershipChapter with its zero or more
// OrderLineItems, make one new:
(membershipChapter, orderLineItemsOfThisMembershipChapter) => new
{
// Select only the membershipChapter properties that you plan to use
Id = membershipChapter.Id,
Name = membershipChapter.Name,
...
// The zero or more OrderLineItems of this membershipChapter
OrderLineItems = orderLineItemsOfThisMembershipChapter
.Select(orderLineItem => new
{
// Select only the OrderLineItems that you plan to use:
Id = orderLineItem.Id,
...
// not needed, you already know the value
// MembershipChapterId = orderLineItem.MembershipChapterId,
})
.ToList(),
});
This is fairly straightforward. However, if you want to GroupJoin three tables, then this looks horrible, although it is doable.
Another method that looks simpler:
var result = dbContext.MemberShipChapters
.Where(membershipChapter => ...)
.Select((membershipChapter => new
{
Id = membershipChapter.Id,
Name = membershipChapter.Name,
...
OrderLineItems = dbContext.OrderLineItems
// keep only the OrderLineItems with a foreign key referring to this MembershipChapter
.Where(orderLineItem => orderLineItem.MemberShipChapterId == membershipChapter.Id)
.Select(orderLineItem => new
{
Id = orderLineItem.Id,
...
// do the same with the third table
Products = dbContext.Products
.Where(product => product.OrderLineItemId == orderLineItem.Id)
.Select(product => new
{
Id = product.Id,
Price = product.Price,
...
})
.ToList(),
})
.ToList(),
});
It is a little hard to read, but if the domain is linked correctly then I think you just want to end up with a query like this:
from ol in ctx.OrderLines where
ol.MembershipChapter.PartitionKey == controllerStateManager.PartitionKey
select new {ol.Whatever, ol.Product.Whatever};
I have two tables, Table1 and Table2. Table1 has 1 to many relationship with Table2.
Table1 has ListId, ListName
Table2 has FirstName, LastName, Phone, ListId, CustomField1, CustomField2
What I want to retrieve is All rows from Table1 but only retrieve CustomField2 value of Table2 for only 1st row.
The existing query I have below is retrieving me all rows from Table2 which I do not want.
var result = _db.Lists
.Join(_db.ListUsers, c => c.ListID, d => d.ListID, (c, d) => new { c, d });
My final resultset need to look like this
Table1.ListId, Table1.ListName, Table2.CustomField2
1, first list, "abc"
2, second list, "def"
This should do the trick, get only the first record on the right table:
from i in _db.Lists
let p = _db.ListUsers.Where(p2 => i.ListID == p2.ListID).FirstOrDefault()
select new
{
ListID = i.ListID,
ListName = i.ListName,
CustomField2 = p.CustomField2
}
With lambda expression, it would be:
_db.Lists
.Select (
i =>
new
{
i = i,
p = _db.ListUsers
.Where (p2 => i.ListID == p2.ListID))
.Take(1)
.FirstOrDefault()
})
.Select (
results =>
new
{
ListID= results.i.ListID,
ListName = results.i.ListName,
CustomField2 = results.p.CustomField2
}
)
From what I read, the result you want to achieve is retrieve a list of results contains All columns from Table1 & 1 column from Table2 (Not rows).
There are few ways to do this. I would use create a DTO class to retrieve my results. And in the select , you would need to specifically list out the item in your select result.
For example,
Create a DTO class
public class DTOListResult
{
public string XXX {get; set;}
...
}
Then in your result, you can write in this way.
var result = (from a in _db.Lists select new DTOListResult { XXX = _db.table2.ID,
xxx = a.ID,
XXX = a.XX});
In oracle I can do the following query:
SELECT *
FROM Tabl Tabb
WHERE (tabb.Col1, tabb.Col2) IN ( (1,2), (3,4))
Consider I 've following entity:
public class Tabb
{
public int Col1 {get; set; }
public int Col2 {get; set; }
// other props
}
and criteria class
public class Search
{
public int Col1 {get; set; }
public int Col2 {get; set; }
}
I need to write:
public IEnumerable<Tabb> Select(IEnumerable<Search> s)
{
var queryable = this.context.Tabbs;
return queryable.Where(\* some *\).ToList();
}
How can I select entities, that search collection contain instance of search that has the same value of Col1 and Col2?
EDIT:
var result = from x in entity
join y in entity2
on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
It doesn't work (As I expected) - in may case entity2 is not a entity table, it is static collection, so EF throws exception (sth like: cannot find mapping layer to type Search[]);
There's a few ways, which all have pros and cons, and are sometimes a little bit tricky...
Solution 1
You enumerate the ef part first (of course, depending on the size of your data, this might be a very bad idea)
Solution 2
You concatenate your fields with an element you're sure (hum) you won't find in your fields, and use a Contains on concatenated EF data.
var joinedCollection =entity2.Select(m => m.field1 + "~" + m.field2);
var result = entity.Where(m => joinedCollection.Contains(m.field1 + "~" + m.field2));
of course, this would be a little bit more complicated if field1 and field2 are not string, you'll have to use something like that
SqlFunctions.StringConvert((double)m.field1) + "~" + //etc.
Solution 3
you do this in two step, assuming you will have "not too much result" with a partial match (on only one field)
var field1Collection = joinedCollection.Select(m => m.field1);
var result = entity.Where(m => joinedCollection.Contains(m.field1)).ToList();
then you make the "complete join" on the two enumerated lists...
Solution 4
use a stored procedure / generated raw sql...
Just understood the problem better. You want all rows where the columns match, may be this will help:
myDBTable.Where(x =>
myStaticCollection.Any(y => y.Col2 == x.Col2) &&
myStaticCollection.Any(y => y.Col1 == x.Col1))
.ToList()
.Select(x => new Search { Col1 = x.Col1, Col2 = x.Col2 });
This is saying, I want each row where any Col2 in my static collection matches this database Col2 AND where any Col1 matches this database Col1
this.context.Searches.Join(
this.context.Tabbs,
s => s.Col2,
t => t.Col2,
(search, tab) => new {
search,
tab
});
This will bring back IEnumerable<'a> containing a search and a tab
This guy is doing something similar LINK
var result = from x in entity
join y in entity2
on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
Once you have your result then you want to enumerate that to make sure you're hitting the database and getting all your values back. Once they're in memory, then you can project them into objects.
result.ToList().Select(a => new MyEntity { MyProperty = a.Property });
How can I do this SQL query with Entity Framework?
SELECT DISTINCT NAME FROM TestAddresses
Using lambda expression..
var result = EFContext.TestAddresses.Select(m => m.Name).Distinct();
Another variation using where,
var result = EFContext.TestAddresses
.Where(a => a.age > 10)//if you have any condition
.Select(m => m.name).Distinct();
Another variation using sql like syntax
var result = (from recordset
in EFContext.TestAddresses
.where(a => a.city = 'NY')//if you have any condition
.select new
{
recordset.name
}).Distinct();
Try this:
var results = (from ta in context.TestAddresses
select ta.Name).Distinct();
This will give you an IEnumerable<string> - you can call .ToList() on it to get a List<string>.
The way that #alliswell showed is completely valid, and there's another way! :)
var result = EFContext.TestAddresses
.GroupBy(ta => ta.Name)
.Select(ta => ta.Key);
I hope it'll be useful to someone.
DBContext.TestAddresses.Select(m => m.NAME).Distinct();
if you have multiple column do like this:
DBContext.TestAddresses.Select(m => new {m.NAME, m.ID}).Distinct();
In this example no duplicate CategoryId and no CategoryName i hope this will help you
Entity-Framework Select Distinct Name:
Suppose if you are using Views in which you are using multiple tables and you want to apply distinct in that case first you have to store value in variable & then you can apply Distinct on that variable like this one....
public List<Item_Img_Sal_VIEW> GetItemDescription(int ItemNo)
{
var Result= db.Item_Img_Sal_VIEW.Where(p => p.ItemID == ItemNo).ToList();
return Result.Distinct().ToList();
}
Or you can try this Simple Example
Public Function GetUniqueLocation() As List(Of Integer)
Return db.LoginUsers.Select(Function(p) p.LocID).Distinct().ToList()
End Function
use Select().Distinct()
for example
DBContext db = new DBContext();
var data= db.User_Food_UserIntakeFood .Select( ).Distinct();
In order to avoid ORDER BY items must appear in the select list if SELECT DISTINCT error, the best should be
var results = (
from ta in DBContext.TestAddresses
select ta.Name
)
.Distinct()
.OrderBy( x => 1);
Entity-Framework Select Distinct Name:
Suppose if you are want every first data of particular column of each group ;
var data = objDb.TableName.GroupBy(dt => dt.ColumnName).Select(dt => new { dt.Key }).ToList();
foreach (var item in data)
{
var data2= objDb.TableName.Where(dt=>dt.ColumnName==item.Key).Select(dt=>new {dt.SelectYourColumn}).Distinct().FirstOrDefault();
//Eg.
{
ListBox1.Items.Add(data2.ColumnName);
}
}