I have my class structure as follows
public class Email
{
public string Subject {get;set;}
public string Message {get;set;}
public Contact Sender {get;set;}
public string SenderEmail {get;set;}
}
public class Contact
{
public string Email {get;set;}
public string Name {get;set;}
}
and I run my Linq query in two parts
First i select all the emails.
var query = from msg in context.Email
select msg;
Then i assign the Contact details to the Email Class
List<Email> outputList = new List<Email>();
foreach (var item in query.ToList())
{
var q = from contact in context.Contact
where contact.Email = item.SenderEmail
select contact;
item.Sender = q.SingleOrDefault();
outputList.Add(item);
}
return outputList;
Is there anyway i can run a join query and simply output the List without having to run multiple queries
I think this would do the trick (warning: untested code):
var qry = from email in context.Email
join contact in context.Contact
on email.SenderEmail equals contact.Email
into contacts
select new { eml = email, sender = contacts.FirstOrDefault() };
var items = qry.ToList();
foreach (var item in items)
{
item.eml.Sender = item.sender;
outputList.Add(item.eml);
}
return outputList;
I suspect this should work:
var query = from msg in context.Email
join contact in context.Contact
on msg.SenderEmail equals contact.Email
into contacts
select new { msg, contacts };
var list = query.ToList();
foreach (var pair in list)
{
pair.msg.Sender = pair.contacts.FirstOrDefault();
}
var messages = list.Select(pair => pair.msg);
This uses a group join. You haven't said which LINQ provider you're using, but I expect it should work for most providers...
Related
I am currently mapping the database query output to a class object in the following. Is there anyway I can directly map it with out using foreach loop?
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Address { get; set; }
}
string select = "SELECT Id, Name, Address FROM emp where Id = 100";
string dbConnection="Server = TestServer; userid = XXXX; password = YYYYY; database = TestDB; port = 1234";
Person person = new Person();
using (var connection = new MySqlConnection(dbConnection))
{
var v = await connection.QueryAsync<Person>(select);
if (v != null)
{
foreach (var res in v)
{
person.Id = res.Id;
person.Name = res.Name;
person.Address = res.Address;
}
}
}
The code doesn't make logical sense as you are looping over a list of people & overwriting a single Person object with every new person being enumerated.
That aside, the result of await connection.QueryAsync<Person>(select); will be of type IEnumerable<Person>.
var v = await connection.QueryAsync<Person>(select);
is equal to:
IEnumerable<Person> = await connection.QueryAsync<Person>(select);
You already have a collection of Person objects mapped.
I have the following method to return the data of two entities.
public List<Object> GetDados()
{
var sendFilter = new Filter<MessageSent>();
//employeeFilter.Add(x => x.Name, name);
sendFilter.Add(x => x.MessageSentSeq, ID_GROUP_SEND);
// You can add more filters
MessageSentService svc = new MessageSentService();
var messages = svc.Find(sendFilter).ToList();
var employees = new EmployeeService().GetAll();
var query =
from employee in employees
join message in messages
on employee.EmployeeId equals message.EmployeeId
select new
{
MessageSentId = message.MessageSentId,
//EmployeeId = message.EmployeeId,
//MessageSentSeq = message.MessageSentSeq,
Name = employee.Name,
Surname = employee.Surname,
Mobile = employee.Mobile,
Email = employee.Email,
Status = "N"
};
return query.ToList<Object>();
}
Call
ILog log = LogManager.GetLogger(typeof(Form));
List<Object> Send;
Send = GetDados();
gvSent.DataSource = Send;
When doing send I would like to update the record that is inside object var query that in turn populates my grid
I need to set the status field with S at the end of everything I updated my database with the items that were sent correctly.
Because your function returns a List<Object>, you'll be unable to modify those properties without using Reflection or some other drastic measure. If you plan on this data being modifiable, you'll need to make sure the data you're returning is typed. First define a simple class:
class Dado
{
public string MessageSentId { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public bool Mobile { get; set; }
public string Email { get; set; }
public string Status { get; set; }
}
...then modify your function to return instances of this type:
public List<Dado> GetDados()
{
var sendFilter = new Filter<MessageSent>();
//employeeFilter.Add(x => x.Name, name);
sendFilter.Add(x => x.MessageSentSeq, ID_GROUP_SEND);
// You can add more filters
MessageSentService svc = new MessageSentService();
var messages = svc.Find(sendFilter).ToList();
var employees = new EmployeeService().GetAll();
return (
from employee in employees
join message in messages
on employee.EmployeeId equals message.EmployeeId
select new Dado
{
MessageSentId = message.MessageSentId,
//EmployeeId = message.EmployeeId,
//MessageSentSeq = message.MessageSentSeq,
Name = employee.Name,
Surname = employee.Surname,
Mobile = employee.Mobile,
Email = employee.Email,
Status = "N"
}
).ToList();
}
Now, in your code after populating the grid can do something like:
foreach (var dado in Send) {
dado.Status = "S";
}
I have two tables:
Employee: Id, Name, DepartmentId
Department: Id, Name
Employee.cs:
public int Id {get;set;}
public string Name {get;set;}
public int DepartmentId {get;set;}
Department.cs:
public int Id {get;set;}
public string Name {get;set;}
ViewModel: EmployeeDepartmentVM:
public Department department {get;set;}
public List<Employee> employees {get;set;}
To Join these two tables I have written this code:
SELECT E.* , D.Id as DId , D.Name as DName
from [Employee] as E
LEFT OUTER JOIN [Department] as D
ON E.DepartmentId = D.Id
where D.Id = 1
How do I get EmployeeDepartmentVM type from the above query?
I know if I write a model like my problem will be solved:
public int Id {get;set;}
public string Name {get;set;}
public int DepartmentId {get;set;}
public int DId {get;set;}
public string Name {get;set;}
But I don't want to write extra model. Simply want bind query data into EmployeeDepartmentVM type.
I really don't see what's the challenge. The EmployeeDepartmentVM definition implies that you need to group the result set by the Department. Assuming the result set is unordered, it can be achieved by simply maintaining a dictionary for locating the view models of the already added departments during the read.
Which leads to something like this:
static List<EmployeeDepartmentVM> GetEmployeeDepartmentVMList(DbCommand command)
{
var resultById = new Dictionary<int, EmployeeDepartmentVM>();
using (var reader = command.ExecuteReader())
{
var employeeIdCol = reader.GetOrdinal("Id");
var employeeNameCol = reader.GetOrdinal("Name");
var departmentIdCol = reader.GetOrdinal("DId");
var departmentNameCol = reader.GetOrdinal("DName");
while (reader.Read())
{
var departmentId = reader.GetInt32(departmentIdCol);
EmployeeDepartmentVM result;
if (!resultById.TryGetValue(departmentId, out result))
{
result = new EmployeeDepartmentVM
{
department = new Department(),
employees = new List<Employee>()
};
result.department.Id = departmentId;
result.department.Name = reader.GetString(departmentNameCol);
resultById.Add(departmentId, result);
}
var employee = new Employee();
employee.Id = reader.GetInt32(employeeIdCol);
employee.Name = reader.GetString(employeeNameCol);
employee.DepartmentId = departmentId;
result.employees.Add(employee);
}
}
return resultById.Values.ToList();
}
Some things to note. The way written, your SQL query implies that Department related fields can be null (LEFT OUTER JOIN). However, the WHERE clause and also the Employee model (DepartmentId field non nullable) implies that it cannot happen. If the intent is to include the departments with no employees, then better change the join to RIGHT OUTER and use something like this:
// ...
if (reader.IsDBNull(employeeIdCol)) continue;
var employee = new Employee();
// ...
EDIT: For completeness, here is another approach. It's similar to the way EF materializes similar queries and does not need temporary dictionary, but requires the input set to be ordered by the PK of the master table, so you need to add
ORDER BY D.Id
at the end of your SQL. Databases can easily and efficiently provide such ordering, and the benefit of this solution is that it allows deferred execution and does not require processing the whole set in order to start returning results. It's not essential if you want to just get a list, but can be useful in other scenarios.
static IEnumerable<EmployeeDepartmentVM> GetEmployeeDepartmentVMs(DbCommand command)
{
using (var reader = command.ExecuteReader())
{
var employeeIdCol = reader.GetOrdinal("Id");
var employeeNameCol = reader.GetOrdinal("Name");
var departmentIdCol = reader.GetOrdinal("DId");
var departmentNameCol = reader.GetOrdinal("DName");
for (bool more = reader.Read(); more;)
{
var result = new EmployeeDepartmentVM
{
department = new Department(),
employees = new List<Employee>()
};
result.department.Id = reader.GetInt32(departmentIdCol);
result.department.Name = reader.GetString(departmentNameCol);
do
{
if (reader.IsDBNull(employeeIdCol)) continue;
var employee = new Employee();
employee.Id = reader.GetInt32(employeeIdCol);
employee.Name = reader.GetString(employeeNameCol);
employee.DepartmentId = result.department.Id;
result.employees.Add(employee);
}
while ((more = reader.Read()) && reader.GetInt32(departmentIdCol) == result.department.Id);
Debug.Assert(!more || reader.GetInt32(departmentIdCol) > result.department.Id); // Sanity check
yield return result;
}
}
}
To get a list as in the first approach, just add ToList() after the call, e.g.
var result = GetEmployeeDepartmentVMs(command).ToList();
I have a Linq query like this:
var result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new
{
s.Stud_Id,
s.FirstName,
c.Cls_Id,
c.Room,
c.Notification
};
repeater.DataSource = result.ToList();
repeater.DataBind();
But in Notification field has content like this: This Room of C Programming Class/NTFF. If binding in Lable Text='<%#DataBinder.Eval(Container.DataItem, "Notification")%>' it will display: This Room of C Programming Class/NTFF.
I want to split this string into 2 string like this:
str1 = This Room of C Programming Class;
str2 = NTFF;
before binding and binding str1 into Lable1 and str2 into Lable2. How can I do this?
You could use something like this: First create a DTO to store the result entities with all the fields plus one extra field to store the the list of notifications.
public class Result
{
public int Stud_Id { get; set; }
...
...
public string Notification { get; set; }
public string[] Notifications { get; set; }
}
List<Result> result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new Result
{
Stud_Id = s.Stud_Id,
...
...
Notification = c.Notification
}).ToList();
result.ForEach(r =>
{
r.Notifications = r.Notification.Split('/');
});
Now you have two strings in Notifications:
Notification[0] = "This Room of C Programming Class";
Notification[1] = "NTFF"
You can now use whichever you want to bind in the Lable.
You can use Split function to get str1 like this:-
var result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new
{
s.Stud_Id,
s.FirstName,
c.Cls_Id,
c.Room,
str1 = c.Notification.Split('/').FirstOrDefault()
};
Then, you can bind it to your Label like this:-
<asp:Lable Text='<%#DataBinder.Eval(Container.DataItem, "str1")%>'><asp:Label/>
Update:
Since you are using Entity Framework, you can't use Split function directly. You need to bring the results in memory. One way is to create a custom class and fill it like this:-
public class Students
{
public int Stud_Id { get; set; }
public string FirstName{ get; set; }
public int Cls_Id{ get; set; }
public string Room{ get; set; }
public string Notification{ get; set; }
public string str1{ get; set; }
public string str2{ get; set; }
}
Then, first fill your custom class with query like this:-
List<Students> students = (from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new Students
{
Stud_Id = s.Stud_Id,
FirstName = s.FirstName,
Cls_Id = c.Cls_Id,
Room = c.Room,
Notification= c.Notification
}).ToList();
Finally, iterate through the result and fill up the str1 & str2 variables like this:-
foreach (Student student in students)
{
string[] Notifications = student.Notification.Split('/');
student.str1 = Notifications.FirstOrDefault();
student.str2 = Notifications.ElementAtOrDefault(1);
}
After this, simply bind your labels with parameters str1 & str2.
Use string.Replace() like below:
<%# ((string)DataBinder.Eval(Container.DataItem, "Notification")).Replace("/NTFF", string.Empty) %>
Please check the syntax first. But should work in this case. Let me know if its not working.
Edit:
Code Behind:
var result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new
{
Id = s.Stud_Id,
FirstName = s.FirstName,
ClassId = c.Cls_Id,
Room = c.Room,
FirstNotification = c.Notification.Split('/')[0],
SecondNotification = c.Notification.Split('/')[1]
};
Then on front end use FirstNotification and SecondNotification properties.
Note: Above code will throw Index out of bound exception when there is no '/' character.
class Program
{
static void Main(string[] args)
{
MyDatabaseEntities entities = new MyDatabaseEntities();
var result = from c in entities.Categories
join p in entities.Products on c.ID equals p.IDCategory
group p by c.Name into g
select new
{
Name = g.Key,
Count = g.Count()
};
Console.WriteLine(result.ToString());
Console.ReadLine();
}
}
How can I extract the values from ths result set so I can work with them?
foreach (var item in result)
{
var name = item.Name;
var count = item.Count;
...
}
This will only work inside the same method where the LINQ query is located, since the compiler will only then know which properties are available in the anonymous object type (new { }) used in your LINQ select.
If you return a LINQ query to a calling method, and you want to access it in the way shown above, you'd have to define an explicit type and use it in your LINQ query:
class NameCountType
{
public string Name { get; set; }
public int Count { get; set; }
}
...
return from ... in ...
...
select new NameCountType
{
Name = ...,
Count = ...,
};
For example:
foreach (var x in result)
{
Console.WriteLine(x.c.Name);
}
var results = (from myRow in ds.Tables[0].AsEnumerable()
where myRow.Field<String>("UserName") == "XXX"
select myRow).Distinct();
foreach (DataRow dr in results)
UserList += " , "+dr[0].ToString();