How to Copy Data From Sql Object to C# Model Property - c#

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();

Related

Fill DTO from two distinct objects

I have the following DTO:
public int IdTableOne { get; set; }
public string ValueTableOne { get; set; }
public int IdTableTwo { get; set; }
public string ValueTableTwo { get; set; }
Also, I have two Models (TableOne & TableTwo) and I fill this models in my repository doing the following code:
return dbContext.TableOne;
At this point everything it's okay. TableOne & TableTwo are populated, but now I want to return the combination of these values into my DTO object (TableOneId is equal to TableTwoId, it's a relationship between both tables) for doing this I'm trying something like this:
public IEnumerable<TableOneAndTwoDTO> GetTableOneAndTwo()
{
List<TableOneAndTwoDTO> combination = new List<TableOneAndTwoDto>();
var t1 = myRepository.GetTableOne();
var t2 = myRepository.GetTableTwo();
var query = from p in t1
select new {
IdTableOne = p.Id,
ValueTableOne = p.Value,
};
foreach (var item in query)
{
combination.Add(new TableOneAndTwoDTO { IdTableOne = item.IdTableOne, ValueTableOne = item.ValueTableOne });
}
}
So my question is, how can I add the TableTwo values to my DTO only when IdTableOne = IdTableTwo.
You can join your table results. Something like this:
var query = from p in t1
join j in t2 on p.IdTableOne equals j.IdTableTwo
select new { p, j };
And then you can add the join values to your DTO using something like this:
foreach (var item in query)
{
combination.Add(new TableOnwAndTwoDTO { IdTableOne = item.p.IdTableOne, IdTableTwo = item.j.IdTableTwo... })
}
Just do a LINQ Join. You can avoid a lot of the ceremony from your original code by putting the whole thing into one query and then calling .ToList() before returning.
public IEnumerable<TableOneAndTwoDTO> GetTableOneAndTwo()
{
var t1 = myRepository.GetTableOne();
var t2 = myRepository.GetTableTwo();
var combination =
from p in t1
join j in t2 on p.IdTableOne equals j.IdTableTwo
select new {
IdTableOne = p.Id,
ValueTableOne = p.Value,
IdTableTwo = j.Id,
ValueTableTwo = j.Value,
};
return combination.ToList();
}

can we call entity framework query with passing table value?

Is any way to pass the table name as a parameter and get the record from db with make generalized function
string table = "tbl_Category";
int Id = Class.getLastId(table);
Class.aspx
public static int getLastId(string table)
{
int lastID = 0;
using (HatnEntities context = new HatnEntities())
{
// Fetch Id of last record from table
var result = (from c in context.tbl_Category.OrderByDescending(u => u.Id) select new { Id = c.Id }).FirstOrDefault();
^
//any way to use table name from parameter value"+table+"
if (result != null)
{
lastID = Convert.ToInt32(result.Id);
}
obj.Id = lastID + 1;
context.tbl_Category.Add(obj);
context.SaveChanges();
}
return status;
}
Please let me know is it possible
You can use Set() which would require that passing your table string parameter, you must get the Type from your assembly.
context.Set<TypeFromTableStringParameter>() ...
This is what you would have to do if you just want to be able to access any table in EF:
public static int getLastId<T>()
where T : PrimaryKey
{
using (HatnEntities context = new HatnEntities())
{
// Fetch Id of last record from table
var result = (from c in context.Set<T>().OrderByDescending(u => u.Id) select new { Id = c.Id }).FirstOrDefault();
var lastID = 0;
if (result != null)
{
lastID = Convert.ToInt32(result.Id);
}
obj.Id = lastID + 1;
context.Set<T>().Add(obj);
context.SaveChanges();
}
// not sure where this comes from?
return status;
}
public abstract class PrimaryKey
{
public int Id { get; set; }
}
All of you Entities would need to inherit (extend) PrimaryKey in order for you to Select, Where or FirstOrDefault (etc. etc.) based on the property Id.

Linq returning different result than SQL

