Linq result return zero vs null - entity-framework

Is there a way I have have this linq statement not return nulls ? I would like it to returns 0 I have tried ?? 0 but that is not working
Customer = (from c in db.Customers
join cx in db.CustomerXrefs on c.CustomerId equals cx.CustomerId
join g in db.Groups on cx.GroupId equals g.GroupId
select new Customer
{
CustomerId = c.CustomerId,
Institution = (from ig in db.Groups
join icx in db.CustomerXrefs on ig.GroupId equals icx.GroupId
where icx.CustomerId == c.CustomerId && ig.GroupTypeId == 308
select new Institution()
{
Name = ig.Name == "In1 " ? "Institution 1" :
ig.Name == "In2" ? "Institution 2" :
ig.Name
}
).FirstOrDefault()
}).FirstOrDefault();

It is a bit difficult to distill from your question what you want to achieve. You give us some code, and tell us that the code doesn't do what you want, and ask us to give you the code that does give the requested result.
I hope I can extract your requirement from your code.
It seems that you have a table of Customers and a table of Groups. There seems to be a many-to-many relation between Customers and Groups: every Customer belongs to zero or more Groups, every Group has zero or more Customers that belong to this Group.
In relational databases, a many-to-many relation is implemented using a junction table. The junction table for the many-to-many between Customers and Groups is table CustomerXRefs
For every Customer in the table of Customers, you want its CustomerId, and a Property Institution.
The value of Institution is filled with a Name. This name is taken from the first Group of this Customer with GroupTypeId equal to 308.
There are two solutions for this:
Use GroupJoins to get the Customers with their Groups and extract the Institution from these groups
Use the capability of entity framework to translate the virtual ICollection<...> into the corrrect GroupJoins. Use the virtual properties, entity framework translates them into the proper GroupJoin.
Usually the latter solution is easier, so we'll start with that one first.
The classes
If you have followed the entity framework conventions, you'll have classes similar to the following:
class Customer
{
public int Id {get; set;}
... // other properties
// every Customer belongs to zero or more Groups (many-to-many)
public virtual ICollection<Group> Groups {get; set;}
}
class Group
{
public int Id {get; set;}
public int GroupTypeId {get; set;}
public string Name {get; set;}
... // other properties
// every Group has to zero or more Customers(many-to-many)
public virtual ICollection<Customer> Customers {get; set;}
}
This is enough for entity framework to detect your many-to-many relation. Without even mentioning it, entity framework will create a junction table for you. Only if you want different names of tables or properties, you'll need fluent API or attributes.
In entity framework the columns of the table are represented by non-virtual properties; the virtual properties represent the relations between the tables: one-to-many, many-to-many, ...
Query using the virtual ICollection
var customersWithTheirGroups = dbContext.Customers.Select(customer => new
{
CustomerId = customer.Id,
Institution = customer.Groups.Where(group => group.GroupTypeId == 308)
.Select(group => new Institution
{
name = (ig.Name == "In1") ? "Institution 1" :
(ig.Name == "In2") ? "Institution 2 :
ig.Name,
})
.FirstOrDefault(),
});
BTW: note the parentheses around (ig.Name == "In1"). I think that if Name equals "In1" that you want a name "Institution 1", etc.
Is it correct that you have a class Institution with only one property?
class Institution
{
public string Name {get; set;}
}
Although it is allowed, it is a bit awkward, why not just select the InstitutionName?
.Select(group => (ig.Name == "In1") ? "Institution 1" :
(ig.Name == "In2") ? "Institution 2 :
ig.Name)
.FirstOrDefault(),
Anyway, entity framework knows the relations between the tables and will create the correct groupjoins with the junction tables.
This solution seems very natural.
Solution using GroupJoins
If you want to do the GroupJoin yourself, you'll need to define the junction table:
class CustomerXrefs
{
// every Xref in the junction table belongs to exactly one Customer:
public int CustomerId {get; set;}
// every Xref in the junction table belongs to exactly one Group:
public int GroupId {get; set;}
}
To get each Customer with its Groups, do a GroupJoin of the XRefs with the Customers, followed by another GroupJoin of Groups and XRefs.
var customersWithTheirGroups = dbContext.Customers.GroupJoin(dbContext.CustomerXRefs,
customer => customer.Id, // from every Customer take the Id
xref => xref.CustomerId, // from every xref take the foreign key to Customer
// Parameter resultSelector:
// for every Customer and all its xrefs make one new object:
(customer, xrefsOfThisCustomer) => new
{
Customer = customer,
// To get the Groups of this customer, GroupJoin the Groups with the xrefs:
Groups = dbContext.Groups
// GroupJoin the Group with the xrefs of this Customer
.GroupJoin(xrefsOfThisCustomer,
group => group.Id, // for every Group take the Id,
xRef => xref.GroupId, // for every xref of this customer take the foreign key
// parameter resultSelector:
// for every Group, and all xrefs of this Group that are xrefs of the Customer
// make one new:
(group, xrefs) => group).ToList(),
});
Result: all Customers, each with their Groups.
Select the institution:
var result = customersWithTheirGroups.Select(joinResult => new
{
CustomerId = joinResult.Customer.Id,
Institution = joinResult.Groups
// Keep only the Groups with the desired GroupTypeId
.Where(group => group.GroupTypeId == 308)
// The Select is similar as the Select in the virtual ICollection method:
.Select(group => new Institution
{
name = (ig.Name == "In1") ? "Institution 1" :
(ig.Name == "In2") ? "Institution 2 :
ig.Name,
})
.FirstOrDefault(),
});

