Possible to implement a conditional join clause - JOIN ON condition1 OR condition2? - c#

Is it possible to implement a conditional join clause in Entity Framework 6? Specifically, INNER JOIN ON (boolean condition1) OR (boolean condition2).
The code below works, but calls the database twice. Is it possible to consolidate it down into one call?
There is a foreign key relationship that ties FirmFeatures.FeatureId to Nullable FirmParameters.FeatureId
var dbContext = new MyEntities();
var feature = dbContext.FirmFeatures
.Where(f => f.FeatureId == featureId)
.First();
var parameters = dbContext.FirmParameters.AsQueryable();
parameters = feature.IsDbTable
? parameters.Where(p => p.FeatureId == null)
: parameters.Where(p => p.FeatureId == featureId);
var list = parameters.ToList()
The SQL call would look something like:
SELECT feature.*, parameter.*
FROM [FirmFeature] AS feature
INNER JOIN [FirmParameter] AS parameter
ON (feature.IsDbTable = 0 AND feature.FeatureId = parameter.FeatureId) OR (feature.IsDbTable = 1 AND parameter.FeatureId IS NULL)
WHERE feature.[FeatureId] = 3
This leveraged database model first.
I'm new to the Entity Framework.
Edit2: I'm hoping to have both a features object and a parameters object loaded from the database as a result of this.
EDIT: As requested, here are the models:
{
public FirmFeature()
{ this.FirmParameters = new HashSet<FirmParameter>(); }
public byte FeatureId { get; set; }
public bool IsDbTable { get; set; }
...
public virtual ICollection<FirmParameter> FirmParameters { get; set; }
}
public partial class FirmParameter
{
public byte ParameterId { get; set; }
public Nullable<byte> FeatureId { get; set; }
...
public virtual FirmFeature FirmFeature { get; set; }
public virtual FirmParameter FirmParameter1 { get; set; }
public virtual FirmParameter FirmParameter2 { get; set; }
}

try giving this a shot:
var isDbTableQuery = dbContext.FirmFeatures.Where(f => f.FeatureId == featureId && f.IsDbTable);
var parameters = dbContext.FirmParameters.Where(p => isDbTableQuery.Any() ? p.FeatureId == null : p.FeatureId == featureId);
var list = parameters.ToList();

I cannot test it right now, but if your only problem is the two round trips you can acomplish that using two LEFT joins, and selecting the appropriate source.
Something like:
var query = from feature in dbContext.FirmFeatures
join parameter0 in dbContext.FirmParameters
on new { IsDbTable = feature.IsDbTable, FeatureId = feature.FeatureId } equals new { IsDbTable = false, FeatureId = parameter0.FeatureId ?? 0 }
into left_parameter_0
from parameter_0 in left_parameter_0.DefaultIfEmpty()
join parameter1 in dbContext.FirmParameters
on new { IsDbTable = feature.IsDbTable, FeatureId = (byte?)null } equals new { IsDbTable = true, FeatureId = parameter1.FeatureId }
into left_parameter_1
from parameter_1 in left_parameter_1.DefaultIfEmpty()
select new { Feature = feature, Parameter = parameter_0 != null ? parameter_0 : parameter_1 };
var list = query.ToList();

You can put the condition in the join statement. I'll do this in query syntax because that always reads far easier with joins:
var q = from f in dbContext.FirmFeatures
where f.FeatureId == featureId
join p in dbContext.FirmParameters on
(f.IsDbTable ? null : f.FeatureId) equals p.FeatureId
select new { p, f };
Or simply:
var q2 = from p in dbContext.FirmParameters.Include(p => p.FirmFeature)
where (p.FirmFeature.FeatureId == featureId && p.FirmFeature.IsDbTable)
|| p.Feature == null
select p;
where you use Include to get FirmParameters having their FirmFeature references loaded (if there are any).

