Can I convert this extension method to use IDbSet<> instead of DbContext? - entity-framework

Currently I use the below method like this: db.GetProjectsAllowed(profileId, profOrgList, projectList). I would like to convert this to use IDbSet<Project>, but I'm not sure how to get the second LINQ query.
public static IQueryable<Project> GetProjectsAllowed
(
this IMkpContext db,
Guid profileId,
List<Guid> profOrgIds = null,
List<Guid> projectIds = null
)
{
var projects =
(
from p in db.Project
.Include(p => p.Proposals)
.Include(p => p.RoleAssignments)
.Include("RoleAssignments.AssigneeSnapshot")
where p.IsActive
select p);
if (profOrgIds != null && profOrgIds.Any())
{
var profileIds = db.ProfileOrganization
.Where(po => po.IsActive && profOrgIds.Contains(po.OrganizationId))
.Select(po => po.ProfileId);
projects = projects.Where(p => profileIds.Contains(p.CreatedById));
}
if (projectIds != null && projectIds.Any())
projects = projects.Where(proj => projectIds.Contains(proj.ProjectId));
return projects;//.ToList();
}
Can I convert this to use IDbSet<Project> or not?

Here, why not split this into two extension methods? This makes your GetProjectsAllowed extension method more cohesive and single responsible.
First:
public static IEnumerable<Guid> GetProfileIds(
this IDbSet<ProfileOrganization> profileOrganizations,
IEnumerable<Guid> profOrgIds = null)
{
return profOrgIds == null ? null :
from po in profileOrganizations
where po.IsActive
where profOrgIds.Contains(po.OrganizationId)
select po.OrganizationId;
}
And second:
public static IQueryable<Project> GetProjectsAllowed(
this IDbSet<Project> projects,
IEnumerable<Guid> profileIds,
IEnumerable<Guid> projectIds = null)
{
var activeProjects =
from project in projects
//.Include(..
where project.IsActive
select project;
if (profileIds != null && profileIds.Any())
{
activeProjects = activeProjects.Where(p => profileIds.Contains(p.CreatedById));
}
if (projectIds != null && projectIds.Any())
{
activeProjects = activeProjects.Where(proj => projectIds.Contains(proj.ProjectId));
}
return activeProjects;//.ToList();
}
And then the consumer can call it like this:
var profileIds = db.ProfileOrganization.GetProfileIds(profOrgIds);
var projectsAllowed = db.Projects.GetProjectsAllowed(profileIds, projectIds);

Related

System.Linq.Queryable.Except is not supported in Entity Framework Core

Please note this error message :
System.NotSupportedException: 'Could not parse expression
'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
This overload of the method 'System.Linq.Queryable.Except' is
currently not supported.'
Is supported in newer versions of ef core?
my code :
var tuple = SearchBrand(searchTerm);
var result = GetExceptionsBrand(tuple.Item1, categoryId);
return Json(new
{
iTotalDisplayRecords = tuple.Item2,
iDisplayedBrand = result
.Skip(page * 10)
.Take(10)
.ToList(),
});
public async Task<Tuple<IQueryable<BrandDto>, int, int>>SearchBrand(string searchTerm)
{
var result = _context.Brands
.Where(c => c.IsDeleted == displayIsDeleted)
.WhereDynamic(searchTerm)
return new Tuple<IQueryable<BrandDto>, int, int>(result,
filteredResultsCount, totalResultsCount);
}
public IQueryable<BrandDto> GetExceptionsBrand(IEnumerable<BrandDto> filteredBrand, int categoryId)
{
var query = _context.CategoriesBrands.Where(x => x.CategoryId == categoryId);
var selectedList = new List<BrandDto>();
foreach (var item in query)
{
var cb = new BrandDto()
{
BrandDto_BrandId = item.BrandId
};
selectedList.Add(cb);
}
IQueryable<BrandDto> ExcpetList = filteredBrand.AsQueryable().Except(selectedList, new ComparerBrand());
return ExcpetList;
}

Dynamic Search Using Entity Framework

