Build dynamic select using LINQ with nested class - linq

I am trying to create a dynamic select builder that includes also nested class.
Basically I have entity class DB then DTO like in the following classes
/// <summary>
/// Base entity class
/// </summary>
public abstract class BaseEntity
{ }
/// <summary>
/// EF Schema entity
/// </summary>
public class AMOS_AMOSUSER : BaseEntity
{
public decimal USERID { get; set; }
public decimal? SUPERIORID { get; set; }
public decimal? EMPLOYEEID { get; set; }
public virtual AMOS_AMOSUSER SUPERIOR { get; set; }
public string LOGINID { get; set; }
public string NAME { get; set; }
public string COMMENT1 { get; set; }
public decimal ACCOUNTDISABLED { get; set; }
public DateTime? LASTLOGIN { get; set; }
}
/// <summary>
/// DTO base for all dtos
/// </summary>
public abstract class DTO_BASE
{
}
/// <summary>
/// DTO amos user base
/// </summary>
public class DTO_AMOSUSER_BASE : DTO_BASE
{
public decimal USERID { get; set; }
public string LOGINID { get; set; }
public string NAME { get; set; }
}
/// <summary>
/// DTO AMOS User
/// </summary>
public class DTO_AMOSUSER : DTO_AMOSUSER_BASE
{
public decimal? SUPERIORID { get; set; }
public virtual DTO_AMOSUSER_BASE SUPERIOR { get; set; }
}
Here below code for testing and creating dynamic select.
public static class TestDynamicSelect
{
public static void TestDynamicSelectDTO()
{
List<AMOS_AMOSUSER> users = new List<AMOS_AMOSUSER>();
PopulateUsers(users);
var q = users.AsQueryable().Select(DynamicSelectGenerator2<AMOS_AMOSUSER, DTO_AMOSUSER>(typeof(DTO_AMOSUSER).GetProperties().Select(p => p.Name)));
var cc = q.ToArray();
foreach (var item in cc)
{
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item));
}
Console.ReadLine();
}
public static Expression<Func<T, TSelect>> DynamicSelectGenerator1<T, TSelect>(string fields)
{
return DynamicSelectGenerator2<T, TSelect>(fields.Split(','));
}
/// <param name="fields">
/// Format1: "Field1"
/// Format2: "Nested1.Field1"
/// Format3: "Field1:Field1Alias"
/// </param>
public static Expression<Func<T, TSelect>> DynamicSelectGenerator2<T, TSelect>(IEnumerable<string> fields)
{
string[] EntityFields;
if (fields == null || fields.Count() == 0)
// get Properties of the T
EntityFields = typeof(T).GetProperties().Select(propertyInfo => propertyInfo.Name).ToArray();
else
EntityFields = fields.ToArray();
// input parameter "x"
var xParameter = Expression.Parameter(typeof(T), "x");
// new statement "new Data()"
var xNew = Expression.New(typeof(TSelect));
// create initializers
var bindings = EntityFields
.Select(x =>
{
string[] xFieldAlias = x.Split(':');
string field = xFieldAlias[0];
string[] fieldSplit = field.Split('.');
if (fieldSplit.Length > 1)
{
// original value "x.Nested.Field1"
Expression exp = xParameter;
foreach (string item in fieldSplit)
exp = Expression.PropertyOrField(exp, item);
// property "Field1"
PropertyInfo member2 = null;
if (xFieldAlias.Length > 1)
member2 = typeof(TSelect).GetProperty(xFieldAlias[1]);
else
member2 = typeof(T).GetProperty(fieldSplit[fieldSplit.Length - 1]);
// set value "Field1 = x.Nested.Field1"
var res = Expression.Bind(member2, exp);
return res;
}
// property "Field1"
var mi = typeof(T).GetProperty(field);
PropertyInfo member;
if (xFieldAlias.Length > 1)
member = typeof(TSelect).GetProperty(xFieldAlias[1]);
else
member = typeof(TSelect).GetProperty(field);
// original value "x.Field1"
if (member != null)
{
if (mi != null)
{
if (typeof(BaseEntity).IsAssignableFrom(mi.PropertyType))
{
List<string> props = new List<string>();
mi.PropertyType.GetProperties().ToList().ForEach(p => {
if (member.PropertyType.GetProperties().Any(c => c.Name.Equals(p.Name, StringComparison.Ordinal)))
props.Add(p.Name);
});
Type ex = typeof(TestDynamicSelect);
MethodInfo mm = ex.GetMethod("DynamicSelectGenerator2");
MethodInfo miConstructed = mm.MakeGenericMethod(mi.PropertyType, member.PropertyType);
var expr = (LambdaExpression)miConstructed.Invoke(null, new object[] { props });
// Probably here is not correct
return Expression.Bind(member, Expression.TypeAs(expr, member.PropertyType));
}
else
{
var xOriginal = Expression.Property(xParameter, mi);
return Expression.Bind(member, xOriginal);
}
}
}
//return Expression.Constant(GetDefault(mi.PropertyType));
throw new Exception("No property to bind");
}
);
var b = bindings.Where(x => x != null && xNew.Type.GetProperties().Any(p => p.Name.Equals(x.Member.Name, StringComparison.OrdinalIgnoreCase)));
// initialization "new Data { Field1 = x.Field1, Field2 = x.Field2 }"
var xInit = Expression.MemberInit(xNew, b);
// expression "x => new Data { Field1 = x.Field1, Field2 = x.Field2 }"
var lambda = Expression.Lambda<Func<T, TSelect>>(xInit, xParameter);
return lambda;
}
}
I can't populate correctly the nested property SUPERIOR which is always null.
Basically what I am trying to do is to populate DTO classes from an Entity class using property name comparison.
Please help.

