Combining Linq-Entity statements - c#

I have 2 repositories, Member and Person. The Person Model contains a nullable reference property to a Member Model. The Member Model is defined in the Member Repository and I would like to put in place a pattern that ensures this stays that way. however, when I call the methods in the member repo from the person repo I get the
LINQ to Entities does not recognize the method 'System.Linq.IQueryable 1[...IMember] Get(..Entities,System.Linq.Expressions.Expression 1[System.Func`2[..tblMember,System.Boolean]])' method, and this method cannot be translated into a store expression."
While I understand this can be easily solved by putting .asEnumerable() then a second select there is a cost that means your doing 2 queries instead of one and you loose the ability to expose the method as awaitable. Below is the code I have removed a few non essential parts to clearify. I am not using lambda because I have not found a good way to say let in a lambda expression. My goal is person.Member would simply be a nested select. Also Note that the Member side of things is a very ugly database that I have not control over and these repos are being separated for a reason. Thanks in advance
public class MemberRepository : Interfaces.IRepository<IMemberBase, string>
{
private Data.LSAEntities entities { get; set; }
public MemberRepository(Data.LSAEntities entities)
{
this.entities = entities;
}
internal static IQueryable<IMember> Get(Data.LSAEntities entities, Expression<Func<tblMember, bool>> predicate)
{
return (from t in entities.tblMembers.Where(predicate)
let options = entities.tblDataOptions.Where(o => o.DataName == "MemberStatus")
select new Member()
{
MemberID = t.MemberID,
...
});
}
}
public class PersonRepository : IRepository<IPerson, int>
{
private Data.LSAEntities entities { get; set; }
public PersonRepository(Data.LSAEntities entities)
{
this.entities = entities;
}
public IQueryable<IPerson> Get(int key)
{
return (from p in entities.tblPersons
where p.PersonId == key
select new Person()
{
PersonId = p.PersonId,
...
Member = MemberRepository.Get(entities, m=> p.MemberId == m.MemberID).FirstOrDefault()
});
}
}

This may help you:
Given
var ans = from a in table
let b = a.TotalPrice / a.Quantity
where b > 500
select new {
PriceEa = b,
a.ID,
a.Description
};
translates to
var ans = table.Select(a => new { b = a.TotalPrice / a.Quantity, a })
.Where(ba => ba.b > 500)
.Select(ba => new {
PriceEa = ba.b,
ba.a.ID,
ba.a.Description
});
the let clause in query comprehension syntax in LINQ is translated to a Select adding a new field to hold the let value when using lambda syntax.

Related

C# how to return 1 row from List<T> object passed as parameter

I need to return one row of List from my function Selectus.
So I pass to the function Selectus object that reflects database table fields and I need to return one row which match the parameter looking_for:
public static List<T> Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.ToList();//here one record should be returned
}
I do not know how to select one row from the list assuming that T parameter is vary. In SelectusT> method I want to pass as T different objects which reflect fields in database table rather than creatinig separate methods for each select. e.g. call Selectus, where object passed is public class ProductCodes { public int ID { get; set; } public string SapIndex { get; set; } public string SapName { get; set; } }. Then I want to call another Selectus<ProductTypes> for another table etc... So I want to write generic/overall method and use it universally for all types of my objects which reflects the fields of few database tables. The SapIndex property is always in the same place of all objects...
Using prop[1] is incredibly fragile. Who says that the property you're currently interested in is always going to be in second place? What if someone adds another property tomorrow? What if not every T that you use have the same property in the second place on its list of properties? It is quite unclear what your actual goal is here and why you've taken the reflection route.
You would be better off using inheritance or interface implementation here. I'm going to use an interface in this answer, but either would work.
For the sake of clarity, let's assume there is a Code field in all your possible lists, and this is the property you're trying to match with.
Define a reusable interface:
public interface ICodeEntity
{
string Code { get; }
}
Apply your interface to all of the classes that you intend to use for your Selectus method.
public class Person : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
public class Document : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
Add a generic type constraint that limits the use of T only to types that implement your interface.
public static List<T> Selectus<T>(string code)
where T : ICodeEntity
You can now write your code in a way that it relies on the type in question having a Code property, and the compiler will help enforce it.
var db = OrmLiteBaza().Open();
var list = db.Select<T>().ToList();
db.Dispose();
return list.Where(item => item.Code == code).ToList();
Usage examples:
List<Person> peopleWithCodeABC = Selectus<Person>("ABC");
List<Person> documentsWithCodeXYZ = Selectus<Document>("XYZ");
// This will fail if Animal does not implement ICodeEntity
var compilerError = Selectus<Animal>("ABC");
I might not understand fully what you want, but instead of string looking_for you could pass in a Func<,> delegate which acts as a selector.
Something like:
public static List<TField> Selectus<T, TField>(Func<T, TField> selector)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Select(selector); // 'using System.Linq;'
return list_selected_record.ToList();
}
Then I believe it could be called like this:
var list_one = Selectus((ProductCodes x) => x.SapIndex);
var list_two = Selectus((ProductTypes x) => x.SapIndex);
var list_three = Selectus((ProductCodes x) => x.SapName);
With this syntax I leave out the <ProductCodes, string> generic arguments to the method since they can be inferred.
Hmm, maybe you want it in the opposite dimension. You could do:
public static List<T> Selectus<T>(Func<T, bool> predicate)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Where(predicate); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus((ProductCodes x) => x.SapIndex == "ABC");
var list_two = Selectus((ProductTypes x) => x.SapIndex == "ABC");
var list_three = Selectus((ProductCodes x) => x.SapName == "DaName");
or:
var list_one = Selectus<ProductCodes>(x => x.SapIndex == "ABC");
var list_two = Selectus<ProductTypes>(x => x.SapIndex == "ABC");
var list_three = Selectus<ProductCodes>(x => x.SapName == "DaName");
But if it is going to always be the "same" property, like always x.SapIndex (but for different types of x), then Flater's answer looks good.
Otherwise, if you insist, your reflection approach should be possible. Use propety's name, not its index! Let me try:
public static List<T> Selectus<T>(string looking_for)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
const string prop_name = "SapIndex";
var prop = typeof(T).GetProperty(prop_name); // can blow up for bad T
var list_selected_record = select_all_list
.Where(x => (string)(prop.GetValue(x)) == looking_for); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus<ProductCodes>("ABC");
var list_two = Selectus<ProductTypes>("ABC");
you can change code to return just one element
public static T Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.FirstOrDefault();//here one record should be returned
}