I have a search screen with optional fields using Entity Framework, I want to build a dynamic query without selecting the object and filter on it.
I want to Optimize the following Existing Search, I don't want to select "var query = from p in context.APLCN_TRCKR select p;" at this stage because the application will be used by more than 100 people at once:
using (var context = new VASTEntities())
{
var query = from p in context.APLCN_TRCKR select p;
if (!string.IsNullOrEmpty(searchObj.CUST_NAME_X))
query = query.Where(p => p.CUST_NAME_X == searchObj.CUST_NAME_X.Trim());
if (!string.IsNullOrEmpty(searchObj.SURNM_X))
query = query.Where(p => p.CUST_SURNM_X == searchObj.SURNM_X.Trim());
if (!string.IsNullOrEmpty(searchObj.QUEUE_ID))
query = query.Where(p => p.QUEUE_ID == searchObj.QUEUE_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.BP_ID))
query = query.Where(p => p.BPID == searchObj.BP_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.UserID))
query = query.Where(p => p.CURR_OWNR_USER_ID == searchObj.UserID.Trim());
if (!string.IsNullOrEmpty(searchObj.APLCN_TRCKR_ID))
query = query.Where(p => p.APLCN_TRCKR_ID == searchObj.APLCN_TRCKR_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.APLCN_STTS_ID))
query = query.Where(p => p.APLCN_STTS_ID == searchObj.APLCN_STTS_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.CUST_ID))
query = query.Where(p => p.CUST_ID == searchObj.CUST_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.CELL_ID))
query = query.Where(p => p.CELL_ID == searchObj.CELL_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.ORIGN_ID))
query = query.Where(p => p.ORIGN_ID == searchObj.ORIGN_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.ORGTN_CHANL_ID))
query = query.Where(p => p.ORGTN_CHANL_ID == searchObj.ORGTN_CHANL_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.CR_DCSN_ID))
query = query.Where(p => p.CR_DCSN_ID == searchObj.CR_DCSN_ID.Trim());
if (!string.IsNullOrEmpty(searchObj.SBSA_CUST_I))
query = query.Where(p => p.SBSA_CUST_I == searchObj.SBSA_CUST_I.Trim());
if (!string.IsNullOrEmpty(searchObj.USER_ID_APP_CRTD))
query = query.Where(p => p.USER_ID_APP_CRTD == searchObj.USER_ID_APP_CRTD.Trim());
if (!string.IsNullOrEmpty(searchObj.RGION_ID))
{
int r = int.Parse(searchObj.RGION_ID.Trim());
query = query.Where(p => p.RGION_ID == r);
}
if (!string.IsNullOrEmpty(searchObj.CR_REGION))
{
int x = int.Parse(searchObj.CR_REGION);
if (x == 0)
{
// check 0 - not applicable or null
query = query.Where(p => p.CR_REGION_ID == 0 || p.CR_REGION_ID == null);
}
else
{
query = query.Where(p => p.CR_REGION_ID == x);
}
}
if (!string.IsNullOrEmpty(searchObj.Process_Type))
query = query.Where(p => p.PRCES_TYPE_ID == searchObj.Process_Type.Trim());
query.ToList();
foreach (var a in query)
{
searchAppsObj.Add(Translator.TranslateReqObjToBO.TranslateDTOToSearchApp(a));
}
if (query.Count() == 0)
{
throw new Exception("No Applications Found.");
}
context.Connection.Close();
return searchAppsObj;
}
I want to do something like this but this one is not working properly:
string cust_name_x = "", surname_x = "", queue_id = "", bp_id = "", user_id = "", aplcn_trckr_id = "",
aplcn_stts_id = "", cust_id = "", process_type = "", cr_region = "", cell_id = "", Origin = "", region = "", channel = "", credit_verdict = "", sbsa_cust_id = "", app_creator_id = "";
if (!string.IsNullOrEmpty(searchObj.CUST_NAME_X))
cust_name_x = searchObj.CUST_NAME_X.Trim();
if (!string.IsNullOrEmpty(searchObj.SURNM_X))
surname_x = searchObj.SURNM_X.Trim();
if (!string.IsNullOrEmpty(searchObj.QUEUE_ID))
queue_id = searchObj.QUEUE_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.BP_ID))
bp_id = searchObj.BP_ID;
if (!string.IsNullOrEmpty(searchObj.UserID))
user_id = searchObj.UserID.Trim();
if (!string.IsNullOrEmpty(searchObj.APLCN_TRCKR_ID))
aplcn_trckr_id = searchObj.APLCN_TRCKR_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.APLCN_STTS_ID))
aplcn_stts_id = searchObj.APLCN_STTS_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.CUST_ID))
cust_id = searchObj.CUST_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.Process_Type))
process_type = searchObj.Process_Type.Trim();
if (!string.IsNullOrEmpty(searchObj.CR_REGION))
cr_region = searchObj.CR_REGION.Trim();
if (!string.IsNullOrEmpty(searchObj.CELL_ID))
cell_id = searchObj.CELL_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.ORIGN_ID))
Origin = searchObj.ORIGN_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.RGION_ID))
region = searchObj.RGION_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.ORGTN_CHANL_ID))
channel = searchObj.ORGTN_CHANL_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.CR_DCSN_ID))
credit_verdict = searchObj.CR_DCSN_ID.Trim();
if (!string.IsNullOrEmpty(searchObj.SBSA_CUST_I))
sbsa_cust_id = searchObj.SBSA_CUST_I.Trim();
if (!string.IsNullOrEmpty(searchObj.USER_ID_APP_CRTD))
app_creator_id = searchObj.USER_ID_APP_CRTD.Trim();
using (var context = new VASTEntities())
{
var query = from p in context.APLCN_TRCKR
where
p.CUST_NAME_X.Contains(cust_name_x) &&
p.CUST_SURNM_X.Contains(surname_x) &&
p.QUEUE_ID.Contains(queue_id) &&
p.BPID.Contains(bp_id) &&
p.CURR_OWNR_USER_ID.Contains(user_id) &&
p.APLCN_TRCKR_ID.Contains(aplcn_trckr_id) &&
p.APLCN_STTS_ID.Contains(aplcn_stts_id) &&
p.CUST_ID.Contains(cust_id) &&
p.PRCES_TYPE_ID.Contains(process_type) &&
p.CELL_ID.Contains(cell_id) &&
SqlFunctions.StringConvert((double)p.CR_REGION_ID).Contains(cr_region) &&
p.ORIGN_ID.Contains(Origin) &&
SqlFunctions.StringConvert((double)p.RGION_ID).Contains(region) &&
p.ORGTN_CHANL_ID.Contains(channel) &&
p.CR_DCSN_ID.Contains(credit_verdict) &&
p.SBSA_CUST_I.Contains(sbsa_cust_id)
select p;
query.ToList();
if (query.Count() == 0)
{
throw new Exception("No Applications Found.");
}
foreach (var a in query)
{
searchAppsObj.Add(Translator.TranslateReqObjToBO.TranslateDTOToSearchApp(a));
}
context.Connection.Close();
return searchAppsObj;
}
You can just create a collection of lambda expression like below:
var filters = new List<Expression<Func<Application, bool>>>();
if (!string.IsNullOrWhitespace(searchObj.CUST_NAME_X))
filters.Add(application => application .CUST_NAME_X.Contains(searchObj.CUST_NAME_X.Trim());
if (!string.IsNullOrEmpty(searchObj.SURNM_X))
filters.Add(application => application .CUST_SURNM_X.Contains(searchObj.SURNM_X.Trim());
// And so on for all criteria
After that you can do a loop on filters like below:
using (var context = new VASTEntities())
{
var query = context.APLCN_TRCKR;
foreach(var filter in filters)
{
query = query.Where(filter);
}
var result = query.ToList();
if (result.Count() == 0)
{
throw new Exception("No Applications Found.");
}
foreach (var a in result)
{
searchAppsObj.Add(Translator.TranslateReqObjToBO.TranslateDTOToSearchApp(a));
}
context.Connection.Close();
return searchAppsObj;
}
change
var query = from p in context.APLCN_TRCKR select p;
to
var query = context.APLCN_TRCKR.AsQueryable();
And when the filtering work is done:
await query.ToListAsync() // this one goes to the database
Also have a look at this: Linq query filtering
In Addition: don't call context.Connection.Close(); as this is going to be executed anyway because using behaves like try { ... } finally { // dispose work }

Entity Framework - LINQ - Use Expressions in Select

I am using within my code some EF LINQ expressions to keep complex queries over my model in one place:
public static IQueryable<User> ToCheck(this IQueryable<User> queryable, int age, bool valueToCheck = true)
{
return queryable.Where(ToBeReviewed(age, valueToCheck));
}
public static Expression<Func<User, bool>> ToCheck(int age, bool valueToCheck = true)
{
return au => au.Status == UserStatus.Inactive
|| au.Status == UserStatus.Active &&
au.Age.HasValue && au.Age.Value > age;
}
I am then able to use them in queries:
var globalQuery = db.Users.ToCheck(value);
And also in selects:
var func = EntityExtensions.ToCheck(value);
var q = db.Department.Select(d => new
{
OrdersTotal = d.Orders.Sum(o => o.Price),
ToCheck = d.Users.AsQueryable().Count(func),
})
What I am trying to achieve is to actually use the same expression/function within a select, to evaluate it for each row.
var usersQuery = query.Select(au => new {
Id = au.Id,
Email = au.Email,
Status = au.Status.ToString(),
ToBeChecked = ???, // USE FUNCTION HERE
CreationTime = au.CreationTime,
LastLoginTime = au.LastLoginTime,
});
I am pretty that threre would be a way using plain EF capabilities or LINQKit, but can't find it.
Answering my own question :)
As pointed by #ivan-stoev, the use of Linqkit was the solution:
var globalQueryfilter = db.Users.AsExpandable.Where(au => au.Department == "hq");
var func = EntityExtensions.ToCheck(value);
var usersQuery = globalQueryfilter.Select(au => new
{
Id = au.Id,
Email = au.Email,
Status = au.Status.ToString(),
ToBeChecked = func.Invoke(au),
CreationTime = au.CreationTime,
LastLoginTime = au.LastLoginTime,
});
return appUsersQuery;
It's required to use the AsExpandable extension method from Linqkit along with Invoke with the function in the select method.
I want to add one more example:
Expression<Func<AddressObject, string, string>> selectExpr = (n, a) => n == null ? "[no address]" : n.OFFNAME + a;
var result = context.AddressObjects.AsExpandable().Select(addressObject => selectExpr.Invoke(addressObject, "1"));
Also, expression can be static in a helper.
p.s. please not forget to add "using LinqKit;" and use "AsExpandable".

Property or indexer 'AnonymousType#1.FilePath' cannot be assigned to

Basically I retrieved the records from the table and wanted to updated one column.
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.Select(c => new { c.FilePath, c.FileName })
.ToList();
So this query has only two fields: FilePath and FileName, then next I want to assign FilePath = null;
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();
Then I got an error:
Property or indexer 'AnonymousType#1.FilePath' cannot be assigned to -- it is read only
You select from query anonymous class. You cannot set properties of anonymous class. To do what you want you should get whole entity:
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.ToList();
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();

How can I access other then the "General" attributes of a Entity-Property from a EntityModel within a T4?

I am using sort of the following code to get all properties of a entity
IList<EdmProperty> list = entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity)
Then I iterate through these list, access each Property and read the Property Properties (Yeah, much properties, hope no one gets to confused).
While I can easily access the General attributes I don't know how to access the other Properties of the the Entity-Property like Max Length & Fixed Length
Those properties are not part of PrimitiveType. They are directly in p.TypeUsage under Facets property.
Try the following code:
var MaxLength = (property as EdmMember).TypeUsage.Facets.Where(f => f.Name == "MaxLength").SingleOrDefault();
int maxLength = -1;
if(MaxLength != null)
maxLength = (int)MaxLength.Value;
You can use the maxLength variable in the template code. Any other facet can be accessed in a similar way.
protected void RecognizeByMetadata(IList<Facet> facets)
{
//Dictionary<string, string> attributes = new Dictionary<string, string>();
//facets.AsParallel().ForAll(x => attributes.Add(x.Name, x.Value + ""));
try{
var t = facets.Where(x => x.Name == "MaxLength").Select(x => x.Value).FirstOrDefault();
if (t != null)
{
string typ = t.GetType().FullName;
this.isMax = (t.ToString() == "Max");
if (!isMax)
this.MaxLength = (int?)t;
}
else
{
isMax = false;
MaxLength = null;
}
this.IsNullable = (bool?)facets.Where(x => x.Name == "Nullable").Select(x => x.Value).FirstOrDefault();
this.Defaultvalue = facets.Where(x => x.Name == "DefaultValue").Select(x => x.Value).FirstOrDefault();
this.IsUnicode = (bool?)facets.Where(x => x.Name == "Unicode").Select(x => x.Value).FirstOrDefault();
this.IsFixedlength = (bool?)facets.Where(x => x.Name == "FixedLength").Select(x => x.Value).FirstOrDefault();
//string precision = facets.Where(x => x.Name == "Precision").Select(x => x.Value + "").FirstOrDefault();
//string scale = facets.Where(x => x.Name == "Scale").Select(x => x.Value + "").FirstOrDefault();
isRecognized = true;
recognizeUnique();
} catch (Exception e)
{
string mewssage = e.Message;
throw;
}
}