How To Select All Columns From Inherited Class with LINQ and C# - c#

I've got an entity I created using Entity Framework's auto code gen and don't want to modify that entry, as I regenerate that code on every table alter. For the sake of a single EF query, I've created an inherited entity that adds one extra column, SessionId as showing below.
public class SpeakerWithSessionIdAdded : Speaker
{
public int SessionId { get; set; }
}
Where Speaker is defined as below but with about 100 total column (I do know there should be less columns, but it is was it is right now).,
public class Speaker {
public int Id {get;set;}
public string firstName {get;set;}
public string lastName {get;set;}
... MANY MORE COLUMNS
}
I have a LINQ query as follows where I want to get out all the columns plus the one I added (SessionId). Is there any easy way to do this without using either reflection, or by hand listing every column I want to show?
var speakersWithSessionIdAdded = await
(from sessionPresenter in _dbContext.SessionPresenter
join speaker in _dbContext.Speakerson sessionPresenter.SpeakerId equals attendee.Id
where sessionIds.Contains(sessionPresenter.SessionId)
select new SpeakerWithSessionIdAdded
{
SessionId = sessionPresenter.SessionId,
Id = attendee.Id,
UserFirstName = attendee.UserFirstName,
UserLastName = attendee.UserLastName,
Email = attendee.Email,
Username = attendee.Username,
UserBio = attendee.UserBio,
AllowHtml = attendee.AllowHtml
.. I DON'T WANT TO LIST OUT 100 MORE LINES HERE FOR EVERY PROPERTY
}).ToListAsync();
return speakersWithSessionIdAdded.ToLookup(r => r.SessionId);
}

Related

SQL query to multiple objects using Entity Framework 6