Customer is a class (reference type). Default value for a reference type is null. If you want to return some non null value (if there exists correct "default" for your use case) you will need to provide one, for example:
Customer = (...).FirstOrDefault() ?? new Customer();

Related

Entity Framework group by left join query

The Result should look like this
I've started to use Entity Framework for one month so I am not familiar with linq queries. The query I wrote in SQL is:
SELECT
om0001.CUSTOMER, om0001.ITEM_CODE,
SUM(om0001.AMOUNT) AS AMOUNT,
SUM(ep0001.EXPORT_AMOUNT) AS EXPORT_AMOUNT
FROM
om0001
LEFT OUTER JOIN
ep0001 ON om0001.ID = ep0001.om0001_ID
GROUP BY
om0001.CUSTOMER, om0001.ITEM_CODE;
When I run this query in SQL, it runs well so I tried to convert it to linq queries.
What I made so far is
var testjoin = from om0001 in EF.om0001
join ep0001 in EF.ep0001 on om0001.ID equals ep0001.om0001_ID
into jointable
from z in jointable.DefaultIfEmpty()
group z by new {om0001.CUSTOMER, om0001.ITEM_CODE } into g
select new
{
CUSTOMER = g.Key.CUSTOMER,
ITEM_CODE = g.Key.ITEM_CODE,
om0001SUMamount = g.Sum(x => x.AMOUNT),
ep0001EXPORTsumAmount = g.Sum(y => y.EXPORT_AMOUNT)
};
The problem over this linq query is I can not get om0001SUMamount. I get only ep0001 column data. Please help
Obviously, I cant peek into your EF database, so I created some sample data (the 'item' class structures are implied):
var EF = new efClass {
om0001 = new List<om0001item> {
new om0001item { ID = 0, CUSTOMER = 0, ITEM_CODE = 0, AMOUNT = 10 },
new om0001item { ID = 1, CUSTOMER = 0, ITEM_CODE = 0, AMOUNT = 20 },
new om0001item { ID = 2, CUSTOMER = 1, ITEM_CODE = 1, AMOUNT = 30 },
new om0001item { ID = 3, CUSTOMER = 1, ITEM_CODE = 1, AMOUNT = 40 }
},
ep0001 = new List<ep0001item> {
new ep0001item { om0001_ID = 0, EXPORT_AMOUNT = -20 },
new ep0001item { om0001_ID = 1, EXPORT_AMOUNT = -20 }
}
};
With this data, I created a query that frankly feels inelegant and left me disappointed, but that's the nature of left joins in LINQ:
var testjoin = from om0001 in EF.om0001
join ep0001 in EF.ep0001 on om0001.ID equals ep0001.om0001_ID into jointable
select new { om0001, ep0001 = jointable.DefaultIfEmpty() } into combined
group combined by new {
combined.om0001.CUSTOMER,
combined.om0001.ITEM_CODE
} into g
select new {
CUSTOMER = g.Key.CUSTOMER,
ITEM_CODE = g.Key.ITEM_CODE,
om0001SUMamount = g.Sum(x => x.om0001.AMOUNT),
ep0001EXPORTsumAmount = g.Sum(x => x?.ep0001.Sum(y => y?.EXPORT_AMOUNT ?? 0))
};
Bottom line is that when you group by 'jointable', you've lost ep0001 references. So select both ep0001 and om0001 into a new 'combined' object, and then group based off of it.
When I created a javascript library (fluent-data) that had some LINQ-like functionality, I developed a lot of respect and compassion for the LINQ developers. Nevertheless, I don't know why they don't just create a left join operator to add so much more value to all the C# developers who use LINQ.
So you have a table with Oms (actually Om00001, but I'm not going to write all those 0001 over and over again), and a table with Eps, and you have a one-to-many relation between Oms and Eps: Every Om has zero or more Eps, and every Ep belongs to exactly one Om, namely the Om that foreign key EpId refers to.
If you have followed Entity Framework code first conventions you will have classes similar to the following:
class Om
{
public int Id {get; set;}
public string Customer {get; set;}
public string ItemCode {get; set;}
...
// Every Om has zero or more Eps (one-to-many)
public virtual ICollection<Ep> Eps {get; set;}
}
class Ep
{
public int Id {get; set;}
public int Amount {get; set;}
public int ExportAmount {get; set;}
...
// every Ep belongs to exactly one Om, using foreign key
public int OmId {get; set;}
public virtual Om Om {get; set;}
}
This is enough for entity framework to detect your one-to-many relationship. Because I followed the conventions, there is no need for Attributes, nor fluent API. If you want different table names, or columns, you need fluent API / attributes.
In entity framework the non-virtual properties represent the columns of your tables, the virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
Solution using GroupJoin
var result = dbContext.Oms.GroupJoin(dbContext.Eps,
om => om.Id, // from every Om take the primary key
ep => ep.OmId, // from every ep take the foreign key to the Om
(om, epsOfThisOm) => new // from every om and all eps having the correct foreign key
{ // make one new object
// Select only the properties that you plan to use:
Customer = om.Customer,
ItemCode = om.ItemCode,
Amount = epsOfThisOm.Sum(ep => ep.Amount);
ExportAmount = epsOfThisOm.Sum(ep => ep.ExportAmount);
});
Solution using the virtual ICollection
Instead of executing a GroupJoin, you could also use the virtual ICollection.
Requirement: from every Om, give me the Customer and the ItemCode, and the sum of all Amounts of its Eps, and the sum of all ExportAmounts of its Eps.
var result = dbContext.Oms.Select(om => new
{
Customer = om.Customer,
ItemCode = om.ItemCode,
Amount = om.Eps.Sum(ep => ep.Amount);
ExportAmount = om.Eps.Sum(ep => ep.ExportAmount);
});
This looks much neater, and it matches more directly your requirement. Entity framework knows the relations, and will do the correct GroupJoin for you.

One-way One to Many relationship using part of a composite key

I have a model in EF similar to this
Person
PK Guid Id
PK DateTime DateSynced
Test
PK Guid Id
FK Guid PersonId
In Entity Framework 6.2, I really only care about Navigation Property on Person with reference to a collection of Tests. I do not need a property of Test.Persons or anything like that.
I really just want to have Person.Tests where Test.PersonId = Id regardless of DateSynced. There will eventually be many persons with the same Id, each with a different DateSynced DateTime.
Is this doable or do I need a Many-To-Many with an intermediate table?
I understand that EFCore has a concept of Alternate Keys and I thought I might could leverage that in this effort, but there does not seem to be a corresponding functionality in EF 6.2
Edit
I have the following Fluent rule in my OnModelCreating override.
modelBuilder.Entity<Test>()
.HasRequired(t => t.Person)
.WithMany(p => p.Tests)
.HasForeignKey(t => t.PersonId);
I get the following exception complaining about the Dependent and Principal role constraints:
The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
As I understand it, this is because I am referencing an entity with a 2-part composite key by only one part of that key. What I am looking for is a way to do exactly that.
The foreign key on the second table must match the PK on the first.
For example, using Int for the key type for simplicity.
If I have a Person with a PK of ID: 1, SyncDate: 2019-05-22
Then I add a second variant: PK of ID: 1, SyncDate: 2019-05-23
If I go to add a "Test" record, which Person record would it reference with a FK Person ID of 1? It would reference both records, hence EF cannot support a reference of "HasRequired" pointing to a single Person record.
To reference one variant of Person ID 1, your Test record will need both a PersonId and a SyncDate to identify the record:
public class Test
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
public DateTime SyncDate { get; set; }
public virtual Person Person { get; set; }
}
modelBuilder.Entity<Test>()
.HasRequired(t => t.Person)
.WithMany(p => p.Tests)
.HasForeignKey(t => new { t.PersonId, t.SyncDate });
Tables in the database cannot reference each other based on a partial FK unless a many to 1. I.e.
Person
------
PK: PersonID
PersonComment
-----------
PK: PersonId
PK: CommentDate
In this example a Person can have many PersonComments based on the PersonID link, while a Comment can resolve back to the Person via the Person ID. As a FK.
In a table structure that has:
Person
------
PK: PersonID
PK: Version
PersonComment
-------------
PK: PersonCommentID
PersonId
PersonId in the PersonComment cannot be a FK to Person because it doesn't reflect the PK to the Person table. You can legally have this table structure, but PersonId is just a dumb, unconstrained column. You can query all Person records manually using it, but you will get all Versions of the person. There are no constraints etc. to ensure that the Person ID on a comment matches an Id on the Person table.
If you don't care about the versions of a Person You can have a Test entity with a Person ID, but EF can't associate that to Person entities, you'll have to load Person records manually from the Context.
When it comes to the purpose behind your schema structure, I would suggest looking at possible alternatives. For instance, if your goal is to track versioned data I would suggest looking at something like:
Person
PK: PersonId
** Person Fields
PersonHistory
PK: PersonHistoryId
FK: PersonId
VersionDate
** Person Fields
Test
PK: TestId
FK: PersonId (if applies to current person, or PersonHistoryId if a specific version)
Then "Person" reflects the person in it's current state, containing a collection reflecting the History. From there you can prevent modifying the Person fields via private setters and DDD-style methods which would be responsible for composing a new History record based on the current Person data before updating the Person values. This way a Person record can be historical and preserve it's ID for related entities.