Entity Framework Linq set property of an object without additional projection by SELECT

I'm implementing an inheritance scenario with Entity Framework 6. Inheritance only exists on DTO level, i.e. i have two classes Foo and Bar : Foo,
I have first method that selects an IQueryable<Foo> and then several methods that select additional properties for specific inheriting classes like Bar.
Normally, I would have code like
from foo in SelectFoo()
join barAdditionalProps in .....
select new Bar{
Id = foo.Id,
Description = foo.Description,
Baz = barAdditionalProps.Baz}
which would give a nice single SQL query as a result.
This, unfortunately, means that all properties from foo will have to be copied during second projection (first one is inside SelectFoo). In real life code that would mean 20+ properties copied in every method using SelectFoo.
I would like to do something like this (code is prepared in LINQPad, assume this == EFContext):
void Main()
{
(from barBase in SelectT<Bar>()
join field in this.Fields on barBase.Id equals field.ProductId
let _1 = barBase.Baz = field.Baz // this part fails with exception
// An expression tree may not contain an assigment operator
select barBase)
.First()
.Dump();
}
public IQueryable<T> SelectT<T>() where T : Foo, new()
{
return this
.Products
.Select(x => new T
{
Id = x.Id,
Description = x.Description
});
}
public class Foo
{
public string Description {get;set;}
public int Id {get;set;}
}
public class Bar : Foo
{
public int Baz {get;set;}
}
Receiving the exception described above, I'm looking for a way to make this work or any other solution that would allow me not to copy all base class properties during second projection.
Since no existing tools were up to the job, I wrote my own library that uses expression tree modification to project baseclass dto in subclass dto automatically.
Now instead of this
IQueryable<BaseDto> baseQuery = GetBaseQuery();
IQueryable<SubclassDto> query = from baseDto in baseQuery
let moreData = DataContext.vMoreData.FirstOrDefault(x => x.Id == baseDto.Id)
select new SubclassDto()
{
NewProp1 = moreData.Foo,
NewProp2 = moreData.Baz,
OldProp1 = moreData.SomeOverridingData,
OldProp2 = baseDto.OldProp2,
OldProp3 = baseDto.OldProp3,
OldProp4 = baseDto.OldProp4,
//... 20 more projections from BaseDto to SubclassDto
};
We have this
IQueryable<BaseDto> baseQuery = GetBaseQuery();
IQueryable<SubclassDto> query = from baseDto in baseQuery
let moreData = DataContext.vMoreData.FirstOrDefault(x => x.Id == baseDto.Id)
select baseDto.AutoProjectInto(() => new SubclassDto()
{
NewProp1 = moreData.Foo,
NewProp2 = moreData.Baz,
OldProp1 = moreData.SomeOverridingData
});
IQueryable<SubclassDto> activateQuery = query.ActivateAutoProjects();
And all properties that were not bound by the SubclassDto initialization are projected from baseDto automatically.
Library is available via Github https://github.com/IKoshelev/Linq.AutoProject and NuGet https://www.nuget.org/packages/Linq.AutoProject

