Trying to convert an entity object to local object so i can use it for further transformations.
Here is the code that i am using to convert the entity object;
IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea
select new SystemArea
{
SystemAreaId = sa.SystemAreaId,
SystemAreaCode = sa.SystemAreaCode,
SystemAreaType = sa.SystemAreaType,
SystemAreaDescription = sa.SystemAreaDescription,
SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
SystemAreaStatus = sa.SystemAreaStatus,
Count = sa.systemareafunctionality.Count,
SystemAreaFunctionality = sa.systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
}).ToList();
Here the count variable is to confirm whether there is any child data in it.
SystemAreaFunctionality is the child object that i am trying to convert here by using SELECT function but it is always blank collection. Rest data is getting assigned to parent object but the only thing missing here is the child table records. Where am i going wrong, please help!
Generated SQL :
SELECT
`Project3`.`C1`,
`Project3`.`SystemAreaId`,
`Project3`.`SystemAreaCode`,
`Project3`.`SystemAreaType`,
`Project3`.`SystemAreaDescription`,
`Project3`.`SystemAreaCreatedDate`,
`Project3`.`SystemAreaUpdateDate`,
`Project3`.`SystemAreaStatus`,
`Project3`.`C3` AS `C2`,
`Project3`.`C2` AS `C3`,
`Project3`.`SystemAreaCode1`
FROM (SELECT
`Project1`.`SystemAreaId`,
`Project1`.`SystemAreaCode`,
`Project1`.`SystemAreaType`,
`Project1`.`SystemAreaDescription`,
`Project1`.`SystemAreaCreatedDate`,
`Project1`.`SystemAreaUpdateDate`,
`Project1`.`SystemAreaStatus`,
1 AS `C1`,
`Project2`.`SystemAreaCode` AS `SystemAreaCode1`,
`Project2`.`C1` AS `C2`,
`Project1`.`C1` AS `C3`
FROM (SELECT
`Extent1`.`SystemAreaId`,
`Extent1`.`SystemAreaCode`,
`Extent1`.`SystemAreaType`,
`Extent1`.`SystemAreaDescription`,
`Extent1`.`SystemAreaCreatedDate`,
`Extent1`.`SystemAreaUpdateDate`,
`Extent1`.`SystemAreaStatus`,
(SELECT
COUNT(1) AS `A1`
FROM `systemareafunctionality` AS `Extent2`
WHERE `Extent1`.`SystemAreaCode` = `Extent2`.`SystemAreaCode`) AS `C1`
FROM `systemarea` AS `Extent1`) AS `Project1` LEFT OUTER JOIN (SELECT
`Extent3`.`SystemAreaCode`,
1 AS `C1`
FROM `systemareafunctionality` AS `Extent3`) AS `Project2` ON `Project1`.`SystemAreaCode` = `Project2`.`SystemAreaCode`) AS `Project3`
ORDER BY
`Project3`.`SystemAreaCode` ASC,
`Project3`.`C2` ASC
JSON output:
[{"SystemAreaId":1,"SystemAreaCode":"KIO","SystemAreaType":"KIOSK","SystemAreaDescription":"tasks
related to
receptionist","SystemAreaCreatedDate":"/Date(1543421018000)/","SystemAreaUpdateDate":"/Date(1543421018000)/","SystemAreaStatus":true,"SystemAreaFunctionality":[],"Count":1}]
PS : Please don't suggest automapper or extension methods. Thanks!
OPINION :
Took me two days to make MySQL(latest version) work with EF and trust me it was painstaking and on the contrary EF with MSSQL is so simple and easy to implement.
One thing i experienced is that Oracle is not interested in providing support for the free version of MySQL whatsoever, so they are being sloppy on the documentation of new version and are providing unstable .NET connectors.
ACTUAL ANSWER :
EF was behaving so weirdly, that it would only load the data in the child entity (SystemAreaFunctionality) only if i asked EF to load the child of the child entity (i.e. SystemAreaFunctionalityEmployeeRoleMapping which is child to SystemAreaFuncionality), which also means that i had to take unnecessary data.
So my link query looks like this :
var result = (from sa in CurrentContext.systemarea
select new SystemArea
{
SystemAreaId = sa.SystemAreaId,
SystemAreaType = sa.SystemAreaType,
Count = sa.systemareafunctionality.Count,
SystemAreaFunctionalities = sa.systemareafunctionality.Select(saf => new SystemAreaFunctionality
{
SystemAreaId = saf.SystemAreaId,
SystemAreaFunctionalityController = saf.SystemAreaFunctionalityController,
SystemAreaFunctionalityAction = saf.SystemAreaFunctionalityAction,
SystemAreaFunctionalityType = saf.SystemAreaFunctionalityType,
SystemAreaFunctionalityEmployeeRoleMappings = saf.systemareafunctionalityemployeerolemapping.Select(saferm => new SystemAreaFunctionalityEmployeeRoleMapping
{
SystemAreaFunctionalityEmployeeRoleMappingId = saferm.SystemAreaFunctionalityEmployeeRoleMappingId,
SystemAreaFunctionalityCreatedDate = saferm.SystemAreaFunctionalityCreatedDate
})
})
}).ToList();
ALTERNATIVELY :
Tried using the same linq query (posted in OP) with different database this time with PostgreSQL plus npgsql connector and surprisingly EF gives me exactly what i want with out extra baggage.
On top of that PostgreSQL gives better performance with EF than MySQL. So i presume that switching to PostgreSQL would be a better option.
PS : If you are deciding on open sources DBMS then please refer this before jumping in with MySQL :
https://www.youtube.com/watch?v=emgJtr9tIME
https://www.reddit.com/r/csharp/comments/40x3nx/which_orm_is_the_right_choice_for_mysql
Use this:
CurrentContext.systemarea.Include('systemareafunctionality')
or
IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea
join systemareafunctionality in CurrentContext.systemareafunctionality on sa.systemareafunctionalityID equals systemareafunctionality.ID
select new SystemArea
{
SystemAreaId = sa.SystemAreaId,
SystemAreaCode = sa.SystemAreaCode,
SystemAreaType = sa.SystemAreaType,
SystemAreaDescription = sa.SystemAreaDescription,
SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
SystemAreaStatus = SystemAreaStatus,
Count = systemareafunctionality.Count,
SystemAreaFunctionality = systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
}).ToList();
or
IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea
join systemareafunctionality in CurrentContext.systemareafunctionality on sa.systemareafunctionalityID equals systemareafunctionality.ID into item1 from subitem1 in item1.DefaultIfEmpty()
select new SystemArea
{
SystemAreaId = sa.SystemAreaId,
SystemAreaCode = sa.SystemAreaCode,
SystemAreaType = sa.SystemAreaType,
SystemAreaDescription = sa.SystemAreaDescription,
SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
SystemAreaStatus = SystemAreaStatus,
Count = systemareafunctionality.Count,
SystemAreaFunctionality = systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
}).ToList();
Apparently your CurrentContext is a Dbcontext with at least a table of SystemAreas and a table of SystemAreaFunctionalities.
It seems that every SystemArea has zero or more SystemAreaFunctionalities; every SystemAreaFunctionality belongs to exactly one SystemArea, a straightforward one-to-many relationship using a foreign key.
Note: it might be that you have a many-to-many relation, the answer will be similar
Alas you forgot to write your classes, so I'll give a shot:
class SystemArea
{
public int Id {get; set;}
... // other properties
// every SystemArea has zero or more SystemAreaFunctionalities (one-to-many)
public virtual ICollection<SystemAreaFunctionality> SystemAreaFunctionalities {get; set;}
}
class SystemAreaFunctionality
{
public int Id {get; set;}
... // other properties
// every SystemAreaFunctionality belongs to exactly one SystemArea, using foreign key
public int SystemAreaId {get; set;}
public virtual SystemArea SystemArea {get; set;}
}
In entity framework the columns of your tables are represented by non-virtual properties, the virtual properties represent the relationships between the tables. (one-to-many, many-to-many, ...)
for completeness:
class CurrentContext : DbContext
{
public DbSet<SystemArea> SystemAreas {get; set;}
public DbSet<SystemAreaFunctionality> SystemAreaFunctionalities {get; set;}
}
If people want items with their sub-items, like Schools with their Students, Customers with their Orders, etc. people tend to perform a (group)Join. However, when using entity framework joins are seldom necessary. Ise the ICollections instead. Entity framework knows the relationships and knows which (group)join to perform.
Regularly I see people use Include, but if you do that, you'll select the complete object, which is not very efficient. Suppose you have a SystemArea with Id = 10, and 1000 SystemAreaFunctionalities, you know that every SystemAreaFunctionality has a foreign key SystemAreaId with a value 10. Instead of sending this value only once as primary key of SystemArea, Include will also select all 1000 foreign keys with this value 10. What a waste of processing power!
When querying data, always use Select and select only the properties you actually plan to use. Only use Include if you plan to Update the included object.
You wrote:
SystemAreaFunctionality is the child object that i am trying to convert here...
It is not clear what you really want. Do you want a collection of all used SystemAreaCodes? Or do you really want a collection of new SystemAreaFunctionalities where only one field is filled: SystemAreaCode? Because of you use of singular property name, it seems you don't want a collection but only one item.
var result = currentContext.SystemAreas.Select(systemArea => new
{
Id = systemArea.Id,
Code = systemArea.Code,
...
// if you want a collection of SystemAreaFunctionalities
// where every SystemAreaFunctionality is filled with only SysemAreaCode
// do the following:
SystemAreaFunctionalities = systemArea.SystemAreaFunctionalities
.Select(systemAreaFunctionality => new SystemAreFunctionality
{
SystemAreaCode = systemAreaFunctionality.SystemAreaCode,
})
.ToList(), // DON'T FORGET THIS, OR YOU WON'T GET RESULTS!
})
.ToList()
}
I think the cause of your empty SystemAreaFunctionalities is because you forgot to do ToList().
Because you used ToList(), you automatically have the Count of the selected SystemAreaFunctionalities. There is no need to select this Count separately.
One of the slower parts of database queries is the transport of the selected data from the database management system to your local process. It is good practice to only select data you actually plan to use
You query is not very efficient because you select complete SystemAreaFunctionalities and fill only the SystemAreaCode. All other fields will be filled by default values. Apart from the slower query, you also give your callers the impression that they get properly filled SystemAreaFunctionalities. An improved version would be:
var result = currentContext.SystemAreas.Select(systemArea => new
{
// select only the properties you actually plan to use
Id = systemArea.Id,
Code = systemArea.Code,
...
// if you only need the SystemAreaCodes, select only that property
SystemAreaCodes = systemArea.SystemAreaFunctionalities
.Select(systemAreaFunctionality => systemAreaFunctionality.SystemAreaCode)
.ToList(),
})
.ToList()
};
Of courds, if you need mrre than only the SystemAreaCodes, but several SystemAreaFunctionalities, go ahead, select them:
...
SystemAreaFunctionalities = systemArea.SystemAreaFunctionalities
.Select(systemAreaFunctionality => new
{
// again: select only the properties you plan to use!
Id = systemAreaFunctionality.Id
SystemAreaCode = systemAreaFunctionality.SystemAreaCode,
})
An include should do the trick for you like so:
IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea.Include("systemareafunctionality")
select new SystemArea
{
SystemAreaId = sa.SystemAreaId,
SystemAreaCode = sa.SystemAreaCode,
SystemAreaType = sa.SystemAreaType,
SystemAreaDescription = sa.SystemAreaDescription,
SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
SystemAreaStatus = sa.SystemAreaStatus,
Count = sa.systemareafunctionality.Count,
SystemAreaFunctionality = sa.systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
}).ToList();
I'm trying to understand how to use Entity Framework 6. The code below works. However, it appears to have four queries in it for a single write operation. It doesn't seem right to hit the database five separate times. I want a single database call that adds the appropriate item to each table as needed. Is there some better way to do the code below? Or is it really doing a single database hit in the SaveChanges call?
public bool Write(ILogEntry logEntry)
{
var log = logEntry as AssetStateLogEntry;
if (log == null) return false;
using (var db = _dbContextProvider.ConstructContext())
{
if (db != null)
{
var state = new VehicleStateLogEntryDbo
{
LogSource = db.LogSources.FirstOrDefault(l => l.Name == log.Source.ToString())
?? new LogSourceDbo {Name = log.Source.ToString()},
Message = log.Message,
TimeStamp = log.TimeStamp.ToUniversalTime(),
Vehicle = db.Vehicles.FirstOrDefault(v => v.Name == log.Asset.Name)
?? new VehicleDbo {Name = log.Asset.Name, VehicleIdentifier = log.Asset.ID},
VehicleState = db.VehicleStates.FirstOrDefault(v => v.Name == log.StateValue.ToString() && v.VehicleStateType.Name == log.StateType.ToString())
?? new VehicleStateDbo
{
Name = log.StateValue.ToString(),
VehicleStateType = db.VehicleStateCategories.FirstOrDefault(c => c.Name == log.StateType.ToString())
?? new VehicleStateTypeDbo {Name = log.StateType.ToString()},
}
};
db.VehicleStateLogEntrys.Add(state);
db.SaveChanges();
}
}
return true;
}
You are indeed making 4 queries to the database, as a result of these calls:
db.LogSources.FirstOrDefault
db.Vehicles.FirstOrDefault
db.VehicleStates.FirstOrDefault
db.VehicleStateCategories.FirstOrDefault
When you call FirstOrDefault, the LINQ query is executed and thus, the database is hit.
I don't know your schema, but maybe you could join some of them into a single LINQ query (at least the Vehicles* tables seem to be related).
EDIT: sample query using joins as requested by the OP
Take the following query as an starting point of what I suggested, you haven't provided your entities so this is just to give you and idea:
from l in db.LogSources
join v in db.Vehicles on l.Asset.ID equals v.VehicleIdentifier
join vs in db.VehicleStates on vs.VehicleIdentifier equals v.VehicleIdentifier
where l.Name == log.Source.ToString()
&& v.Name == log.Asset.Name
&& vs.Name == log.StateValue.ToString()
&& vs.VehicleStateType.Name == log.StateType.ToString()
select new VehicleStateLogEntryDbo
{
LogSource = l,
Message = log.Message,
TimeStamp = log.TimeStamp.ToUniversalTime(),
Vehicle = s,
VehicleState = vs
}
A couple considerations:
As #Gert suggested, you should probably use foreign keys instead of whole object references.
I haven't considered the possibilities of null values in the example, you can take them into account using left joins with DefaultIfEmpty.
In stead of setting object references you should set primitive foreign key values. From an object-oriented point of view this sounds like a heresy, but it's Entity Framework's recommended approach when it comes to setting associations efficiently.
Of course, there should be foreign key values to be set in the first place. In your VehicleStateLogEntryDbo this could look like:
public int VehicleIdentifier { get; set; } // or guid?
[ForeignKey("VehicleIdentifier")]
public VehicleDbo Vehicle { get; set }
The ForeignKey attribute tells EF that both properties belong together in a foreign key association. This can also be configured by the fluent API, e.g. in the OnModelCreating override:
modelbuilder.Entry<VehicleStateLogEntryDbo>()
.HasRequired(v => v.Vehicle)
.WithMany()
.HasForeignKey(v => v.VehicleIdentifier);
By the way, having a Vehicle property only is referred to as an independent association.
So when you have these foreign key associations in place you can simply set the FK values. Maybe you should modify your DTO to transfer these values in stead of names etc.
I am creating App using Entity Framework.I have loaded the Database model from Database.here In the Course table Department is the Navigation Property,DepartmentID is the foreign key.I have created a gridview and set its Datasource to Course Table,but I want to see the Deaptment Name instead of department ID.I can use the Navigation properties like Department.count but how can I use the navigation property to get data (Department name) from Department Table.
Any one who can help me with this
THIS IS MY CODE
var result = (from o in ctx.Courses.Include("Department")
where o.Title == TextBox2.Text
select o
).First();
//GridView4.DataSourceID="";
GridView3.DataSource=result;
GridView3.DataBind();
If I dont use the First Function then i can't access the Department Name attribute,If i use the First() It say that
Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource.
please tell me how i can solve it?
I think Products and Categories tables in Northwind are similar than your need. I would write a query like this:
var ctx = new NorthwindEntities();
var query = from prod in ctx.Products
where prod.ProductName.StartsWith("C")
select new { prod.ProductName, prod.UnitPrice, prod.Category.CategoryName };
var result = (from c in dbContext.Course
select c).First();
if(!result.Department.IsLoaded)
{
result.Department.Load(); //this will load the course.Department navigation property
}
//Assuming you have 1 - 1 relationship with course to department
string departmentName = result.Department.Name;
or if you have 1 - M relationship with the department then:
foreach(Department d in result.Department)
{
Console.WriteLine(d.Name);
}
EDIT:
Instead of trying to load Department do the following
if(!result.DepartmentReference.IsLoaded)
{
result.DepartmentReference.Load()
}
How to: Explicitly Load Related Objects
I know I've seen this but I cant find it anywhere.
I have a couple objects with a many-to-many relationship: Person and Department. A person can have many departments and vice versa.
I thought it was:
var person = //query user
var d = new Department();
d.Id = 123;
person.Departments.add(d);
This creates a new department in the database and links them but that isnt what I want. The department already exists. I just want to create the relationship. How can I do this without having to requery the database to get an instance of the department?
Try this:
var department = dbContext.Departments.FirstOrDefault(d => d.Id == 123);
person.Departments.Add(department);
dbContext.SaveChanges();
This shouldn't query the database until dbContext.SaveChanges() is called as deferred querying is used. However, if that doesn't work, you can try updating the mapping entity directly instead:
var departmentMember = new DepartmentMember
{
DepartmentId = 123,
MemberId = person.Id
};
dbContext.DepartmentMembers.Add(departmentMember);
dbContext.SaveChanges();
How about:
person.Departments.Add(context.Departments.First(c=>c.Id==123));
I am trying to execute the following query but I get the wrong results back.
foreach (var item in (from project in db.Projects
where project.Id == pProjectId
from task in project.Tasks
from taskItem in task.TaskItems
where taskItem.Velocities.Count() == 0 // not finished yet
select new
{
ProjectId = pProjectId,
PriorityId = task.Priorities.Id,
TaskId = task.Id,
ResourceId = taskItem.Resources.Id,
EstimatedDuration = taskItem.EstimatedDuration,
TaskItemId = taskItem.Id
}))
{
}
I am trying to generate objects from all the taskItems that don't have velocity related objects. The table structure is that every taskItem may have many velocities. Right before this call I give velocities to some of the items, yet they are not filtered by this where clause. Am I doing something obvious wrong?
Edit: I think (after staring at the code for a while) that I need to specify some kind of grouping. I don't actually require any of the Velocity record details, but rather just a count of them that relate to the taskItems
You could try moving the taskItem.Velocities.Count() inside the select new { }
Then do a select on the resultant list where the velicties count == 0
You need to have joins to reflect the relationships, currently you are doing a cartesian (cross) join (i.e. all combinations of project, task and taskItem).
Try something like:
var res = from project in db.Projects
where project.Id == pProjectId
join task in project.Tasks on task.projectId equals project.projectId
join ttaskItem in task.TaskItems on taskItem.taskId equals task.taskId
where taskItem.Velocities.Count() == 0 // not finished yet
select new {
ProjectId = pProjectId,
PriorityId = task.Priorities.Id,
TaskId = task.Id,
ResourceId = taskItem.Resources.Id,
EstimatedDuration = taskItem.EstimatedDuration,
TaskItemId = taskItem.Id
};
Have you saved the changes to the entity model before the query?
The Entity model is querying your database to retrieve those values, if you are adding data to the entities BUT had not saved them to the db, the query can't know about those new values.
Try db.SaveChanges() before the query.