Querying many-To-many relationship with a list of parameters

I use EF core and i have the classes "User" and "Authority" which are connected with a many-to-many relationship ("Users" has a property "authorities" and "Authority" has a property "users"). The relationship is managed via middle class "UserAuthority".
I need to query all "Users" that have "Authorities" with certain names.
I tried this:
List<string> authorities = A list of authorities;
(from user in this.dbContext.user.Include("authorities.authority")
where authorities.Any(x => user.authorities.Any(y => y.authority.authority == x))
select new UserDto(user.id, user.firstname + " " + user.lastname)).ToList()
But the console says that LINQ cant translate
authorities.Any(x => user.authorities.Any(y => y.authority.authority == x))
and that it will be handled in memory.
What is the correct approach here?
Currently the only translatable in-memory collection method is Contains (for primitive type in-memory collection, translates to SQL IN(...)).
So instead of
authorities.Any(x => user.authorities.Any(y => y.authority.authority == x))
use
user.authorities.Any(ua => authorities.Contains(ua.authority.authority))
You wrote:
i have the classes "User" and "Authority" which are connected with a many-to-many relationship ("Users" has a property "authorities" and "Authority" has a property "users").
Something like this:
class User
{
public int Id {get; set;}
...
// every User has zero or more Authorities (many-to-many)
public virtual ICollection<Authority> Authorities {get; set;}
}
class Authority
{
public int Id {get; set;}
public string Name {get; set;}
...
// every Authority has zero or more Users (many-to-many)
public virtual ICollection<User> Users {get; set;}
}
I need to query all "Users" that have "Authorities" with certain names.
If I read this literally, you want all Users, that have at least one Authority that has a Name that is in the collection of certainNames. You want each User with ALL his Authorities, even those Authorities with names that are not in certainNames
It could also mean that you want all Users, each with only those of their Authorities that have a Name which is in certainNames, but only those Users that have at least one such Authority.
How about this:
IEnumerable<string> certainNames = ...
var UsersWithAuthoritiesThatAreInCertainNames = myDbContext.Users
.Where (user => user.Authorities.Select(authority => authority.Name)
.Intersect(certainNames)
.Any())
.Select(user => new
{
// select only the User properties you actually plan to use:
Id = user.Id,
Name = user.Name,
...
Authorities = user.Authorities.Select(authority => new
{
// again select only the Authority properties you plan to use:
Id = authority.Id,
Name = authority.Name,
...
})
.ToList(),
})
In words:
From the collection of all users, keep only those users, that have at least one authority with a name that is also in certainNames. From the remaining users, select several properties.
If you don't want ALL Authorities of the user, but only the ones that are in certain names:
var UsersWithOnlyTheirAuthoritiesThatAreInCertainNames = myDbContext.Users
.Select(user => new
{
// select only the User properties you actually plan to use:
Id = user.Id,
Name = user.Name,
...
Authorities = user.Authorities
.Where(authority => certainNames.Contains(authority.Name))
.Select(authority => new
{
// again select only the Authority properties you plan to use:
Id = authority.Id,
Name = authority.Name,
...
})
.ToList(),
})
// keep only the Users that have at least one such authority
.Where(selectedUser => selectedUser.Authorities.Any());
In words:
from the collection of Users, select some properties of every user, inclusive some properties of only those Authorities of the user that have a name that is also in certainNames. From the remaining sequence of selected users keep only those users that have at least one authority left