I'm taking the time to learn EF (specifically version 6).
I created two tables in MSSQL and linked up EF6 to the database creating the EF model framework.
Then I created the classes in code. My desire is to have one row pulled with a list of items for "UserDatas" (yes I know it's misspelled).
Consider this code:
public class user
{
[Key]
public int pkID;
public string ForeignCode;
public string UserName;
public virtual List<UserData> UserDatas { get; set; }
}
public class UserData
{
[Key]
public int pkID;
public int fkUserID;
public string ColumnName;
public string ColumnValue;
}
class Program
{
static TestData db = new TestData();
static void Main(string[] args)
{
var record = db.tblUsers.Select(x => new { x.UserName, x.pkID }).FirstOrDefault();
var record2 = db.tblUsers.Include(x => x.tblUserDatas).ToList();
Console.ReadLine();
}
}
The first query is just a test to pull the primary record in the tblUsers table.
The second query is where I am attempting to pull all fields related to that user which include things like first name, last name, address, etc...
What happens when I set a break point on the Console.Readline(), I see 5 rows listed for record2. The "user" class is duplicated in each of those rows. I was expecting to see that only listed once with a list of items for "UserDatas".
How can I get this query to come through as I expect with one row containing a list of "UserDatas"?
Again, this is only for learning purposes so don't worry about data and if this is the best way to store it.
It should be as simple as the following (if you don't need the projection/anonymous object) and assuming your entities are configured correctly
var user = db.tblUsers
.Include(x => x.UserDatas) // include user data
.FirstOrDefault(); // select the first user
Some notes,
There is no need to prefix tables with tbl
There is no need to prefix fields with pk, fk
If you used Id, you don't need to specify [key]

Entity Framework dataset mapping

All of my DAL functions are using dbContext.Database.SqlQuery to map stored procedure results in business logic objects.
My application became more complicated and I'm looking for a modern, "up to date" way to handle the following situations. I know that I can achieve this using the low-level ADO.NET component like SqlDataReader and map the result manually, but I am sure there is the best way to do so using Entity Framework 6.
To the question: with this command dbContext.Database.SqlQuery<MyClass>, I can not handle:
The stored procedure that returns 2 result sets
Mapping the result set to a complex datatype
Example:
public class Order
{
public Customer customer { get; set; }
public Items[] items { get; set; }
}
Again, I know that I can map it manually or with AutoMapper, but I'm looking for an "up to date" approach based on Entity Framework 6.
Yes, there's a way using Translate.
Adapted from the official documentation:
var cmd = dbContext.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[GetAllCustomersAndOrders]";
dbContext.Database.Connection.Open();
// Run the sproc
var reader = cmd.ExecuteReader();
var Customers= ((IObjectContextAdapter)dbContext)
.ObjectContext
.Translate<Customer>(reader, "Customers", MergeOption.AppendOnly);
reader.NextResult();
var Orders = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Order>(reader, "Orders", MergeOption.AppendOnly);
As far as the problem of mapping
few columns from the result to a 2nd level complex type? for example:
SELECT FirstName, LastName, OrderId FROM Orders I want to map it to:
public class Order { public Customer customer { get; set; } public int
OrderId { get; set; } }
The best would be to use a CustomerId inside your Order table, referencing a Customer table, instead of FirstName/LastName. It would be a good refactoring, normalizing the database. Otherwise you will not have a real mapping between your objects and your database, since your Order object will have a Customer property that doesn't exist in your database. In that case, you will have to create a class, e.g. NormalizedOrder
public class NormalizedOrder {
int OrderId { get; set; };
Customer OrderCustomer { get; set; };
}
And then, after the code above where you retrieve all Orders, do something like
var NormalizedOrders = Orders.Select new Order(){OrderId = e.OrderId, OrderCustomer = new Customer(){FirstName=>e.FirstName,LastName=>e.LastName}};

C# aggregate data from mulitple columns taken from a resultset of an RAW SQL query with Entity Framework6 and add those data to Lists in a ViewModel

I want to process the data from my database-query using raw SQL in Entity Framework 6 as follows and need a best practice by the use of native functions of C# and LINQ:
PICTURE 1: Resultset taken from database
I have created a class for the resultset above, it looks like that:
public class ProjectQueryModel {
public int Project { get; set; }
public string Projectname { get; set; }
public int RoomId { get; set; }
public string RoomName { get; set; }
public int? EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int? QualificationId { get; set; }
public string QualificationName { get; set; }
public int? QualificationLevel { get; set; }
}
To this point the query works and I got all my data from it stored in a List of type ProjectQueryModel. Now I want to add this data to my ViewModel and don't know how to use the functions C# offers me to process the data of resultsets. How can I achieve the following by saving every entity of type ProjectViewModel in a List, whose objects have the following structure:
PICTURE 2: data organisation in ViewModel
An example dataset for project 1 in the target list should look like this:
ProjectId = 1
Projectname = T1
RoomId = 1
RoomName = Delta Room
======================
Employees *(Attribute of type List <ProjectEmployeesVM> )*
[0].EmployeeId = 2
[0].EmployeeName = Mee
[0].EmployeeQualifications *(Attribute of type List<EmployeeQualificationsVM)*
[0].EmployeeQualifications[0].QualificationId = 1
[0].EmployeeQualifications[0].QualificationName = Programmer
[0].EmployeeQualifications[0].QualificationLevel = 3
...any other qualification of the employee
[1].EmployeeId = 2
[1].EmployeeName = Mee
[1].EmployeeQualifications
[1].EmployeeQualifications[0]
...Any other employee in this project and all of his qualifications
What I also want to achieve is to save a empty list in case the project has no employees, because the resultset is achieved by the use of LEFT OUTER JOINS. For the qualifications it is not necessary, because every employee has at least one qualification.
VERY BIG THANKS in advance
I'm supposing you have a constructor in every class involved that takes all the properties as arguments.
Here's how i would do it:
List<ProjectQueryModel> queryResult = ...;
List<ProyectViewModel> views = queryResult
// Take all the rows that belong to one proyect
.GroupBy(m => m.Proyect)
// Convert every group into a ProyectViewModel
// First use Select to Map every Group into a new Proyect using a function that takes a group of rows and return a Proyect
// Then we use Aggregate inside that mapping function to collapse the entire group of rows into a single ProyectViewModel
// We'll need a contructor in ProyectViewModel that gives us a completly empty instance
// Aggregate takes a starting point, and a function that takes that starting point, and passes it every element of the IEnumerable we're using. The return value of that function is the "new starting point".
// Using this we'll build the Proyect from every row.
.Select(g => g.Aggregate(new ProyectViewModel(), (pvm, nxtRow) => {
// Check if we haven't initialized the instance, and do so.
if (pvm.ProyectId == null) pvm.ProyectId = nxtRow.Proyect;
if (pvm.ProyectName == null) pvm.ProyectName = nxtRow.ProyectName;
if (pvm.RoomId == null) pvm.RoomId = nxtRow.RoomId;
if (pvm.RoomName == null) pvm.RoomName = nxtRow.RoomName;
if (pvm.Employees == null) pvm.Employees = new List<ProyectEmployeeViewModel>();
// If the row has an employee
if (nxtRow.EmployeeId.HasValue) {
// If the Employee is not yet on the Proyect add it
if (!pvm.Employees.Any(e => e.EmployeeId == nxtRow.EmployeeId))
{
// This constructor should create the empty List of Qualifications
pvm.Employees.Add(new ProyectEmployeeViewModel(nxtRow.EmployeeId.Value, nxtRow.EmployeeName);
}
// If the row has a qualification
if (nxtRow.QualificationId.HasValue)
{
// Find it's employee
pvm.Employees.First(e => e.EmployeeId == nxtRow.EmployeeId)
// Add the current row's qualification to the employee
.Qualifications.Add(new EmployeeQualificationsViewModel(nxtRow.QualificationId.Value, nxtRow.QualificationName, nxtRow.QualificationLevel.Value));
}
}
// Return the Proyect with the changes we've made so we keep building it
return pvm;
})).ToList();
LINQ is quite a beauty isn't it?
There might be errors, but use this as a starting point.
Start by making sure that your database has the right foreign key constraints between your tables, then update your model. This will automatically create the correct navigation properties. I've assumed they will be called Employees and Qualifications, but change as appropriate.
Then your query just becomes:
var result=db.Projects
.Include(p=>p.Employees)
.Include(p=>p.Employees.Select(e=>e.Qualifications))
.Where(p=>p.id==1)
.AsEnumerable(); // or .ToList() if you prefer
Then just pass IEnumerable<Project> to your view (or just Project if your view will always only get 1 Project -- in that case, just end the query with .First() instead of .AsEnumerable()) . Unless of course you like creating ViewModels, but I'm guessing you don't and this isn't a project that needs the added complexity or abstractions.
The above code assumes you have the following tables:
Project (int Id, varchar(50) Name, int RoomId)
Room (int Id, int Name)
Employee (int Id, varchar(50) Name)
Qualification (int Id,varchar(50) Name, int Level)
Cross Reference tables:
ProjectEmployees (int ProjectId, int EmployeeId)
EmployeeQualifications (int EmployeeId, int QualificationId)
Foreign Keys:
Project.RoomId -> Room.Id
ProjectEmployees.ProjectId -> Project.Id
ProjectEmployees.EmployeeId -> Employee.Id
EmployeeQualifications.EmployeeId -> Employee.Id
EmployeeQualifications.QualificationId -> Qualification.Id

Entity Framework & Multitables

I have a linq to entity expression:
entities = new zdmEntities();
var reltables = (from r in entities.relations
orderby r.id
select new Relation
{
Id = r.id,
Devices = r.devices.device_name,
Systems = r.systems.system_name,
Models = r.models.name,
Functions = r.functions.function_name
}).ToList();
ultraGrid1.DataSource = reltables.ToList();
class Relation
{
public int Id { get; set; }
public string Devices { get; set; }
public string Systems { get; set; }
public string Models { get; set; }
public string Functions { get; set; }
}
As you can see the relation table contains a link to other tables.
The class Relation contains my columns for the datagrid.
But there is one problem... can't be posssible two way databinding between grid and database. I wrote all the updates manually but it's very difficult.
I understand that this is because in linq expression there is 'new'. But how do you make it without 'new'?
How I can display columns that I need with a two-way databinding and without own class like 'Relation'.
Windows Form. Not wpf)
Thanx, Alex.
When you write entities.Relations.Select(r => new ...) you are making a projection of each Relation EF object into a new non-EF object. By EF object I mean a class which is known by and tracked by EntityFramework.
Making changes to a EF-known class instance would propagate the changes back to DB when you save changes in your db/entity context. In contrast, making changes to a EF-unknown projection (or any projection) has no effect on the original object.
There are two ways you can achive what you want: If your DataGrid (NetAdvantage UltraGrid?) supports binding to subobjects (such as relation.device) you can then use ultraGrid.DataSource = entities.relations and define grid columns to bind to field devices.device_name. The other way would be something like this:
class Relation
{
private readonly EfRelation _originalRelation;
public Relation(EfRelation originalRelation)
{
this._originalRelation = originalRelation;
}
public string Devices
{
get { return this._originalRelation.devices.device_name; }
set { this._originalRelation.devices.device_name = value; }
}
// Repeat for other properties
}
...
var reltables = entities.relations.ToList().Select(r => new Relation(r)).ToList();
Then you just save changes to your db/object context. The EfRelation is the name of your EF Relation class, change it to the name of your EF class which represents a relation.

How to select only a few columns in my NHibernate query?

I have a one class to one table mapping; unfortunately this table has 110+ columns, and queries take a long time process, especially when most of the time I only want to view <10 columns.
My problem is that the queries are dynamically generated based on what the user wants to look at. I can't really create different mappings with different columns because there would be a very large number of combinations. I'm using the criteria API to generate the queries. Can I also use this to only select the columns the user wants? Or some other method?
Thanks
Easy to do with LINQ (assuming you're using NHibernate 3.0 or later):
var products = from p in Session.Query<Product>()
where // ...some query (snip)
select new
{
Name = p.ProductName,
Description = p.ShortDesc,
Price = p.Price,
Units = p.Quantity
};
Also, if you're using HQL, you can just select the columns you need similar to using T-SQL, but use a Transformer to get a strongly typed object back:
First create a class with your narrowed down columns:
public class ProductReport
{
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Units { get; set; }
}
Then your query:
string hql = "select p.ProductName as Name, p.ShortDesc as Description ...(snip) " +
"from Product p " +
"where ...some query (snip)";
IQuery query = Session.CreateQuery(hql)
.SetResultTransformer(Transformers.AliasToBean<ProductReport>());
IList<ProductReport> products = query.List<ProductReport>();
Just sure make the aliases in your query (as Name, as Description etc.) match the property names in your class.
In addition to the example Tim gave you can do something like this:
IList selection =
session.QueryOver<Cat>()
.Select(
c => c.Name,
c => c.Age)
.List<object[]>();
Above example was taken from: http://nhforge.org/blogs/nhibernate/archive/2009/12/17/queryover-in-nh-3-0.aspx
Use a ProjectionList to select the columns you want. See here for the examples.

Categories

Resources