Linq to entities - reusing predicates with navigation properties?

I am trying to find a way to reuse predicates for filtering entities in EF 6.1.3. I've run into a problem filtering related properties using 'Where'.
E.g. if I have this interface IValidFromTo
public interface IValidFromTo
{
DateTime StartDate { get; set;}
DateTime EndDate { get; set; }
}
and a function that returns a predicate for Where :
public class Extensions
{
public static Expression<Func<T, bool>> Current<T>()
where T : IValidFromTo
{
var currentDate = DateTime.Now;
return x => x.StartDate <= currentDate && x.EndDate >= currentDate;
}
}
See http://www.albahari.com/nutshell/predicatebuilder.aspx for background.
When applied directly to DbSet, this method works.
var query = ctx.Items.Where(Extensions.Current<Item>()); // compiles
But how to make it work with a more complex query dealing with navigation properties?
E.g. if I have a DbSet<Person> with a collection of Item:
public class Person
{
...
public virtual ICollection<Item> Items { get; set; }
}
and I want to project it into an object containing the name of the person and just the current Items, I end up with some rather cluttered code:
var relationQuery = ctx.People.Select(x => new
{ Name = x.Name,
CurrentItems = x.Items.AsQueryable().Where(Extensions.Current<Item>())
});
I wonder if it is possible to improve this code, e.g. to be able to write something like
CurrentItems = x.Items.Current() // quasi an extension method on `ICollection<Item>`?
(writing an extension method on ICollection<IValidFromTo> doesn't work, because EFf wouldn't recognize this method and throw an error)
UPDATE
Seems like this is achievable via a Join (supposing that each Person can only have a single valid item):
var isCurrent= x => <<some condition on x>>;
...
var validItems = ctx.Items.Where(isCurrent);
var peopleWithCurrentItems = from person in ctx.Persons
join item in validItems on person.Id equals item.Owner.Id
select new { Person = person, Item = item };
If there may be more than one valid Item per Person, then
var grouped = peopleWithValid.GroupBy(x => x.Person);
However, this version of the query will exclude persons with no matching Items.

How to return LINQ object from method?

