SQL query to multiple objects using Entity Framework 6 - c#

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]

Related

How To Select All Columns From Inherited Class with LINQ and 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);
}

Does Entity size matter for performance in EF Core with DB first?

I'm writing a ASP.NET Core Web API project. As a data source It will be using existing (and pretty big) database. But not entire database. The API will use only some of the tables and even in these tables it will not use all the columns.
Using Reverse engineering and scaffolding I was able to generate DbContext and Entity classes... and it got me thinking. There is a table with 30 columns (or more). I'm using this table, but I only need 5 columns.
My question is:
Is there any advantage of removing 25 unused columns from C# entity object? Does it really matter?
The advantage of leaving them there unused is that in case of someone wants to add new functionality that will need one of them, he will not need to go to the db and reverse engineer needed columns (there are there already).
The advantage of removing unused is... ?
EDIT: Here is the sample code:
public class FooContext : DbContext
{
public FooContext(DbContextOptions<FooContext> options)
: base(options)
{
}
public DbSet<Item> Items { get; set; }
}
[Table("item")]
public class Item
{
[Key]
[Column("itemID", TypeName = "int")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("name", TypeName = "varchar(255)")]
public string Name { get; set; }
}
Sample usage:
public ItemDto GetItem(int id)
{
var item = _fooContext.Items.Where(i => i.Id == id).FirstOrDefault();
// Here I have item with two fields: Id and Name.
var itemDto = _mapper.Map<ItemDto>(item);
return itemDto;
}
Obviously I'm curious about more complex operations. Like... when item entity is being included by other entity. For example:
_foo.Warehouse.Include(i => i.Items)
or other more complex functions on Item entity
Your entity needs to match what's in the database, i.e. you need a property to match each column (neglecting any shadow properties). There's no choice here, as EF will complain otherwise.
However, when you actually query, you can select only the columns you actually need via something like:
var foos = await _context.Foos
.Select(x => new
{
Bar = x.Bar,
Baz = z.Baz
})
.ToListAsync();
Alternatively, if you don't need to be able to insert/update the table, you can instead opt to use DbQuery<T> instead of DbSet<T>. With DbQuery<T>, you can use anything class you want, and project the values however you like, via FromSql.

Insert entity with one related entity which existed in the database

I am stuck at the operation when using Entity Framework Core 2 to perform an insert of a new entity with one related entity which already existed in the database. For example, I have two objects with one to many relationship:
public Class OrderDetail
{
public int ParentID {get;set;}
public string OrderDetailName {get;set;}
public int ProductID {get;set;}
public virtual Product ProductFK {get;set;}
}
public Class Product
{
public int ProductID {get;set;}
public string ProductName {get;set;}
public virtual Collection<OrderDetail> OrderDetails {get;set;} = new Collection<OrderDetail>();
}
I would like to add a new OrderDetail with an existing Product (with productID = 3) into the database, so I perform like:
private void AddOrderDetail(int productID)
{
OrderDetail newOrderDetail = new OrderDetail();
newOrderDetail.Name = "New OrderDetail";
// Here I have no clue what would be the correct way...Should I do
// Approach A(pick related entity from database then assign as related entity to base entity):
var ProductFromDB = DbContext.Products.SingleOrDefault(p => p.ProductID == productID);
newOrderDetail.ProductFK = ProductFromDB;
// Approach B(directly assign the foreign key value to base entity)
newOrderDetail.ProductID = productID
DbContext.SaveChange();
}
By using approach (A), my newOrderDetail failed to save and I looked into SQL resource, looks like it considered the one that I retrieved from the database (ProductFromDB) as a new object and was trying to insert it again. I feel it's redundant job for picking ProductFromDB first then assign to the inserted entity...
By using approach (B), my newOrderDetail still failed to save, and I am getting an exception like "insert A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.", however, this exception does not happen constantly. I looked into the SQL and found the SQL Script, by running it individually in SQL Server it worked, however when running an application side, it's not working...
So what would be the correct way to deal with above scenario?
If you don't need access to the complete Product object right away, you could try to set just the foreign key column value of your newOrderDetail. Also, you need to ADD your newly created OrderDetail object to the DbContext before saving - something like this:
private void AddOrderDetail(int productID)
{
OrderDetail newOrderDetail = new OrderDetail();
newOrderDetail.Name = "New OrderDetail";
newOrderDetail.ProductID = productID
// OF COURSE, you need to ADD the newly created object to the context!
DbContext.OrderDetails.Add(newOrderDetail);
DbContext.SaveChange();
}
Next time around, when you actually fetch the OrderDetail, it will resolve the linked product and you should be fine.:
using (var DbContext = new YourDbContext())
{
OrderDetail od = DbContext.OrderDetails.FirstOrDefault(x => x.Name = "New OrderDetail");
....
}

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

LINQ query a list inside a record

EDIT
Turns out EF wont map List strings to a table. So to fix it I've created a simple table with an id, a string field and a foreign Key to the Example table, then in the example table i've updated the List<> attribute to point to this table. ...I think I had a brain fart. sorry all.
I've tried the other questions but I'm still getting the same error.
I have a table with the following:
public class Example
{
public Example()
{
ExampleList = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<string> ExampleList{ get; set; }
}
Now when I go to query the database, I want to return back entries that contain "imastring" in their ExampleList I'm currently using:
var test = db.Examples.Where(c => c.ExampleList.Count(z => z.Contains("imastring")) == 0).ToList();
However, this query brings back the following error:
The specified type member 'ExampleList' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
I can't figure out whats wrong, what am i missing guys?
For clarity
I'm trying to bring back all "Example" record whos "ExampleList<>" contains the string "imastring"
You cannot store in database list of string type (or primitives). You need to create for example ExampleTable class with id and string value. Or keep whole list in one property (for example strings separated with ';') and parse given string with split. Or serialize list to json and save as string property.

Categories

Resources