var list = dbContext.FirmParameters
.Where(p => (p.FirmFeature.FeatureId == featureId && p.FirmFeature.IsDbTable) ?
p.FeatureId == null : p.FeatureId == featureId)
.ToList();
UPDATE
var list = dbContext.FirmParameters
.Join(dbContext.FirmFeature, p.FeatureId, f.FeatureId, (p, f) => new { Parameter = p, Feature = f})
.Where(#f => #f.Feature.FeatureId == featureId)
.Where(#p => (#p.Feature.IsDbTable ? #p.Parameter.FeatureId == null : #p.Parameter.FeatureId == featureId))
.Select(#x => new { Feature = #x.Feature, Parameter = #x.Parameter })
.DefaultIfEmpty()
.ToList();

Related

Orderby on left join null value

Following situation:
Table of users
Table of addresses
The user has a single optional reference to the address table (=left join)
The query to fetch the data is:
IQueryable<User> query =
from u in _dbContext.Users
join a in _dbContext.Address on u.AddressId equals a.Id into address
from addresses in address.DefaultIfEmpty()
select new User(u, a);
Now I want to do a sorting on the query based on the municipality of the address
query = query.OrderBy(u => u.Address.Municipality);
The problem is that the Address can be a null value (as the address is optional) and for that reason it is throwing following exception.
NullReferenceException: Object reference not set to an instance of an object.
Is there a way to order on the municipality knowing that it can be null?
Already tried following things with same outcome:
query = query.OrderBy(u => u.Address.Municipality ?? "");
query = query.OrderBy(u => u.Address == null).ThenBy(u => u.Address.Municipality);
You can use
query = query.OrderBy(u => u.Address.Municipality.HasValue);
You can write your comparer like this:
public class One
{
public int A { get; set; }
}
public class Two
{
public string S { get; set; }
}
public class T
{
public One One { get; set; }
public Two Two { get; set; }
}
public class OrderComparer : IComparer<Two>
{
public int Compare(Two x, Two y)
{
if (((x == null) || (x.S == null)) && ((y == null) || (y.S == null)))
return 0;
if ((x == null) || (x.S == null))
return -1;
if ((y == null) || (y.S == null))
return 1;
return x.S.CompareTo(y.S);
}
}
static void Main(string[] args)
{
var collection = new List<T> {
new T{ One = new One{A=1}, Two = new Two{ S="dd" } },
new T{ One = new One{A=5}, Two = null },
new T{ One = new One{A=0}, Two = new Two{ S=null } },
new T{ One = new One{A=6}, Two = new Two{ S="aa" } },
};
var comparer = new OrderComparer();
collection = collection.OrderBy(e => e.Two, comparer).ToList();
}
But in your case you have to write var collection = query.AsEnumerable().OrderBy(x=>x.Address).
Also there is other method:
var result = query.Where(x=>x.Address==null || x.Address.Municipality==null)
.Union(query.Where(x.Address!=null && x.Address.Municipality!=null).OrderBy(x=>x.Address.Municipality));
I create a simple demo and it works well when I add nullable foreign key to the two tables.
Besides, I do not understabd what is select new User(u, a); in your code.
Below is my sample code:
Models:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Address")]
public int? AddressId { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string AddressName { get; set; }
public string Municipality { get; set; }
}
Action:
IQueryable<User> query =
from u in _context.Users
join a in _context.Address on u.AddressId equals a.Id into address
from addresses in address.DefaultIfEmpty()
select new User
{
Id = u.Id,
Name = u.Name,
AddressId = u.AddressId,
Address = addresses
};
query = query.OrderBy(u => u.Address.Municipality);

Store LINQ result into view-model refereeing to multiple class objects instead of storing LINQ result in var

I have linq statement that gives three class object data
1- appForm
2- ebsSync
3- SyncAuditLog
var _AppForms2 = (from appForm in _uof.Web_AppFormsRepository.GetAll()
join syncAuditLog in (_uof.Web_SyncAuditLogRepository.GetAll().
Where(sal => sal.LOG_STATUS.Equals("EP") &&
sal.LOOKUP_ID != null &&
sal.ID == maxAuditID)
.Select(shortListedAuditLog => new { shortListedAuditLog })) on appForm.SUBMISSION_ID equals syncAuditLog.shortListedAuditLog.SUBMISSION_ID
join ebsSync in _uof.Web_EBS_SyncRepository.GetAll() on appForm.SUBMISSION_ID equals ebsSync.SUBMISSION_ID
select new {appForm , ebsSync, syncAuditLog }).ToList();
I want to store this LINQ query result into viewModel as instead of 'var _AppForms';
public class WebSyncSummaryEntity
{
public List<Web_AppFormsEntity> AppFormsEntity { get; set; }
public List<Web_EBS_SyncEntity> EBS_SyncEntity { get; set; }
public List<Web_SyncAuditLogEntity> SyncAuditLogEntity { get; set; }
}
Need to fix here!
public List<WebSyncSummaryEntity> GetWebSyncSummary()
{
List<WebSyncSummaryEntity> _AppForms = null;
using (var _uof = new UCAS_WebSync_AdminTool_UOF())
{
var maxAuditID = (from sal in _uof.Web_SyncAuditLogRepository.GetAll().Where(
sal => sal.LOG_STATUS.Equals("EP") &&
sal.LOOKUP_ID != null)
select sal).Max(x => x.ID);
_AppForms = (from appForm in _uof.Web_AppFormsRepository.GetAll()
join syncAuditLog in (_uof.Web_SyncAuditLogRepository.GetAll().
Where(sal => sal.LOG_STATUS.Equals("EP") &&
sal.LOOKUP_ID != null &&
sal.ID == maxAuditID)
.Select(shortListedAuditLog => new { shortListedAuditLog })) on appForm.SUBMISSION_ID equals syncAuditLog.shortListedAuditLog.SUBMISSION_ID
join ebsSync in _uof.Web_EBS_SyncRepository.GetAll() on appForm.SUBMISSION_ID equals ebsSync.SUBMISSION_ID
select new {appForm , ebsSync, syncAuditLog }).ToList();
var test = "d";
}
return _AppForms;
}
Change your view model
public class WebSyncSummaryEntity
{
public Web_AppFormsEntity AppFormsEntity { get; set; }
public Web_EBS_SyncEntity EBS_SyncEntity { get; set; }
public Web_SyncAuditLogEntity SyncAuditLogEntity { get; set; }
}
and then
var _AppForms2 = (from appForm in _uof.Web_AppFormsRepository.GetAll()
join syncAuditLog in (_uof.Web_SyncAuditLogRepository.GetAll().
Where(sal => sal.LOG_STATUS.Equals("EP") &&
sal.LOOKUP_ID != null &&
sal.ID == maxAuditID)
.Select(shortListedAuditLog => new { shortListedAuditLog })) on appForm.SUBMISSION_ID equals syncAuditLog.shortListedAuditLog.SUBMISSION_ID
join ebsSync in _uof.Web_EBS_SyncRepository.GetAll() on appForm.SUBMISSION_ID equals ebsSync.SUBMISSION_ID
select new WebSyncSummaryEntity {AppFormsEntity =appForm , EBS_SyncEntity =ebsSync, SyncAuditLogEntity =syncAuditLog }).ToList();
I haven't tested the code, just suggesting you a way to go, looking for better solution.

Union of two lists in LINQ not working

I've a method where I'll show the books which are present in the db and the ones which are given for lent.
When the user doesn't returns the book,the book becomes inactive.So I've written a LINQ query where the new users can select a book.Then if the the user selects a lost book.He will be thrown a message that the book is not available.
Here I am using the Union to find the books which are active and the books which are lent and not return.
But the duplicate values are returning in method.
public IEnumerable<UsageType> GetUsageType(int BookID = 0)
{
_db.Configuration.ProxyCreationEnabled = false;
List<UsageType> Uresult = new List<UsageType>();
List<UsageType> result = new List<UsageType>();
try
{
if (BookID == 0)
{
result = (from Usage in _db.USAGE_TYPE
where Usage.IS_ACTIVE == true
select new
{
UsageTypeId = Usage.USAGE_TYPE_ID,
UsageTypeName = Usage.USAGE_TYPE_NAME,
IsActive = Usage.IS_ACTIVE
}).AsEnumerable()
.Select(x => new UsageType
{
UsageTypeId = x.UsageTypeId,
UsageTypeName = x.UsageTypeName,
IsActive = x.IsActive.HasValue ? x.IsActive.Value : false
}).Distinct().ToList();
}
else
{
result = (from Usage in _db.USAGE_TYPE
where Usage.IS_ACTIVE == true
select new
{
UsageTypeId = Usage.USAGE_TYPE_ID,
UsageTypeName = Usage.USAGE_TYPE_NAME,
IsActive = Usage.IS_ACTIVE
}).AsEnumerable()
.Select(x => new UsageType
{
UsageTypeId = x.UsageTypeId,
UsageTypeName = x.UsageTypeName,
IsActive = x.IsActive.HasValue ? x.IsActive.Value : false
}).ToList();
var savedUsagetype = (from Usage in _db.USAGE_TYPE
join usageCategory in _db.USAGE_CATEGORY on Usage.USAGE_TYPE_ID equals usageCategory.USAGE_TYPE_ID
join book_usage in _db.BOOK_USAGE
on usageCategory.USAGE_CATEGORY_ID equals book_usage.USAGE_CATEGORY_ID
where book_usage.Book_ID == BookID
select new
{
UsageTypeId = Usage.USAGE_TYPE_ID,
UsageTypeName = Usage.USAGE_TYPE_NAME,
IsActive = Usage.IS_ACTIVE
}).AsEnumerable()
.Select(x => new UsageType
{
UsageTypeId = x.UsageTypeId,
UsageTypeName = x.UsageTypeName,
IsActive = x.IsActive.HasValue ? x.IsActive.Value : false
}).ToList();
Uresult= result.Union(savedUsagetype).Distinct().ToList();
}
return Uresult;
}
catch (Exception ex)
{
return null;
}
}
You are comparing objects of your own class so you should implement IEquatable in your UsageType class to tell linq how to compare your objects. For example:
public class UsageType : IEquatable<UsageType>
{
public int UsageTypeId {get; set;}
...
public bool Equals(UsageType other)
{
return this.UsageTypeId == other.UsageTypeId;
}
public override bool Equals(object other)
{
return this.UsageTypeId == ((UsageType)other).UsageTypeId;
}
public override int GetHashCode()
{
return this.UsageTypeId.GetHashCode();
}
}
Now you can use UsageType to get an instance of IEqualityComparer<UsageType>:
Uresult = result.Union(savedUsageType,IEqualityComparer<UsageType>.Default).ToList();
Note: Always override object.Equals and object.GetHashcode() too. If you don't, old code which still uses the non-generic Enumerable will give unexpected results because they use other logic to compare than you'd expect.

Linq to entities: finding matches

I have these two EF classes:
class Row
{
public long CategoryId { get; set; }
public virtual Category Category { get; set; }
public long VesselId { get; set; }
public virtual Vessel Vessel { get; set; }
public int TruckType { get; set; }
}
class RowFilter
{
public long? CategoryId { get; set; }
public virtual Category Category { get; set; }
public long? VesselId { get; set; }
public virtual Vessel Vessel { get; set; }
public int? TruckType { get; set; }
public long? PortId { get; set; }
public virtual Port Port { get; set; }
public bool IsMatch(Row row)
{
if (CategoryId == null || CategoryId == row.CategoryId) {
if (VesselId == null || VesselId == row.VesselId) {
if (TruckType == null || TruckType == row.TruckType) {
if (PortId == null || PortId == row.Vessel.PortId) {
return true;
}
}
}
}
return false;
}
}
That is:
A Filter matches a Row if IsMatch() returns true for that row.
I have a list of rows, in an IQueryable manner:
var rows = dbContext.Rows.AsQueryable().Where(...);
...and for each row, I want to select (project) the row itself and the list of filters that match this row. I can do this easily in a Linq-to-Objects way ("in memory"):
// Linq-to-objects
rows.ToList().Select(r => new
{
row = r,
filters = dbContext.RowsFilters.Where(f => f.IsMatch(r))
};
Question is... is it possible to do it with Linq-to-Entities? (sql, not "in memory")
In a static world, I would have these navigation properties:
class Row
{
...
public virtual List<RowFilter> RowFilters { get; set; }
}
class RowFilter
{
...
public virtual List<Rows> Rows { get; set; }
}
but... that means a lot of updating: when creating a new RowFilter, when creating a new Row, etc.
You can do the following steps:
Modify the IsMatch method to return a Expression<Func<Row, bool>> type and implement it like this :
public Expression<Func<Row, bool>> IsMatch()
{
Expression<Func<Row, bool>> filter = r => (CategoryId == null || CategoryId == r.CategoryId)
&& (VesselId == null || VesselId == r.VesselId)
&& (TruckType == null || TruckType == r.TruckType)
&& (PortId == null || PortId == r.PortId);
return filter;
}
Then just use it like this :
var rowFilter = new RowFilter { PortId = 1, CategoryId = 2, TruckType = 3, VesselId = 4 };
var query = context.Rows.Where(rowFilter.IsMatch());
All the linq are translated into SQL then executed on the server side. The generated SQL by EF looks like the following:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[CategoryId] AS [CategoryId],
[Extent1].[VesselId] AS [VesselId],
[Extent1].[TruckType] AS [TruckType],
[Extent1].[PortId] AS [PortId]
FROM [dbo].[Rows] AS [Extent1]
WHERE (#p__linq__0 IS NULL OR #p__linq__1 = [Extent1].[CategoryId]) AND (#p__linq__2 IS NULL OR #p__linq__3 = [Extent1].[VesselId]) AND (#p__linq__4 IS NULL OR #p__linq__5 = [Extent1].[TruckType]) AND (#p__linq__6 IS NULL OR #p__linq__7 = CAST( [Extent1].[PortId] AS bigint))
You can use the following query:
var query = from r in context.Rows
from f in context.RowFilters.Where(f =>
(f.CategoryId == null || f.CategoryId == r.CategoryId) &&
(f.VesselId == null || f.VesselId == r.VesselId) &&
(f.TruckType == null || f.TruckType == r.TruckType) &&
(f.PortId == null || f.PortId == r.Vessel.PortId))
.DefaultIfEmpty()
let x = new {r, f}
group x by x.r
into gr
select new
{
row = gr.Key,
filters = gr.Select(y => y.f).Where(yf => yf != null)
};
var result = query.ToList();
Here is an alternative syntax:
var query = context.Rows
.SelectMany(r =>
context.RowFilters.Where(f =>
(f.CategoryId == null || f.CategoryId == r.CategoryId) &&
(f.VesselId == null || f.VesselId == r.VesselId) &&
(f.TruckType == null || f.TruckType == r.TruckType) &&
(f.PortId == null || f.PortId == r.Vessel.PortId))
.DefaultIfEmpty()
.Select(f => new {r, f}))
.GroupBy(x => x.r)
.Select(x => new
{
row = x.Key,
filters = x.Select(y => y.f).Where(yf => yf != null)
});

ASP.NET MVC 5 Entity Join

I'm new in ASP, Entity and lambda expressions. How can I join two tables?
Route Model:
public partial class Route
{
public Route()
{
Flights = new HashSet<Flight>();
}
public int RouteID { get; set; }
public int DepartureAirportID { get; set; }
public int ArrivalAirportID { get; set; }
public int FlightDuration { get; set; }
public virtual Airport Airport { get; set; }
public virtual Airport Airport1 { get; set; }
public virtual ICollection<Flight> Flights { get; set; }
}
Airport Model:
public partial class Airport
{
public Airport()
{
Routes = new HashSet<Route>();
Routes1 = new HashSet<Route>();
}
public int AirportID { get; set; }
public string City { get; set; }
public string Code { get; set; }
public virtual ICollection<Route> Routes { get; set; }
public virtual ICollection<Route> Routes1 { get; set; }
}
SQL query looks like this:
SELECT a.AirportID, a.City
FROM Route r INNER JOIN Airport a ON r.ArrivalAirportID = a.AirportID
WHERE r.DepartureAirportID = #departureAirportID
ORDER BY a.City
Sorry for this easy question but I don't know how to do this with Entity Framework...
Something like this should do (untested and just going on from your query) with a variable hard-coded):
using (var db = new YourDbContext())
{
var query = from r in db.Route
join a in db.Airport a on r.ArrivalAirportID equals a.AirportID
where r.DepartureAirportID = 1 // replace with your varialble.
orderby a.City
select a;
}
Include with join entity framework. here doctorSendAnswerModel also a inner table.
var data = _patientaskquestionRepository.Table.Include(x=>x.DoctorSendAnswer).Join(_patientRepository.Table, a => a.PatientId, d => d.Id, (a, d) => new { d = d, a = a }).Where(x => x.a.DoctorId == doctorid);
if(!string.IsNullOrEmpty(status))
data=data.Where(x=>x.a.Status==status);
var result = data.Select(x => new {x= x.a,y=x.d }).ToList();
var dt = result.Select(x => new PatientAskQuestionModel()
{
PatientId = x.x.PatientId.Value,
AskQuestion = x.x.AskQuestion,
Id = x.x.Id,
DoctorId = x.x.DoctorId,
FileAttachment1Url = x.x.FileAttachment1,
DocName = x.y.FirstName + " " + x.y.LastName,
CreatedDate = x.x.CreatedDate.Value,
doctorSendAnswerModel = x.x.DoctorSendAnswer.Select(t => new DoctorSendAnswerModel { Answer = t.Answer }).ToList()
}).ToList();
return dt;
LinQ query:
from r in context.Route
join a in context.Airport
on r.ArrivalAirportID equals a.AirportID
WHERE r.DepartureAirportID = "value"
ORDER BY a.City
select a.AirportID, a.City
var balance = (from a in context.Airport
join c in context.Route on a.ArrivalAirportID equals c.AirportID
where c.DepartureAirportID == #departureAirportID
select a.AirportID)
.SingleOrDefault();
You can do the following:
var matches = from a in context.Airports
join r in context.Routes
on a.AirportID equals r.ArrivalAirportID
where r.DepartureAirportID = departureAirportID
order by a.City
select new
{
a.AirportID,
a.City
};
Entity query with conditional join with pagination.
if (pageIndex <= 0)
pageIndex = 1;
pageIndex = ((pageIndex - 1) * pageSize) ;
var patient = _patientRepository.Table.Join(_DoctorPatient.Table.Where(x => x.DoctorId == Id && x.IsBlocked==false), x => x.Id, d => d.PatientId, (x, d) => new { x = x });
if (state != "")
patient = patient.Where(x => x.x.State.Contains(state));
if (name != "")
patient = patient.Where(x => (x.x.FirstName + x.x.LastName).Contains(name));
if (sdate != null)
patient = patient.Where(x => x.x.CreatedDate >= sdate);
if (eDate != null)
patient = patient.Where(x => x.x.CreatedDate <= eDate);
var result = patient.Select(x => x.x).Select(x => new PatientDoctorVM() { PatientId = x.Id, Id = x.Id, FirstName = x.FirstName, LastName = x.LastName, SSN = x.NewSSNNo, UserProfileId = x.UserProfileId, Email = x.Email, TumbImagePath = x.TumbImagePath }).OrderBy(x => x.Id).Skip(pageIndex).Take(pageSize).ToList();
Your entity and lembda query will be lool like this:
return (from d in _doctorRepository.Table
join p in _patientDoctor.Table on d.Id equals p.DoctorId
where p.PatientId == patientid.Value select d
).ToList();
Take a look at this site, it will explain you how the join works in Linq.
So if you ever need it again you will be able to solve it yourself.

Categories