I have method which has LINQ query and query return columns from multiple tables.
How can I return that LINQ results object and catch it in caller method iterate results and assign to model class?
public ??? GetLocation(string CustomerNum)
{
if (!string.IsNullOrEmpty(CustomerNum))
{
var results = from ca in _context.CUS_ADDRESS
join cad in _context.CUS_ADDRESS_DETAIL on ca.CUS_ADDRESS_ID equals cad.CUS_ADDRESS_ID
where (cad.PRIORITY_SEQ == 0) && (ca.MASTER_CUSTOMER_ID == CustomerNum)
select new
{
CustomerNumber = ca.MASTER_CUSTOMER_ID,
ca.ADDRESS_1,
ca.ADDRESS_2,
ca.ADDRESS_3,
ca.ADDRESS_4,
ca.CITY,
ca.STATE,
ca.COUNTRY_DESCR,
cad.ADDRESS_TYPE_CODE,
cad.ADDRESS_STATUS_CODE
};
return results;
}
else
{
return null;
}
}
Caller method
var results = Data.GetLocation(CustomerNum)
if (results.Any())
{
var location = results.FirstOrDefault();
.....
.....
}
What will be the GetLocation return type?
Depending on how you are actually using the results, you could return an IQueryable instead of IQueryable<T>.
I've used this in some situations (using IEnumerable), like WebForms, that have dynamic binding (either through Eval or by using a BoundField for instance.
You are creating an anonymous object with select new, you can't return a collection of anonymous object from your function, instead you have to create a class which would have all the properties from your select statement and then return IQueryable<YourClass>
class YourClass
{
public int CustomerNumber { get; set; }
public string ADDRESS_1 { get; set; }
//..............
}
and then :
var results = from ca in _context.CUS_ADDRESS
join cad in _context.CUS_ADDRESS_DETAIL on ca.CUS_ADDRESS_ID equals cad.CUS_ADDRESS_ID
where (cad.PRIORITY_SEQ == 0) && (ca.MASTER_CUSTOMER_ID == CustomerNum)
select new YourClass
{
CustomerNumber = ca.MASTER_CUSTOMER_ID,
ADDRESS_1 = ca.ADDRESS_1,
//...............
And modify your function return type as:
public IQueryable<YourClass> GetLocation(string CustomerNum)
You can look at this question for returning IQueryable or Not
If you didn't feel like creating a class you could use Tuples:
public IEnumerable<Tuple<int, string, string>> GetCustomer(int custId) {
return from p in customers
where p.Id == custId
select new Tuple<int, string, string>(
p.Id,
p.FirstName,
p.LastName
);
}
Though this means that you can't name their fields since you access the data like this:
var customer = GetCustomer(1);
var custId = customer.Item1;
var custFirstName = customer.Item2;
var custLastName = customer.Item3;
Create a custom helper class having all columns as properties. Say its MyClass. Fill this as below. I know this not exactly what you want but will help you to get what you want.
var o= (from c in context.table1
from d in context.table2
where c.key=d.key
select new MyClass
{
Property1=c.abc,
Property2=d.xyz
}).SingleOrDefault();
Or write your joins and where in such a way that it will give you only single row fron db.
In the function you are creating an anonymous object and hence cannot be used in caller without some methods of reflection. But it will be much easier to return an object like
public class CustomerLocation
{
public string CustomerNumber {get; set;}
// and so on
}
which will can be placed in a common layer and accessed by both caller and sender and use properties explicitly.
For this your function is better be
public IQueryable<CustomerLocation> GetLocation(string CustomerNum)
{
// your code here
}

How to apply the same query to different entities?

So, I'm beggining to use EF, and I'm developing an application using it as ORM. The thing is I haven't had much time to dig into the documentation (I Plan to, in due time)and I'm kind of lost in some things. For example, I have these two queries:
public static int GetNextPadlockNumber()
{
LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from p in entities.PadLocks select p.PadlockNumber).DefaultIfEmpty(0).Max();
return (int)query + 1;
}
public static Data.PadLock GetPadLockByNumber(int number)
{
Data.LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from p in entities.PadLocks where p.PadlockNumber == number select p).FirstOrDefault();
return query;
}
and
public static int GetNextLockerNumber()
{
LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from l in entities.Lockers select l.LockerNumber).DefaultIfEmpty(0).Max();
return (int)query+1;
}
public static Data.Locker GetLockerByNumber(int number)
{
Data.LockersDBEntities1 entities = new LockersDBEntities1();
var query = (from l in entities.Lockers where l.LockerNumber == number select l).FirstOrDefault();
return query;
}
And the thing is They're Exactly the same query. Isn't there Any way to do this without having to specify that I want a locker or a padlock? Thanks in advance heroes.
You could have used the LockerNumber and PadlockNumber as Identity Autoincrement fields since that is what you are doing, and then named them the same (Id for instance).
And that way the need to "get next locker number" is no longer necessary since every new entity inserted will get the next available number and with a Repository pattern you could have those methods as common methods in the Base Repository class as something like this:
public IEntity GetEntityById(int number)
{
return ObjectSet.Single(x => x.Id == number);
}
One of the great things with EF is that you can do things like that. It's not trivial, and it will take some trial and error, but you can abstract a lot of the database complexities away.
Now, assuming that LockersDBEntities1 is a DbContext, you could something like this:
public static class LockersDBEntities1Extensions
{
public static int GetNextNumber<T>(
this LockersDBEntities1 context,
Expression<Func<T, int>> getNumberExpression)
{
var query = (from item in context.Set<T>
select getNumberExpression(item))
.DefaultIfEmpty(0)
.Max();
return (int)query + 1;
}
}
and use it like:
int nextPadlockNumber = new LockersDBEntities1()
.GetNextNumber<Padlock>(p => p.PadlockNumber)
and
int nextPadlockNumber = new LockersDBEntities1()
.GetNextNumber<Locker>(l => l.LockerNumber)
The getNumberExpression expression is needed because there is no common way to access the number across all entities. It might be overdesign, but if that is an issue I would do something like this:
//this should be a better name, to reflect the real scenarion
interface ILockNumberProvider
{
int Number {get; }
}
and implement that interface on Locker, Padlock and other classes that need to provide lock numbers. Then, in the method above the expression can be omitted, and a generic constraint can be used, like:
public static class LockersDBEntities1Extensions
{
public static int GetNextNumber<T>(this LockersDBEntities1 context)
where T:ILockNumberProvider
{
var query = (from item in context.Set<T>
select item.Number)
.DefaultIfEmpty(0)
.Max();
return (int)query + 1;
}
}

Categories