I'm running this Linq query:
var patientList = from p in db.Patients
where p.ClinicId==11
select p.Id;
var patientswithplan = from p in db.Plans
where patientList.Contains(p.PatientId)
select p;
It returns 1030 results.
But when I came up with this query I wrote it in sql first to test it out and this displays 956 results
select id from patients where clinicid=11
and id in(select patientid from plans)
order by id
I thought these queries would be the same, what is the difference, which one is correct?
I have written a little code then you could see the difference yourself
void Main()
{
var Plans = new List<Plan>();
Plans.Add(new Plan() {PatientId = 1, PlanName = "Good Plan"});
Plans.Add(new Plan() {PatientId = 2, PlanName = "Bad Plan"});
var Patients = new List<Patient>();
Patients.Add(new Patient() {ClinicId = 1, Name = "Frank"});
Patients.Add(new Patient() {ClinicId = 2, Name = "Fort"});
// This is your LINQ
var patientList = from p in Patients
where p.ClinicId == 1
select p.ClinicId;
var patientswithplan = from p in Plans
where patientList.Contains(p.PatientId)
select p;
Console.WriteLine(patientswithplan);
// We return a PLAN here
// Result
// IEnumerable<Plan> (1 item)
// PatientId 1
// PlanName Good Plan
// This is the equivalent Linq of your SQL
var myPatient = Patients.Where(
pa => pa.ClinicId == 1 &&
Plans.Any(pl => pl.PatientId == pa.ClinicId)
);
Console.WriteLine(myPatient);
// Look! We return a PATIENT here
// Result
// IEnumerable<Patient> (1 item)
// ClinicId 1
// Name Frank
}
// Define other methods and classes here
class Patient
{
public Patient() {}
public int ClinicId { get; set; }
public string Name { get; set; }
}
class Plan
{
public Plan() {}
public int PatientId { get; set; }
public string PlanName { get; set; }
}
The queries do two different things:
1) The first query is basically first getting a list of patients, and then it's fetching plans (you choose "from p in db.Plans") that have those selected patients in their list of patients.
2) The second query is filtering and fetching patients of given clinic making sure that those patients exist in some plans.
So of course the number of results will be different as you probably have a different number of rows in the patients and plans tables.

Linq query- Collection within a dictonary

I am trying to get result from a dictionary using linq but I am not sure how to do it.
I am trying to write a linq query to get the employee name,salary of the empId=2. Please correct me with the right linq query.
class Program
{
static void Main(string[] args)
{
Dictionary<int, List<Employee>> empDic = new Dictionary<int, List<Employee>>();
var emp = new List<Employee>();
emp.Add(new Employee{EmpID = 1,Name="affsa",Salary=2000}) ;
emp.Add(new Employee { EmpID = 2, Name = "axy",Salary=3000 });
emp.Add(new Employee { EmpID = 3, Name = "xyz",Salary=4000 });
empDic.Add(1,emp);
var selectedEmpDetails = empDic.Values
.Select(r => r.Where(f => f.EmpID ==2))
.ToList();
}
}
public class Employee
{
public string Name { get; set; }
public int EmpID { get; set; }
public int Salary { get; set; }
}
You could try something like this:
var selectedEmpDetails = empDic.SelectMany(x=>x.Value)
.Where(x=>x.EmpId==2)
.Select(x=> new { Name = x.Name, Salary = x.Salary });
Initially we flatten the values of the dictionary in a list of Employee objects.
Then we select the object with the EmpId equals to 2.
Last we select it's name and salary properties.
I would use SelectMany() function in your case so that I get a flattened list of Employee instances. Then try to get the first Employee instance that matches the employee id (if any) with the FirstOrDecault() function.
var selectedEmpDetails = empDic.Values.SelectMany(r => r.Where(f => f.EmpID == 2)).FirstOrDefault();
if(selectedEmpDetails != null)
{
string employeeName = selectedEmpDetails.Name;
int employeeSalary = selectedEmpDetails.Salary;
}

LINQ to Entities does not recognize the method, 2 repositories