Related

how to apply generic multiple include with generic repository?

I need to make generic multiple include function as a service with generic repository.
But unfortunately, I get nothing !!
here is my attempt using aggregate linq.
public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
{
GridSetting gs = GetGrid();
IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }
// or
//foreach (var include in includes)
//{
// rows = rows.Include(include);
//}
return rows;
}
when I add debugging point I get that the includes has list of expression
and then here is how I use it
var xxx = _customerService.getRowsWithIncludeMultiple(page: 0, i => i.cityDTO, i => i.ageDTO);
the problem here I get customers without the included things (cityDTO & ageDTO)
let me include here models
public class CustomerDTO
{
public int Id { get; set; }
public string CustName { get; set; }
public string CustJobTitle { get; set; }
public string CustAge { get; set; }
public bool IsManager { get; set; }
// FKs
public int AgeId { get; set; }
public int CityId { get; set; }
public AgeDTO ageDTO { get; set; }
public CityDTO cityDTO { get; set; }
}
public class CityDTO
{
public int Id { get; set; }
public string CityName { get; set; }
public List<CustomerDTO> customerDTO { get; set; }
}
public class AgeDTO
{
public int Id { get; set; }
public int AgeName { get; set; }
public List<CustomerDTO> customerDTO { get; set; }
}
Update ... showing the whole service, usage, and injection
here is the whole generic repository service and how it looks like
public class Repository<TEntity, TEntityDTO> : IRepository<TEntity, TEntityDTO> where TEntity : class where TEntityDTO : class
{
protected readonly AppDbContext _context;
private readonly DbSet<TEntity> _dbSet;
private readonly IMapper _mapper;
public Repository(AppDbContext context, IMapper mapper)
{
_context = context;
_dbSet = context.Set<TEntity>();
_mapper = mapper;
}
// GENERIC CRUD ...
// and then here where i want to focus
public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
{
GridSetting gs = GetGrid();
IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }
// or
//foreach (var include in includes)
//{
// rows = rows.Include(include);
//}
return rows;
}
}
and then here is how customer service uses generic repo
public class CustomerService : Repository<Customer, CustomerDTO>, ICustomerService
{
public CustomerService(AppDbContext db, IMapper mapper) : base(db, mapper) { }
}
finally injection in Program.cs
builder.Services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));
builder.Services.AddScoped<ICustomerService, CustomerService>();
thanks #Svyatoslav Danyliv and thanks for all contributors your answer helped me a lot.
cannot apply Include to DTO object
so, I would to share what I have made so far.
instead of
public IQueryable<TEntityDTO> getRowsWithIncludeMultiple(int page = 0, params Expression<Func<TEntityDTO, object>>[] includes)
{
GridSetting gs = GetGrid();
IEnumerable<TEntity> getPage = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage);
IQueryable<TEntityDTO> rows = _mapper.Map<IEnumerable<TEntityDTO>>(getPage).AsQueryable();
if (includes != null) { rows = includes.Aggregate(rows, (current, include) => current.Include(include)); }
return rows;
}
I applied include to the original entity not to DTO
public IEnumerable<TEntityDTO> IncludeMultiple(int page = 0, params Expression<Func<TEntity, object>>[] includes)
{
GridSetting gs = GetGrid();
IQueryable<TEntity> rows = _dbSet.Skip((page == 0 ? page : page - 1) * gs.ItemsPerPage).Take(gs.ItemsPerPage).AsQueryable();
if (includes != null)
{ rows = includes.Aggregate(rows, (current, include) => current.Include(include).AsQueryable().AsNoTracking()); }
return _mapper.Map<IEnumerable<TEntityDTO>>(rows).AsQueryable();
}
And here is some clarification to make the idea more understandable
public IEnumerable<TEntityDTO> IncludeMultiple(params Expression<Func<TEntity, object>>[] includes)
{
// get IQueryable of TEntity
IQueryable<TEntity> rows = _dbSet.AsQueryable();
// and here using `linq Aggregate` to append multiple include statement
if (includes != null)
{ rows = includes.Aggregate(rows, (current, include) => current.Include(include).AsQueryable().AsNoTracking()); }
// here I use auto mapper to map IEnumerable object to IEnumerable object as a result
return _mapper.Map<IEnumerable<TEntityDTO>>(rows).AsQueryable();
}
and finally the usage
_ServiceName.IncludeMultiple(i => i.city, i => i.age);

