I am very very new to writing c# using delegates and funcs. Literally watched a pluralsite video an hour ago.
I am trying to write a function that will allow me to write a flexible method for building up a Where clause in my mvc application using EF and IQueryable
To try explain I thought I would write a small example of what I am trying to do as a console application
public static void Main(string[] args)
{
var city1 = new ArgsSearch() {Address = "city1"};
var city2 = new ArgsSearch() { Address = "city2" };
var city1Dealers = DummyDealers().Where(GenerateSearchCriteria<CarDealer>(city1)).ToList();
var city2Dealers = DummyDealers().Where(GenerateSearchCriteria<CarDealer>(city2)).ToList();
foreach (var carDealer in city1Dealers)
{
Console.WriteLine(carDealer.Name);
}
Console.WriteLine("_______________________________________");
foreach (var carDealer in city2Dealers)
{
Console.WriteLine(carDealer.Name);
}
Console.WriteLine("_______________________________________");
}
public static List<CarDealer> DummyDealers()
{
return new List<CarDealer>()
{
new CarDealer() {Address = "city1", Id = 1, Name = "name1"},
new CarDealer() {Address = "city1", Id = 2, Name = "name2"},
new CarDealer() {Address = "city2", Id = 3, Name = "name3"},
new CarDealer() {Address = "city2", Id = 4, Name = "name4"}
};
}
public static Func<TSearch, bool> GenerateSearchCriteria<TSearch>(ArgsSearch args) where TSearch : IBaseModel
{
if (this.GetType() == typeof(CarModel))
{
//return
return e => e.Name == args.Name;// && e.Cost == args.Cost;
}
if (GetType() == typeof(CarDealer))
{
return e => e.Name == args.Name;// && e.Address == args.Address;
}
return e => e.Name==args.Name;
}
public class ArgsSearch
{
public string Name { get; set; }
public string Address { get; set; }
public decimal Cost { get; set; }
}
Am I going down the right track? How do I know that TSearch is either CarModel or CarDealer? allowing my custom search values to be applied.
Thanks for any suggestions and any help would be really appreciated!
You need to cast:
(warning: ugly code ahead)
if (typeof(TSearch) == typeof(CarModel))
{
//return
return e => e.Name == args.Name && ((CarModel)(object)e).Cost == args.Cost;
}
if (typeof(TSearch) == typeof(CarDealer))
{
return e => e.Name == args.Name && ((CarDealer)(object)e).Address == args.Address;
}
return e => e.Name==args.Name;
Related
Given these source classes (used in an EFCore application)
class StudentCourse
{
public int StudentId { get; set; }
public int CourseId { get; set; }
}
class Student
{
public virtual ICollection<StudentCourse> Courses { get; set; }
}
and the following Protobuf message
message StudentDto {
repeated int32 courseIds = 1;
}
How am I able to map Student.Courses to StudentDto.CourseIds using AutoMapper?
So far I tried the following:
class StudentProfile : Profile
{
class Converter : IValueConverter<ICollection<StudentCourse>, RepeatedField<int>>
{
public RepeatedField<int> Convert(ICollection<StudentCourse> sourceMember, ResolutionContext context)
{
return new RepeatedField<int> { sourceMember.Select(x => x.CourseId) };
}
}
public StudentProfile()
{
CreateMap<Student, StudentDto>()
.ForMember(dst => dst.CourseIds, o => o.MapFrom(src => src.Courses.Select(x => x.CourseId)));
//.ConvertUsing(student => new StudentDto{CourseIds = { student.Courses.Select(x => x.CourseId)}});
//.ForMember(dst => dst.CourseIds, o => o.ConvertUsing(new Converter(), src => src.Courses));
}
}
class Program
{
static void Main(string[] args)
{
var student = new Student
{
Courses = new List<StudentCourse>
{
new StudentCourse { CourseId = 1, StudentId = 1 },
new StudentCourse { CourseId = 6, StudentId = 1 },
new StudentCourse { CourseId = 11, StudentId = 1 }
}
};
IMapper mapper = new Mapper(new MapperConfiguration(cfg => cfg.AddProfile(new StudentProfile())));
var mapped = mapper.Map<StudentDto>(student);
// Expecting: {{ "courseIds": [ 1, 6, 11 ] }}
// Actually: {{ }}
}
}
The ConvertUsing statement in StudentProfile works and maps the member correctly but as the classes I'm using in my project are far more complex, this is really the last option I would like to use.
Does anyone of you know the cause and a possible fix for this problem?
I'm using Google.Protobuf 3.7.0 and AutoMapper 8.0.0.
Edit:
I already checked the execution plan but couldn't find any errors in there.
For the sake of completeness here I'll attach it here:
(src, dest, ctxt) =>
{
StudentDto typeMapDestination;
return (src == null)
? null
: {
typeMapDestination = dest ?? new StudentDto();
try
{
var resolvedValue =
{
try
{
return ((src == null) || ((src.Courses == null) || false))
? null
: src.Courses.Select(x => x.CourseId);
}
catch (NullReferenceException)
{
return null;
}
catch (ArgumentNullException)
{
return null;
}
};
var propertyValue = (resolvedValue == null)
? {
var collectionDestination = (ICollection<int>)((dest == null) ? null : typeMapDestination.CourseIds);
if ((collectionDestination == null) || collectionDestination.IsReadOnly)
{
}
else
{
collectionDestination.Clear();
}
return new RepeatedField<int>();
}
: {
var passedDestination = (dest == null) ? null : typeMapDestination.CourseIds;
var collectionDestination = ((passedDestination == null) || ((ICollection<int>)passedDestination).IsReadOnly)
? new RepeatedField<int>()
: passedDestination;
collectionDestination.Clear();
var enumerator = resolvedValue.GetEnumerator();
while (true)
{
if (enumerator.MoveNext())
{
var item = enumerator.Current;
collectionDestination.Add(item);
}
else
{
break;
}
}
return collectionDestination;
};
return propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
return typeMapDestination;
};
};
I have the below two classes:
public class FirstInner
{
public int Id { get; set; }
public string Type { get; set; }
public string RoleId { get; set; }
}
public class SecondInner
{
public int Id { get; set; }
public string Type { get; set; }
}
Again, there are lists of those types inside the below two classes:
public class FirstOuter
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<FirstInner> Inners { get; set; }
}
public class SecondOuter
{
public int Id { get; set; }
public string Name { get; set; }
public List<SecondInner> Inners { get; set; }
}
Now, I have list of FirstOuter and SecondOuter. I need to check if FirstOuter list is a subset of SecondOuter list.
Please note:
The names of the classes cannot be changed as they are from different systems.
Some additional properties are present in FirstOuter but not in SecondOuter. When comparing subset, we can ignore their presence in SecondOuter.
No.2 is true for FirstInner and SecondInner as well.
List items can be in any order---FirstOuterList[1] could be found in SecondOuterList[3], based on Id, but inside that again need to compare that FirstOuterList[1].FirstInner[3], could be found in SecondOuterList[3].SecondInner[2], based on Id.
I tried Intersect, but that is failing as the property names are mismatching. Another solution I have is doing the crude for each iteration, which I want to avoid.
Should I convert the SecondOuter list to FirstOuter list, ignoring the additional properties?
Basically, here is a test data:
var firstInnerList = new List<FirstInner>();
firstInnerList.Add(new FirstInner
{
Id = 1,
Type = "xx",
RoleId = "5"
});
var secondInnerList = new List<SecondInner>();
secondInner.Add(new SecondInner
{
Id = 1,
Type = "xx"
});
var firstOuter = new FirstOuter
{
Id = 1,
Name = "John",
Title = "Cena",
Inners = firstInnerList
}
var secondOuter = new SecondOuter
{
Id = 1,
Name = "John",
Inners = secondInnerList,
}
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
Need to check if firstOuterList is part of secondOuterList (ignoring the additional properties).
So the foreach way that I have is:
foreach (var item in firstOuterList)
{
var secondItem = secondOuterList.Find(so => so.Id == item.Id);
//if secondItem is null->throw exception
if (item.Name == secondItem.Name)
{
foreach (var firstInnerItem in item.Inners)
{
var secondInnerItem = secondItem.Inners.Find(sI => sI.Id == firstInnerItem.Id);
//if secondInnerItem is null,throw exception
if (firstInnerItem.Type != secondInnerItem.Type)
{
//throw exception
}
}
}
else
{
//throw exception
}
}
//move with normal flow
Please let me know if there is any better approach.
First, do the join of firstOuterList and secondOuterList
bool isSubset = false;
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
var jointOuterList = firstOuterList.Join(
secondOuterList,
p => new { p.Id, p.Name },
m => new { m.Id, m.Name },
(p, m) => new { FOuterList = p, SOuterList = m }
);
if(jointOuterList.Count != firstOuterList.Count)
{
isSubset = false;
return;
}
foreach(var item in jointOuterList)
{
var jointInnerList = item.firstInnerList.Join(
item.firstInnerList,
p => new { p.Id, p.Type },
m => new { m.Id, m.type },
(p, m) => p.Id
);
if(jointInnerList.Count != item.firstInnerList.Count)
{
isSubset = false;
return;
}
}
Note: I am assuming Id is unique in its outer lists. It means there will not be multiple entries with same id in a list. If no, then we need to use group by in above query
I think to break the question down..
We have two sets of Ids, the Inners and the Outers.
We have two instances of those sets, the Firsts and the Seconds.
We want Second's inner Ids to be a subset of First's inner Ids.
We want Second's outer Ids to be a subset of First's outer Ids.
If that's the case, these are a couple of working test cases:
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsTrue(isInnerSubset);
Assert.IsTrue(isOuterSubset);
}
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreNotSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
firstInnerIds.Clear();
firstInnerIds.Add(5);
firstOuterIds.Clear();
firstOuterIds.Add(5);
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsFalse(isInnerSubset);
Assert.IsFalse(isOuterSubset);
}
private List<FirstOuter> GetFirstOuterList() { ... }
private List<SecondOuter> GetSecondOuterList() { ... }
I am new to LINQ, and i am getting this error message :
{"error":"Explicit construction of entity type 'Proj.Models.Ad' in
query is not allowed."}
I am using this code to retrieve the data
public ActionResult favads()
{
bool isValid = false;
string authToken = "";
if (Request["dt"] != null)
authToken = Request["dt"].ToString().Trim();
else
return Content("{\"error\":\"Please send device token parameter 'dt'.\"}", "application/json");
string message = (new CommonFunction()).ValidateToken(authToken, out isValid);
if (isValid)
{
long userID = 0;
if (Request["userID"] != null)
long.TryParse(Request["userID"].ToString().Trim(), out userID);
else
return Content("{\"error\":\"Please send user id parameter 'userID'.\"}", "application/json");
if (userID < 1)
return Content("{\"error\":\"Please select appropriate user to view details.\"}", "application/json");
try
{
var q = from d in db.AdsFavourites.Where(Favtb => Favtb.CreatedBy.Equals(userID) && Favtb.StatusID.Equals(1)).Select(p => new Ad() { ID = p.AdID.Value, CategoryID = int.Parse(p.CategoryID.ToString()) })
from c in db.Ads.Where(Adstb => Adstb.ID == d.ID).DefaultIfEmpty()
select new
{
FavId = d.ID,
c.ID,
c.Category.ParentCategoryID,
c.Category.CategoryID,
c.Category.LogoFile,
c.Category.CatName,
c.AdTitle,
AdDescription = (c.AdDescription.Length > 50 ? c.AdDescription.Substring(0, 50) : c.AdDescription),
c.CityID,
c.Price,
c.CreationDate,
c.Photos,
c.VideoUrl,
c.IsMobileVisibile
};
int pg = 0;
if (Request["p"] != null)
int.TryParse(Request["p"], out pg);
string _sortby = "MRF";
if (Request["sortby"] != null)
_sortby = Request["sortby"];
if (_sortby.Equals("OF"))
q = q.OrderBy(ad => ad.CreationDate.Value);
else if (_sortby.Equals("PD"))
q = q.OrderByDescending(ad => ad.Price.Value);
else if (_sortby.Equals("PA"))
q = q.OrderBy(ad => ad.Price.Value);
else
q = q.OrderByDescending(ad => ad.CreationDate.Value);
return Json(q.ToList().Skip(pg * recordsPerPage).Take(recordsPerPage), JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Content("{\"error\":\"" + ex.Message + "\"}", "application/json");
}
}
else
{
return Content("{\"error\":\"" + message + "\"}", "application/json");
}
}
Ads class
public class Ads : Ad
{
public long ID { get; set; }
public string FashionType { get; set; }
public string ForRentSale { get; set; }
public string ForJobHire { get; set; }
public string JobTypeID { get; set; }
}
Ad is also mapped and you cannot project a new instance of it like this in a query. See this question.
What you can do is just create an anonymous type or even better in this case, since in your query you only use the ID of the Ad object retrieve just that:
var q = from d in db.AdsFavourites.Where(Favtb => Favtb.CreatedBy.Equals(userID) &&
Favtb.StatusID.Equals(1))
.Select(p => p.AdID.Value)
from c in db.Ads.Where(Adstb => Adstb.ID == d).DefaultIfEmpty()
select new
{
FavId = d,
c.ID,
c.Category.ParentCategoryID,
/* ... */
};
I recommend you look into Navigation Properties. I think it will make this query look neater
Also maybe look at this option instead of using the Method Syntax for the first table, might be more readable:
var q = from d in db.AdsFavourites
//In this case also no need for `Equals` - you are comparing value types
where d.CreateBy == userID && d.StatusID == 1
from c in db.Ads.Where(Adstb => Adstb.ID == d.AdID.Value).DefaultIfEmpty()
select new
{
FavId = d.AdID.Value,
c.ID,
/* ... */
};
I am having trouble dynamically specifying the column and direction to sort on.
I currently have the following code:
if (sort != "OrderID")
{
if (sort == "EmployeeName")
{
sort = "Employee.FirstName"; //sort by Employee FirstName
}
else
{
sort = "Customer." + sort; //Customer.CompanyName sort
}
}
var sortCriteria = string.Format("{0} {1}", sort, sortDir);
var res1 = nwd.Orders //response
.OrderBy(o => sort+" "+sortDir)
.ThenBy(o => o.OrderID)
.Skip((page - 1) * rowsPerPage)
.Take(rowsPerPage)
.Select(o => new
{
o.OrderID,
o.Customer.CompanyName,
o.Customer.ContactName,
o.Employee.FirstName,
o.Employee.LastName,
o.Order_Details
}).ToList();
Any help would be much appreciated!
How about this?
var res1 = nwd.Orders //response
IOrderedQueryable<Orders> result;
if (sort != "OrderID")
{
if (sort == "EmployeeName")
{
result = res1.OrderBy(o => o.Employee.FirstName);
}
else
{
result= res1.OrderBy(o => o.Customer.CompanyName);
}
}
result = result.ThenBy(o => o.OrderID)
.Skip((page - 1) * rowsPerPage)
.Take(rowsPerPage)
.Select(o => new
{
o.OrderID,
o.Customer.CompanyName,
o.Customer.ContactName,
o.Employee.FirstName,
o.Employee.LastName,
o.Order_Details
}).ToList();
Ok, here's a working example with a dummy class:
internal class Program
{
private static void Main(string[] args)
{
var list_people = new List<Person> {
new Person() {Age = 4, Name = "yo"},
new Person() {Age = 5, Name = "a"}
};
var dynamic_propretry = typeof (Person).GetProperty("Name");
var sorted = list_people.OrderBy(person => dynamic_propretry.GetValue(person, null));
foreach (var person in sorted)
{
Console.Out.WriteLine(person);
}
Console.ReadLine();
}
}
public class Person{
public int Age { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name + ":" + Age;
}
}
So, all you have to do is store the property you want in a string, and just use that string where I used "Name". It should work.
I have the following where objectiveData is: IEnumerable<Objective>
public IList<Objective> createObjectives()
{
var objectiveData = GetContent.GetType5();
var objectives = objectiveData.Select(o => {
var result = new Objective {
Name = o.Name,
Text = o.Text
};
if (o.Name != null && o.Name.EndsWith("01"))
{
result.ObjectiveDetails.Add
(
new ObjectiveDetail
{
Text = o.Text
}
);
}
});
return objectives.ToList();
}
I am getting an error on the line with the "select" saying:
The type arguments for method 'System.Linq.Enumerable.Select<TSource,TResult>
(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,int,TResult>)'
cannot be inferred from the usage. Try specifying the type arguments explicitly.
Here's my Objective class:
public partial class Objective : AuditableTable
{
public Objective()
{
this.ObjectiveDetails = new List<ObjectiveDetail>();
}
public int ObjectiveId { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
You need
return result;
at the end of your expression.
var objectives = objectiveData.Select(o => {
var result = new Objective {
Name = o.Name,
Text = o.Text
};
if (o.Name != null && o.Name.EndsWith("01"))
{
result.ObjectiveDetails.Add
(
new ObjectiveDetail
{
Text = o.Text
}
);
}
//you miss this
return result;
});
First of all, LINQ and side effects are... well, bad. Due to lazy loading, and many other issues. However, what you need is to add a return result; line at then end of your code like so:
var objectives = objectiveData.Select(o => {
var result = new Objective {
Name = o.Name,
Text = o.Text
};
if (o.Name != null && o.Name.EndsWith("01"))
{
result.ObjectiveDetails.Add
(
new ObjectiveDetail
{
Text = o.Text
}
);
}
return result;
});
However, for this to behave in a more regular manner, I would do it like this:
var objectives =
objectiveData.Select(o => new Objective { Name = o.Name, Text = o.Text})
result.ObjectiveDetails.AddRange(
objectiveData.Where(o => (o.Name ?? "").EndsWith("01"))
.Select(o => new ObjectiveDetail { Text = o.Text }));