It's a master detail scenario, where each TABLE1 has multiple rows from TABLE2 assigned and I want to do something lihe this:
From a In TABLE1
Group Join c In TABLE2 On c.ID2 Equals a.ID Into Group
Select New ViewModel1 With {
.COLUMN1 = a.COLUMN1,
.COLUMN2 = a.COLUMN2,
.SUBTABLE = New ViewModel2 With {
.SUBCOLUMN1 = c.SUBCOLUMN1,
.SUBCOLUMN2 = c.SUBCOLUMN2,
}
}
Is this somehow possible?
Do you mean this:
var foobars = from foo in foolist
join bar in barlist on foo.Fooo equals bar.FooBar into t
select new
{
foo.Baar,
barbar = from bar in t
select new { bar.FooBar, bar.BarFoo }
};
That would be (approximately) how you do the query you described.
Sorry, I had to reread the question a few times to get that it was being mapped to the first element.
Perhaps the following can help you:
class Program
{
public class A
{
public int ID { get; set; }
public string COLUMN1 { get; set; }
public string COLUMN2 { get; set; }
}
public class B
{
public int ID { get; set; }
public int AID { get; set; }
public string SUBCOLUMN1 { get; set; }
public string SUBCOLUMN2 { get; set; }
}
static void Main(string[] args)
{
var listA = new List<A>{
new A() { ID = 1, COLUMN1="COLUMN11",COLUMN2 = "COLUMN12"},
new A() { ID = 2 ,COLUMN1="COLUMN21",COLUMN2 = "COLUMN22"}
};
var listB = new List<B>()
{
new B(){ID=1,AID = 1 },
new B(){ID=2,AID = 1},
new B(){ID=3,AID = 1},
new B(){ID=4,AID = 2},
new B(){ID=5,AID = 2}
};
//Group Join As Method Chain:
var queryAsMethodChain = listA.GroupJoin(listB, a => a.ID, b => b.AID, (a, t) => new
{
ID = a.ID,
COLUMN1 = a.COLUMN1,
COLUMN2 = a.COLUMN2,
SUBTABLE = t.Select(tt => new { tt.SUBCOLUMN1, tt.SUBCOLUMN2 })
});
//Group Join As Standard Method
var queryAsStandardMethod = from a in listA
join b in listB
on a.ID equals b.AID into t
select new
{
ID = a.ID,
COLUMN1 = a.COLUMN1,
COLUMN2 = a.COLUMN2,
SUBTABLE = t.Select(tt => new { tt.SUBCOLUMN1, tt.SUBCOLUMN2 })
};
}
Related
public class Entity
{
public string Id { get; set; }
public string CreatedBy { get; set; }
public string LastUpdatedBy { get; set; }
[NotMapped]
public string Creator { get; set; }
[NotMapped]
public string Updater { get; set; }
}
public class User
{
public string Name { get; set; }
public string Id { get; set; }
}
I am going to search Entities, sort on Creator/Updater properties(and return UserInfo.Name) with ef core query, any idea?
After hours of researches, refers
How do you perform a left outer join using linq extension methods
Entity Framework Join 3 Tables
There 3 ways in oder(presonal perfer the first than second):
class Program
{
static void Main()
{
using TenantDBContext dbContext = new TenantDBContext("PORT=5432;DATABASE=linqtosql;HOST=xxx.com;PASSWORD=xxx;USER ID=postgres;Pooling=true;Minimum Pool Size=10;Application Name=xxx");
var result = (
from entity in dbContext.Entities
join user in dbContext.Users on entity.CreatedBy equals user.Id into temp1
from ce in temp1.DefaultIfEmpty()
join user1 in dbContext.Users on entity.UpdatedBy equals user1.Id into temp2
from cu in temp2.DefaultIfEmpty()
select new Entity() { Id = entity.Id, CreatedBy = entity.CreatedBy, UpdatedBy = entity.UpdatedBy, Creator = ce.Name, Updater = ce.Name }
).ToList();
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
var result2 = dbContext.Entities
.GroupJoin(dbContext.Users, e => e.CreatedBy, u => u.Id, (e, u) => new { Entity = e, User = u })
.SelectMany(eUser => eUser.User.DefaultIfEmpty(), (e, u) => new Entity() { Id = e.Entity.Id, CreatedBy = e.Entity.CreatedBy, UpdatedBy = e.Entity.UpdatedBy, Creator = u.Name })
.GroupJoin(dbContext.Users, e => e.UpdatedBy, u => u.Id, (e, u) => new { Entity = e, User = u })
.SelectMany(eUser => eUser.User.DefaultIfEmpty(), (e, u) => new Entity() { Id = e.Entity.Id, CreatedBy = e.Entity.CreatedBy, UpdatedBy = e.Entity.UpdatedBy, Creator = e.Entity.Creator, Updater = u.Name }
).ToList();
Console.WriteLine(JsonConvert.SerializeObject(result2, Formatting.Indented));
var result3 = dbContext.Entities
.SelectMany(entity => dbContext.Users.Where(user => entity.CreatedBy == user.Id).DefaultIfEmpty(), (entity, user) => new { Entity = entity, User = user })
.SelectMany(entity => dbContext.Users.Where(user => entity.Entity.UpdatedBy == user.Id).DefaultIfEmpty(), (entity, user) => new Entity { Id = entity.Entity.Id, CreatedBy = entity.Entity.CreatedBy, UpdatedBy = entity.Entity.UpdatedBy, Creator = entity.User.Name, Updater = user.Name })
.ToList();
Console.WriteLine(JsonConvert.SerializeObject(result2, Formatting.Indented));
}
}
I'm newbie to EF, Linq and C# in general, I'm stuck with developing following.
I cannot map data into structure like this:
Id,
Actions [
Action1,
Action2,
Action3
]
I have 2 DTO classes like this:
public class TestDTO
{
public int TestId { get; set; }
public TestDTO2[] Actions { get; set; }
}
and
public class TestDTO2
{
public int TestActionId { get; set; }
public DateTime? StartDate { get; set; }
...
}
I've separated calls to DB into file called BusinessLogic, I'm doing it like this:
public IQueryable<TestDTO> GetNested(Filter filter)
{
var query =
from a in db.Table1.AsQueryable()
select new TestDTO
{
TestId = a.Id,
Actions = (
from b in db.Table2.AsQueryable()
where a.Id == b.TestId
select new TestDTO2
{
TestActionId = b.TestActionId,
StartDate = b.StartDate
}
).ToArray()
};
return query;
}
I'm getting following error:
LINQ to Entities does not recognize the method 'Project.Core.Models.TestDTO2[] ToArrayTestDTO2' method, and this method cannot be translated into a store expression.
You can't perform exactly this query, it is better to make two simple queries and then process their results on client side:
var main = db.Table1.Select(x => new { x.Id, x.Title }).ToList();
var mainIds = main.Select(x => x.Id).ToList();
var actions = db.Table2.Where(x => mainIds.Contains(x.TestId)).Select(x => new
{
x.TestId,
x.TestActionId,
x.StartDate
}).ToList();
var result = main.Select(x => {
var actions = actions.Where(y => y.TestId == x.Id).Select(y => new TestDTO2
{
TestActionId = y.TestActionId,
StartDate = y.StartDate
}).ToArray();
return new TestDTO
{
TestId = x.Id,
Title = x.Title,
Actions = actions.Length == 0 ? null : actions
};
}).ToList();
yes, you can't use any c# method that can't translate a sql in EF.
actually, you need get a list,then covert it to your DTO
db.Table1
.Join(db.Table2,
a => a.Id,
b => b.TestId,
(a, b) => new
{
a.Id,
b
})
.GroupBy(k => k.Id, v => v).ToList()
.Select(a=>new TestDTO
{
TestId = a.Id,
Actions = a.Select(b=>
new TestDTO2
{
TestActionId = b.TestActionId,
StartDate = b.StartDate
}.ToArray()
}).ToList()
I've got two classes
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Item
{
public int Id { get; set; }
public sting Name { get; set; }
public Category Category { get; set; }
}
I have EF Migrations and the following seed:
var instockCategory = new Category() { Name = "InStock" };
var outofStockCategory = new Category() { Name = "OutOfStock" };
context.Items.AddOrUpdate(
d => d.Name,
new Item() { Name = "Item1", Category = instockCategory },
new Item() { Name = "Item2", Category = outofStockCategory },
new Item() { Name = "Item3", Category = outofStockCategory }
);
The line "d => d.Name" makes sure that based on the name of the item, there won't be duplicate records when I reseed the database.
However, the first time I execute this, two categories are created with id 1 and 2. But the second time I run this, 3 new categories are created!
Can I fix this without manually adding every single category first?
You have to use AddOrUpdate for your categories too.
var instockCategory = default(Category);
var outofStockCategory = default(Category);
context.Set<Category>().AddOrUpdate(
c => c.Name,
instockCategory = new Category() { Name = "InStock" },
outofStockCategory = new Category() { Name = "OutOfStock" }
);
context.Items.AddOrUpdate(
d => d.Name,
new Item() { Name = "Item1", Category = instockCategory },
new Item() { Name = "Item2", Category = outofStockCategory },
new Item() { Name = "Item3", Category = outofStockCategory }
);
An explicit DbSet on your Context class is not necessary.
public class Context : DbContext
{
public DbSet<Item> Items { get; set; }
}
I wrote this code
class Student {
public Student() {
this.Courses = new HashSet<Course>();
}
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
class Course {
public Course() {
this.Students = new HashSet<Student>();
}
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
class SchoolDBContext : DbContext {
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public SchoolDBContext()
: base("SchoolDbConnectionString") {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
My Seed method looks like
protected override void Seed(ConsoleApplication6.SchoolDBContext context)
{
Course c1 = new Course { ID = 1, Name = "Chemistry" };
Course c2 = new Course { ID = 2, Name = "Maths" };
Course[] courses = new Course[2];
courses[0] = c1;
courses[1] = c2;
Student s1 = new Student { ID = 1, Name = "Student 1" };
Student s2 = new Student { ID = 1, Name = "Student 2" };
Student[] students = new Student[2];
students[0] = s1;
students[1] = s2;
c1.Students = students;
c2.Students = students;
context.Courses.AddOrUpdate(course => new { course.ID }, courses);
}
After I run Update-database I can see that the database has 3 tables. Student and Course tables have 2 rows each and StudentCourse table has 4 rows. So I guess all data is seeded correctly.
Now when I write this code in my main method
static void Main(string[] args) {
SchoolDBContext c = new SchoolDBContext();
c.Configuration.LazyLoadingEnabled = true;
Student s = (from student in c.Students where student.ID == 1 select student).FirstOrDefault();
List<Course> courses = s.Courses.ToList();
Console.WriteLine(s.Name);
Console.WriteLine(courses.Count);
foreach (Course co in courses) {
Console.WriteLine(co.Name);
}
}
it prints the name of the student correctly... but prints 0 for courses.Count and the forloop on courses List returns nothing.
why am I not able to get the courses for student 1?
I also tried the other way round
static void Main(string[] args) {
SchoolDBContext c = new SchoolDBContext();
c.Configuration.LazyLoadingEnabled = true;
Course co = (from course in c.Courses where course.ID == 1 select course).FirstOrDefault();
Console.WriteLine(co.Name);
List<Student> students = co.Students.ToList();
foreach (Student s in students) {
Console.WriteLine(s.Name);
}
}
here also the name of the course is returned correctly... but it doesn't print any of the students.
So entity framework is not able to walk to the related table and fetch rows from there.
What's going on?
Found the answer myself.
static void Main(string[] args) {
SchoolDBContext c = new SchoolDBContext();
c.Configuration.LazyLoadingEnabled = false;
Course co = (from course in c.Courses.Include("Students") where course.ID == 1 select course).FirstOrDefault();
Console.WriteLine(co.Name);
List<Student> students = co.Students.ToList();
foreach (Student s in students) {
Console.WriteLine(s.Name);
}
}
putting it here so that it benefits someone....
But according to me my original code should have worked (lazy loading) so I don't understand why my original code which was doing lazy loading did not work.
OK. here is the solution with lazy loading
static void Main(string[] args) {
SchoolDBContext context = new SchoolDBContext();
context.Configuration.LazyLoadingEnabled = true;
Course co = (from course in context.Courses where course.ID == 1 select course).FirstOrDefault();
//Course co = (from course in c.Courses where course.ID == 1 select course).FirstOrDefault();
Console.WriteLine(co.Name);
foreach (Student s in context.Entry(co).Collection(c => c.Students).Query()) {
Console.WriteLine(s.Name);
}
}
This was really useful
http://msdn.microsoft.com/en-us/data/jj574232#lazy
I have one action in my controller :
public ActionResult GiftVochure()
{
if (Request.QueryString["gc"] != "")
{
var obj = from b in context.GiftCards
join cus in context.Customers on b.CustomerID equals cus.ID
where b.ID == int.Parse(Request.QueryString["gc"])
select new
{
b.ID,
b.Date,
b.CardNo,
b.Price,
CustomerName = cus.FirstName + " " + cus.LastName
};
return View(obj.ToList());
}
return View();
}
And I want to take "obj" to loop and display in the GiftVochure Views, Does any one know, how to do this?
Thanks.
You should start by defining a model type to replace the anonymous type projected by the query.
public class CardInfo
{
int ID { get; set; }
DateTime Date { get; set; }
int CardNo { get; set; }
double Price { get; set; }
string CustomerName { get; set; }
}
Modifying your action method:
var obj = from b in context.GiftCards
join cus in context.Customers on b.CustomerID equals cus.ID
where b.ID == int.Parse(Request.QueryString["gc"])
select new CardInfo
{
ID = b.ID,
Date = b.Date,
CardNo = b.CardNo,
Price = b.Price,
CustomerName = cus.FirstName + " " + cus.LastName
};
return View(obj);
Then you should strongly type your GiftVochure view to this type's sequence.
#model IEnumerable<CardInfo>
In the end you can iterate the items in your view.
#foreach(CardInfo current in Model) {
//display info
}
You should probably move your markup for displaying a single object to a partial view. Then render it in your loop.