I keep getting the error below on my code, and can't understand why it is having problems translating it to a query, it is pretty simple.
I have 2 repositories, Album and AlbumImage, when I fetch an album do I want a cover, that is a subselect in AlbumImages. What am I doing wrong here?
LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[Sogaard.us.Cosplay.Data.AlbumImage] Get()'
method, and this method cannot be translated into a store expression.
Album repository
public class AlbumRepository : IRepository<Album>
{
private CosplayEntities _entities;
private IRepository<AlbumImage> _imageRepository;
public AlbumRepository(CosplayEntities entities, IRepository<AlbumImage> imageRepository)
{
_entities = entities;
_imageRepository = imageRepository;
}
public IQueryable<Album> Get()
{
return (from a in _entities.Albums
select new Album()
{
Id = a.Id,
UserId = a.UserId,
Name = a.Name,
Created = a.Created,
LastEdit = a.LastEdit,
Description = a.Description,
Views = a.Views,
Location = a.Location,
Photoshoot = a.Photoshoot,
Cover = (from ai in _imageRepository.Get() where ai.AlbumId == a.Id orderby ai.Cover descending, ai.Id ascending select ai).FirstOrDefault(),
});
}
}
AlbumImage repository
public class AlbumImageRepository : IRepository<AlbumImage>
{
private CosplayEntities _entities;
public AlbumImageRepository(CosplayEntities entities)
{
_entities = entities;
}
public IQueryable<AlbumImage> Get()
{
return (from ai in _entities.AlbumImages
select new AlbumImage()
{
Id = ai.Id,
AlbumId = ai.AlbumId,
UserId = ai.UserId,
Type = ai.Type,
Width = ai.Width,
Height = ai.Height,
Description = ai.Description,
Views = ai.Views,
Uploadet = ai.Uploadet,
LastView = ai.LastView,
Thumblink = ai.Thumblink,
Imagelink = ai.Imagelink,
Cover = ai.Cover
});
}
This is the code i am getting the error on
_albumImageRepository = new AlbumImageRepository(_entities);
_albumRepository = new AlbumRepository(_entities, _albumImageRepository);
_albumImagesTagRepository = new AlbumImagesTagRepository(_entities);
....
var album = _albumRepository.Get().Where(x => x.Id == image.AlbumId).FirstOrDefault();
Update: I have commented the Cover = ... out in my IQueryable Get() so it is 2 simple select as object.
And i still get the error in something as simple as
model.Albums = (from a in _albumRepository.Get()
orderby a.Id descending
select new AlbumDisplayModel()
{
Album = a,
ImageCount = _albumImageRepository.Get().Where(x => x.AlbumId == a.Id).Count(),
User = _userRepository.Get().Where(x => x.Id == a.UserId).FirstOrDefault()
})
.Skip(AlbumsPrPage * (page - 1))
.Take(AlbumsPrPage).ToList();
Update 2: If i rewrite the IQueryable Get() to the following, do it work flawlessly, there there should really be no diffrence in how it is handled?
public IQueryable<Album> Get()
{
return (from a in _entities.Albums
select new Album()
{
Id = a.Id,
UserId = a.UserId,
Name = a.Name,
Created = a.Created,
LastEdit = a.LastEdit,
Description = a.Description,
Views = a.Views,
Location = a.Location,
Photoshoot = a.Photoshoot,
Cover = (from ai in _entities.AlbumImages where ai.AlbumId == a.Id orderby ai.Cover descending, ai.Id ascending select new AlbumImage()
{
Id = ai.Id,
AlbumId = ai.AlbumId,
UserId = ai.UserId,
Type = ai.Type,
Width = ai.Width,
Height = ai.Height,
Description = ai.Description,
Views = ai.Views,
Uploadet = ai.Uploadet,
LastView = ai.LastView,
Thumblink = ai.Thumblink,
Imagelink = ai.Imagelink,
Cover = ai.Cover
}).FirstOrDefault(),
});
}
Update 3: Did a little test, and the problem seems to be with Entity framework, se the following code, The var linqAlbum = testClass.LinqAlbumGet().ToList(); executes without any problems and return the correct data, var eeAlbum = testClass.EEAlbumGet().ToList(); fails with the exception
LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[RepositoryTest.TestAlbumCover] EEImageGet()'
method, and this method cannot be translated into a store expression.
My test script
class Program
{
static void Main(string[] args)
{
var linq = new LinqDataContext();
var ee = new NewCosplayEntities();
var testClass = new Test(linq, ee);
var linqAlbum = testClass.LinqAlbumGet().ToList();
var eeAlbum = testClass.EEAlbumGet().ToList();
}
}
public class Test
{
public NewCosplayEntities ee { get; set; }
public LinqDataContext linq { get; set; }
public Test(LinqDataContext linq, NewCosplayEntities ee)
{
this.linq = linq;
this.ee = ee;
}
public IQueryable<TestAlbum> LinqAlbumGet()
{
return from a in linq.Albums
select new TestAlbum
{
Id = a.Id,
Name = a.Name,
Cover = (from i in LinqImageGet() where i.AlbumId == a.Id select i).FirstOrDefault()
};
}
public IQueryable<TestAlbumCover> LinqImageGet()
{
return from i in linq.AlbumImages
select new TestAlbumCover()
{
Id = i.Id,
AlbumId = i.AlbumId
};
}
public IQueryable<TestAlbum> EEAlbumGet()
{
return from a in ee.Albums
select new TestAlbum
{
Id = a.Id,
Name = a.Name,
Cover = (from i in EEImageGet() where i.AlbumId == a.Id select i).FirstOrDefault()
};
}
public IQueryable<TestAlbumCover> EEImageGet()
{
return from i in ee.AlbumImages
select new TestAlbumCover()
{
Id = i.Id,
AlbumId = i.AlbumId
};
}
}
public class TestAlbum
{
public int Id { get; set; }
public string Name { get; set; }
public TestAlbumCover Cover { get; set; }
}
public class TestAlbumCover
{
public int Id { get; set; }
public int AlbumId { get; set; }
}
Your problem comes in the ItemRepository for Albumn. Specifically because _entities has no knowledge of the _imageRepository type, so it doesn't know how to translate that type into the appropriate TSQL script. You could cast the _entities.Albums.ToList() to force the IQueryable into an IEnumerable before you try to access the _ImageRepository.Get() from the scope of the hydrated object instead of directly on the database instance. Realize that you are then going to see a perf hit on the n+1 database requests for the AlbumImage child objects for each Album.
public IQueryable<Album> Get()
{
return (from a in _entities.Albums
select new Album()
{
Id = a.Id,
UserId = a.UserId,
Name = a.Name,
Created = a.Created,
LastEdit = a.LastEdit,
Description = a.Description,
Views = a.Views,
Location = a.Location,
Photoshoot = a.Photoshoot,
Cover = (from ai in _imageRepository.Get() where ai.AlbumId == a.Id orderby ai.Cover descending, ai.Id ascending select ai).FirstOrDefault(),
});
}
Ultimately, the problem is that your trying to use an ActiveRecord pattern rather than a true repository. Everything in a single IQueryable needs to be fetched through the same database context instance for parsing and tracking purposes.
Potentially its because you are wrapping the Album and AlbumImage in new references. I would remove that and do the projection after your query.
I don't think you can project into an entity and have each projection use a result from another IQueryable. If you replaced the contents of IQueryable<AlbumImage> Get() with this, it might work:
from a in _entities.Albums
join c in _imageRepository.Get() on a.Id equals c.AlbumId into acJoin
from ac in acJoin.DefaultIfEmpty()
select new Album()
{
Id = a.Id,
etc..,
etc..,
Cover = ac
}
I'm actually fairly certain that you will need to adjust this freehand query, but essentially it's joining the IQueryables, then projecting those results into your objects, instead of projecting to your objects then inserting an IQueryable into those results. Not the best explanation I know, but just look up "LINQ Left Join" or "Linq Left Outer Join" to see the syntax of what I'm describing here. Example

Categories

Resources