OutputCache varying by a complex object property

I have a controller action that receives a complex object as a parameter, I need the OutputCache to vary by one of the properties of this complex object. Is this possible? How?
if you have a model like
public class person{
public string Name {get;set;}
public string location {get;set;}
}
and in the (strongly typed)view you have a form
#model Person
#Html.BeginForm(){
#Html.TextBoxFor(x=>x.Name)
#Html.TextBoxFor(x=>x.location)
}
and you submit the form to an ActionResult savePerson, with varying signature like
public ActionResult savePerson(Person p){
// p.Name
// p.location
}
or
public ActionResult savePerson(string Name, string location){
}
therefore i think if you annotate the ActionResult like
[OutputCache(Duration=3600, VaryByParam="Name")]
public ActionResult savePerson(Person p)
{
//
return View();
}
it will do for you, or if you have a complex model like
public class person{
public string Name {get;set;}
public Location loc {get;set;}
}
public class Location{
public string address
}
try
[OutputCache(Duration=3600, VaryByParam="Person.Location.address")]
public ActionResult savePerson(Person p)
{
//
return View();
}
I had the same requirement as above and came up with a slightly different approach
The class
/// <summary>
/// This class is used to encapsulate search filters for monitor graphs
/// </summary>
public class DatacarMonitorSearchCriteriaModel
{
public int? SynergyCode { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime EndDate { get; set; }
/// <summary>
/// Filter to apply
/// </summary>
public IEnumerable<int> Countries { get; set; }
public DatacarMonitorSearchCriteriaModel()
{
Countries = new List<int>();
}
}
OutputCacheComplexAttribute
/// <summary>
/// <para>
/// An instance of this class mimic the behaviour of OutputCacheAttribute but for complex objects.
/// </para>
/// <para>
/// It allows to cache the output of any action that takes complex objects
/// </para>
/// </summary>
public class OutputCacheComplexAttribute : OutputCacheAttribute
{
private readonly Type[] _types;
private string _cachedKey;
/// <summary>
/// Initializes a new instance of the <see cref="OutputCacheComplexAttribute"/> class.
/// </summary>
/// <param name="types">Types that this attribute will lookup for in QueryString/Form data and store values in cache.</param>
/// <exception cref="System.ArgumentOutOfRangeException">type;type cannot be null</exception>
public OutputCacheComplexAttribute(params Type[] types)
{
if (types == null)
{
throw new ArgumentOutOfRangeException("type", "type cannot be null");
}
_types = types;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
StringBuilder sbCachedKey = new StringBuilder();
if (filterContext.HttpContext.Request.Url != null)
{
string path = filterContext.HttpContext.Request.Url.PathAndQuery;
IDictionary<string, object> parameters = filterContext.ActionParameters;
//we need to compute a cache key which will be used to store the action output for later retrieval
//The cache key scheme is
// {url}:{key 1}:{value};[{key 2}:{value 2}[; ... {key n}:{value n}]];
// where :
// - url is the url of the action that will be executed
// - key n is the name of the n-th parameter
// - value n is the value of the n-th parameter as json string.
foreach (KeyValuePair<string, object> kv in parameters)
{
var kv1 = kv;
if (kv.Value != null && _types.AtLeastOnce(t => t.IsInstanceOfType(kv1.Value)))
{
sbCachedKey = sbCachedKey.AppendFormat("{0}:{1};",kv.Key,
JsonConvert.SerializeObject(kv.Value, Formatting.None, new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}));
}
}
_cachedKey = String.Format("{0}:{1}:{2}", GetType().Name, path, sbCachedKey.ToString());
}
if (!String.IsNullOrWhiteSpace(_cachedKey) && filterContext.HttpContext.Cache[_cachedKey] != null)
{
filterContext.Result = (ActionResult)filterContext.HttpContext.Cache[_cachedKey];
}
else
{
base.OnActionExecuting(filterContext);
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (!String.IsNullOrWhiteSpace(_cachedKey))
{
filterContext.HttpContext.Cache.Add(_cachedKey, filterContext.Result, null,
DateTime.UtcNow.AddSeconds(Duration), Cache.NoSlidingExpiration,
CacheItemPriority.Default, null);
}
base.OnActionExecuted(filterContext);
}
}
Attribute usage
[OutputCacheComplex(typeof(DatacarMonitorSearchCriteriaModel), Duration = OutputCacheDurationInSeconds, Location = OutputCacheLocation.Server)]
public async Task<JsonNetResult<DatacarMonitorDetailModel>> ReadMonitorDetailsJson([DataSourceRequest] DataSourceRequest request, DatacarMonitorSearchCriteriaModel criteria)
{
//some really complicated code here
}
with this new attribute, you can specify which type[s] to use for caching and the cache key will be computed based on values of each its properties.
For object, just that work fine:
[OutputCache(VaryByParam = "*", Duration = 60)]

Troubleshoot object reference error when filling mvc models with datareader?

I am using MVC 3 and I am using a datareader to create a list of items that have subItems. When I add the subitem I get "Object reference not set to an instance of an object." With the following code:
QuestionaireLine question = new QuestionaireLine();
question.Question_ID = Convert.ToInt32(reader["Question_ID"]);
question.Question_Answer = reader["Question_Answer"].ToString();
...etc..
currentGroup.Lines.Add(question); //exception thrown here
The models:
public class Questionaire
{
public int Question_Group_Id { get; set; }
public string Question_Group_Name { get; set; }
public int Question_Group_Indent { get; set; }
public int Question_Group_Order { get; set; }
public List<QuestionaireLine> Lines { get; set; }
}
public class QuestionaireLine
{
public int Question_ID { get; set; }
public string Question_Label { get; set; }
public string Question_Answer { get; set; }
public int Data_Type { get; set; }
public int Control_Type { get; set; }
public string Data_Choices { get; set; }
public int Data_Max_Length { get; set; }
public bool Issue_Tagged { get; set; }
public int Question_Order { get; set; }
public string NumberedQuestion
{
get { return String.Format("{0}. {1}", Question_Order, Question_Label); }
}
}
The whole code:
// what am I missing??
using (var conn = new SqlConnection(_connectionString))
{
List<Questionaire> groups = new List<Questionaire>();
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.Parameters.Add(new SqlParameter
{
ParameterName = "#Review_ID",
Value = reviewID
});
com.CommandText = "Review_Get_Question_Groups_Answers";
conn.Open();
// Get the reader
SqlDataReader reader = com.ExecuteReader();
// Process each result in the result set
int currQuestionGroupId = 0;
Questionaire currentGroup = null;
while (reader.Read())
{
var questionGroupId = Convert.ToInt32(reader["Question_Group_Id"]);
if (questionGroupId != currQuestionGroupId)
{
currQuestionGroupId = questionGroupId;
if (currentGroup != null)
{
groups.Add(currentGroup);
}
currentGroup = new Questionaire();
currentGroup.Question_Group_Id = Convert.ToInt32(reader["Question_Group_Id"]);
currentGroup.Question_Group_Indent = Convert.ToInt32(reader["Question_Group_Indent"]);
currentGroup.Question_Group_Name = reader["Question_Group_Name"].ToString();
currentGroup.Question_Group_Order = Convert.ToInt32(reader["Question_Group_Order"]);
}
if (reader["Question_ID"] != DBNull.Value)
{
QuestionaireLine question = new QuestionaireLine();
question.Question_ID = Convert.ToInt32(reader["Question_ID"]);
question.Question_Answer = reader["Question_Answer"].ToString();
question.Issue_Tagged = Convert.ToBoolean(reader["Issue_Tagged"]);
question.Data_Type = Convert.ToInt32(reader["Data_Type"]);
question.Data_Max_Length = Convert.ToInt32(reader["Data_Max_Length"]);
question.Data_Choices = reader["Data_Choices"].ToString();
question.Question_Label = reader["Question_Label"].ToString();
question.Question_Order = Convert.ToInt32(reader["Question_Order"]);
question.Control_Type = Convert.ToInt32(reader["Control_Type"]);
currentGroup.Lines.Add(question);
}
if (currentGroup != null)
{
groups.Add(currentGroup);
}
}
reader.Close();
com.Dispose();
return groups;
}
Your Lines property on your Questionaire instance is Null. Change to:
public class Questionaire
{
public int Question_Group_Id { get; set; }
public string Question_Group_Name { get; set; }
public int Question_Group_Indent { get; set; }
public int Question_Group_Order { get; set; }
public List<QuestionaireLine> Lines { get; set; }
public Questionaire() {
Lines = new List<QuestionaireLine>();
}
b.t.w. stepping through your code would have shown you that.

Linq - drill down with find

I am completely new to linq and need help.
These are my poco classes:
public class User {
public User()
{
this.Profiles = new List<Profile>();
}
public Guid ID { get; set; }
public bool IsValid{ get; set; }
public virtual ICollection<Profile> Profiles { get; set; }
}
public class Profile {
public Profile() {
this.Users = new List<User>();
this.Customers = new List<Customer>();
}
public Guid ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
}
public class Customer {
public Customer()
{
this.Profiles = new List<Profile>();
}
public Guid ID { get; set; }
public string Number { get; set; }
public virtual ICollection<Profile> Profiles { get; set; }
}
I would like to search for valid Users with special customers. Special customers would come from another user. So I would send another user as method argument.
Is it even possible with linq or I need stored procedure to solv the problem?
Best regards
You can try this:
public static List<User> FindAllUsersBySameCustomers(User sourceuser)
{
var res = sourceuser.Profiles.SelectMany(p => p.Customers)
.SelectMany(c => c.Profiles)
.SelectMany(p => p.Users)
.Distinct();
return res.ToList();
}
But beware, it will work only if your relations are populated (included) like in my example here.
NOTE
You should not call virtual member inside the constructor. Anwer is here on SO: Virtual member call in a constructor
Try this..
/// <summary>
/// Search for valid Users with special customers.
/// Special customers would come from another user.
/// So I would send another user as method argument.
/// </summary>
/// <returns></returns>
public List<User> FindValidUsersWithSpecialCustomers(List<User> allUsers, User anotherUser)
{
var specialCustomers = anotherUser.Profiles.SelectMany(aProfile => aProfile.Customers);//.Select(cust => cust.Number == "SpecialCustomerNumber" && cust.ID == new Guid("SpecialCustomerGuid"));
Func<IEnumerable<Customer>, IEnumerable<Customer>, Boolean> IsSpecialCustomersPresentInThisCustomersList =
delegate(IEnumerable<Customer> customerList, IEnumerable<Customer> specialCustomersList)
{
if ((from cust in customerList where specialCustomersList.Contains(cust) select cust).Any())
return true;
else
return false;
};
var validUsersWithSpecialCustomers = (from user in allUsers where user.IsValid && IsSpecialCustomersPresentInThisCustomersList(user.Profiles.SelectMany(p => p.Customers), specialCustomers) select user);
return validUsersWithSpecialCustomers.ToList();
}

LINQ and Entity Framework, get sum of related rows of a non mapped column

I want to get the sum of applicants that applied to a specific position, this should not be saved as a column.
My model is simple:
We have positions:
net developer
java developer
We have applicants:
Luis
John
etc
We have applicants per position
With this column or property I want to know how many people have applied to each position, depending on the status.
So in my mvc view I want to show something like:
Position Applied Accepted Rejected ... other status
.net developer 5 3 2
java developer 3 2 1
The real problem here is the linq query which I am not very expert.
EDIT: I think I needed to change where the linq query must be coded, I suppose it should be in the ApplicantPosition class instead of Position, I also changed the types of Position and Application to be ICollection.
Please see the modified code.
public class Position
{
public int id { get; set; }
[StringLength(20, MinimumLength=3)]
public string name { get; set; }
public int yearsExperienceRequired { get; set; }
}
public class Applicant
{
public int ApplicantId { get; set; }
[StringLength(20, MinimumLength = 3)]
public string name { get; set; }
public string telephone { get; set; }
public string skypeuser { get; set; }
public ApplicantImage photo { get; set; }
}
public class ApplicantPosition
{
public virtual ICollection<Position> appliedPositions { get; set; }
public virtual ICollection<Applicant> applicants { get; set; }
public DateTime appliedDate { get; set; }
public int StatusValue { get; set; }
public Status Status
{
get { return (Status)StatusValue; }
set { StatusValue = (int)value; }
}
[NotMapped]
public int numberOfApplicantsApplied
{
get
{
var query =
from ap in appliedPositions
select new
{
positionName = g.Key.name,
peopleApplied = g.Count(x => x.Status == Status.Applied),
};
return query.Count(); ---??
}
}
}
Use direct SQL with PIVOT operator. This is really not a case for Linq query.
You can paste this into LINQPad as C# Program and run.
public enum Status
{
Applied,
Accepted,
Rejected
}
public class Position
{
public int id { get; set; }
public string name { get; set; }
}
public class Applicant
{
public int ApplicantId { get; set; }
public string name { get; set; }
}
public class ApplicantPosition
{
public Position appliedPosition { get; set; }
public Applicant applicant { get; set; }
public DateTime appliedDate { get; set; }
public Status Status { get; set; }
}
void Main()
{
var p1 = new Position { id = 1, name = ".net developer" };
var p2 = new Position { id = 2, name = "java developer" };
var a1 = new Applicant { ApplicantId = 100, name = "Luis" };
var a2 = new Applicant { ApplicantId = 200, name = "John" };
var ap1 = new ApplicantPosition { appliedPosition = p1, applicant = a1, Status = Status.Applied };
var ap2 = new ApplicantPosition { appliedPosition = p1, applicant = a2, Status = Status.Accepted };
var ap3 = new ApplicantPosition { appliedPosition = p2, applicant = a2, Status = Status.Rejected };
var db = new[] { ap1, ap2, ap3};
var query =
from ap in db
group ap by ap.appliedPosition into g
select new
{
positionName = g.Key.name,
peopleApplied = g.Count(x => x.Status == Status.Applied),
peopleAccepted = g.Count(x => x.Status == Status.Accepted),
peopleRejected = g.Count(x => x.Status == Status.Rejected),
};
query.Dump();
}
The result will be:
positionName peopleApplied peopleAccepted peopleRejected
.net developer 1 1 0
java developer 0 0 1
According to my experiences you can use LinQ or Entity Framework just by mapping your tables in to a DBML to a Entity Framework Model file.
In other way Microsoft gives you a Dynamic LinQ class that you can use it.I think you map all your columns and user Dynamic LinQ class.Good luck

Resources