EF Core 2.0/2.1 - How to efficiently handle large, infrequently accessed columns?

I have a table as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOB VARBINARY(MAX) NULL
)
With an Entity defined as:
public class Entity
{
public int Id {get;set;}
public string Name {get;set;}
public virtual byte[] LargeBlob {get;set;}
}
99% of my use cases involve displaying ID and NAME only.
1% of the time I need LARGEBLOB.
Is there any way I can mark LargeBlob as Lazily Loaded so to avoid
huge wasted data transfers? Alternatively, are there other ways of
achieving the same outcome?
I tried splitting into 2 tables with a 1->[0|1] relationship as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOBID INT NULL
)
CREATE TABLE MySubTable
(
ID INT PRIMARY KEY,
LARGEBLOB VARBINARY(MAX) NOT NULL
)
with entities
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual LargeBlob LargeBlob { get; set; }
}
public class LargeBlob
{
public int Id { get; set; }
public virtual byte[] Blob { get; set; }
}
That did work in so far as lazy loading was concerned, but I tried all manner of inverse relationship / foreign key tags, HasOne, OwnsOne, OnDelete(Cascade) in all kinds of combinations, but I couldn't achieve what I wanted to achieve. Just to recap, that would be:
Blob is loaded only when the LargeBlob property is actually derefenced.
If entity.LargeBlob property gets set to a new LargeBlob, the (now "orphaned" ) old LargeBlob gets deleted from the database.
If the entity gets deleted, the related large blob gets deleted.
Quick Update re: versions &c
Note: I'm using VS 2017 15.6.2, .net core 2.0, with EF core 2.1 (to get at least the possibility of some lazy loading). Nuget packages:
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="2.1.0-preview1-final" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0-preview1-final" PrivateAssets="All" />
I tried splitting into 2 tables with a 1->[0|1] relationship as follows
But by putting the FK in the Entity you actually did the opposite - [0|1]->1 relationship.
To get the desired relationship, the FK must be at LargeBlog. It could be a separate property (column), but the most appropriate is to use the Id property as both PK and FK (the so called shared PK association). You can do it with the following fluent configuration:
modelBuilder.Entity<Entity>()
.HasOne(e => e.LargeBlob)
.WithOne()
.HasForeignKey<LargeBlob>(e => e.Id);
Once you do that, since the whole purpose of doing it was to get separate controllable (eager, explicit or lazy when available) load behavior, it can be seen that the separate table is not really needed - the "entity" containing the blob data can be embedded inside the same table using the table splitting which is achieved by simply adding the following to the above configuration:
modelBuilder.Entity<Entity>().ToTable("MyTable");
modelBuilder.Entity<LargeBlob>().ToTable("MyTable");
Note that while the most logical choice seems to be owned type, unfortunately currently owned types are always loaded (similar to EF6 complex types), so they cannot be used to achieve controllable load behavior.
You should only select the columns you need to save bandwidth:
var entity = await dbContext.Entities
.Where(...)
.Select(e => new
{
Id = e.Id,
Name = e.Name,
LargeBlob = null,
})
.FirstOrDefaultAsync();
and whenever you really need the LargeBlob column, load it manually
entity.LargeBlob = await dbContext.Entities
.Where(e => e.Id == entity.Id)
.Select(e => e.LargeBlob)
.SingleOrDefaultAsync();
You can delete an entity without loading the whole entity, just the Id (and the concurrency token, if present on the entity) suffices
var entity = new Entity { Id = removeEntityId };
dbContext.Entities.Remove(entity);
await dbContext.SaveChangesAsync();

Using Inner joins in Entity Framework on multiple tables?

I have a student table in my database with columns Id, Name, DepartmentId, CollegeId, CityId etc and a Department table, College table to map to the DepartmentId, CollegeId in the Student table.
Now I want to pass CityId and retrieve Id, Name, DepartmentName, CollegeName with help of the corresponding tables using inner join based on the CityId.
I'm implementing a method FindAll() which takes in CityId as the input parameter and retrieves data based on CityId
public IEnumerable<StudentRegionEntity> FindAll(int CityId)
{
var totalStudents = new List<StudentRegionEntity>();
foreach(var entity in this.Dbcontext.Students)
{
}
}
In the foreach loop I want to bind the list of students with Id, Name, Department, College fields, how can I use joins and implement the functionality for FindAll()?
var studentEntities = this.Dbcontext.Students
.Include("StudentList").Where(s => s.cityId == CityId).ToList()
this will give the list of all the StudentEntity entities with StudentRegionEntity attached, using one join query to the database.