Writing a predicate for Linq (2Sqlite) - c#

I have a following problem - limited ORM that imposes coupling of infrastructure to domain objects.
Please note this is a Windows Store Application and Reflection API differs from standard .Net
For example I have to manually implement navigational properties (relations in code) like this (assuming a I have a book aggregate with one-to-many child collection of chapters):
List<Chapters> Chapters
{get {return db.Query<Chapters>.Where(b => b.BookId == this.Id);}}
I want to reduce this coupling be the means of generic extension method, that will retrieve child entities for parent, like
IEnumerable<T> GetChildren<TParent,TChild>(this TParent parent)
where TParent, TChild : Entity
I can assume that all Entities have an Id primary key property and foreign key is composed of Parent entity name and Id ("BookId")
How do I implement a predicate for db.Query.Where Linq method in this extension method, assuming this parameter is parent entity?

Something like so (simplified version):
public static TChild GetHierarchyChild<TParent, TChild>(this TParent parent)
{
var pType = typeof(TParent);
var chType = typeof(TChild);
var chPropInfo = pType
.GetProperties()
.FirstOrDefault(p => p.PropertyType == chType);
if (chPropInfo == null)
{
return default(TChild);
}
return (TChild)chPropInfo.GetValue(parent);
}
public class A
{
public IEnumerable<B> Bs
{
get
{
return new[] { new B(1) };
}
}
}
public class B
{
public B(int id)
{
Id = id;
}
public int Id { get; protected set; }
}
an example:
var a = new A();
var bs = GetHierarchyChild<A, IEnumerable<B>>(a);
bs.ToString();

This works for me but unfortunately Sqlite-Net does not support expressions of this type in its Linq2Db implementation. It crashes on the last statement. I will have to rewrite this part to SQL.
private const string keyName = "Id";
public static async Task<IEnumerable<TChild>> GetChildrenAsync<TParent, TChild>(this TParent parent)
where TParent : Entity
where TChild : Entity, new()
{
var parentType = typeof (TParent);
var parentName = parentType.GetTypeInfo().Name;
var parentKeyValue = (int)parentType.GetRuntimeProperty(keyName).GetValue(parent);
var foreignKeyName = String.Format("{0}{1}", parentName, keyName);
var childProperty = typeof(TChild).GetRuntimeProperty(foreignKeyName);
var connection = DbConnection.Current;
var query = connection.Table<TChild>().Where(c => (int)childProperty.GetValue(c) == parentKeyValue);
return await query.ToListAsync();
}

Related

C#: Reflect class by its name stored in string variable

I want to reflect model classes of a MVC project by its name which I have in a string variable refName. Currently I am using switch case to use those class as template i.e <T>. How can we do this part better, so that if a new class comes in then I don't want to insert a new case in this switch statement.
Basically, what I want to do is to collect data from cosmos db into a specific template class format according to a field gRefType. Here's what I have done:
IEnumerable<Object> itemsRefDetail;
switch (refName)
{
case "AMCaseStatus":
itemsRefDetail = await DocumentDBRepository<AMCaseStatus>.GetItemRefDetailAsync(p => p.GrefType == refName && p.Tag == tag, collectionId);
break;
case "AMcaseSubStatus":
itemsRefDetail = await DocumentDBRepository<AMcaseSubStatus>.GetItemRefDetailAsync(p => p.GrefType == refName && p.Tag == tag, collectionId);
break;
case "AMRole":
itemsRefDetail = await DocumentDBRepository<AMRole>.GetItemRefDetailAsync(p => p.GrefType == refName && p.Tag == tag, collectionId);
break;
}
As you can see in the above code, Template class used in each case is as same as case value. All the classes have both the properties(GrefType and Tag) in common.
Here is the DocumentDbRepository class:
public static class DocumentDBRepository<T> where T : class
{
public static async Task<IEnumerable<T>> GetItemRefDetailAsync(Expression<Func<T, bool>> predicate, string collectionId)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, collectionId))
.Where(predicate)
.AsDocumentQuery();
FeedResponse<T> privilegeQueryResponse = await query.ExecuteNextAsync<T>();
return privilegeQueryResponse;
}
}
I made an example of how to reflect generic types, The only thing you should do is, wrap up the GrefType and Tag into a baseclass/interface. This might give you a startup:
The BaseType is the basetype of your data, which contains the fields you can use in your lambda function.
Data is your data service (client.CreateDocumentQuery), in this case some test data.
DocumentDBRepository contains the static SelectData method, which executes the lambda function passed as parameter.
MyLookupThing is derived from the BaseType, used in reflection.
TestReflection does the reflection and executes it.
Here's the code:
// The basetype of an item (which contains de RefName etc)
public class BaseType
{
public string RefName { get; set; }
public string Tag { get; set; }
}
// static T service with some testdata (mock)
public static class Data<T> where T : BaseType
{
public static List<T> MyData { get; } = new List<T>();
static Data()
{
// create an item
var item = Activator.CreateInstance<T>();
item.RefName = "Test";
item.Tag = "Bla";
MyData.Add(item);
var item2 = Activator.CreateInstance<T>();
item2.RefName = "SomethingElse";
item2.Tag = "TagThing";
MyData.Add(item2);
var item3 = Activator.CreateInstance<T>();
item3.RefName = "Test2";
item3.Tag = "Bla2";
MyData.Add(item3);
}
}
// the generic class which uses the basetype as generic
public static class DocumentDBRepository<T> where T : BaseType
{
public static IEnumerable<T> SelectData(Func<T, bool> predicate)
{
// some static test data
return Data<T>.MyData.Where(predicate);
}
}
// your derived class from BaseType
public class MyLookupThing : BaseType
{
}
class TestReflection
{
public TestReflection()
{
// you can create more classes derived from BaseType
var typeStr = "TestRef.MyLookupThing";
// resolve the type:
var lookupType = (from ass in AppDomain.CurrentDomain.GetAssemblies()
from t in ass.GetTypes()
where t.FullName == typeStr
select t).First();
// get the type of the generic class
var myType = typeof(DocumentDBRepository<>);
// create a generic type
var myGenericType = myType.MakeGenericType(lookupType);
var method = myGenericType.GetMethod("SelectData", BindingFlags.Static | BindingFlags.Public);
// Create the function (with the BaseType)
var func = new Func<BaseType, bool>(item => item.RefName.StartsWith("Test"));
// invoke the method of the generic class
IEnumerable<BaseType> result = (IEnumerable<BaseType>)method.Invoke(null, new object[] { func });
// show the results
foreach (var item in result)
Console.WriteLine(item.RefName);
Console.ReadKey();
}
}
Which will give:
Test
Test2
as result
Here is how you can create this expression that limits the query to the type:
internal static Expression<Func<TEntity, bool>> CreateTypeSpesificExpression<TEntity>(string refName) where TEntity : class
{
var parameter = Expression.Parameter(typeof(IAmObject));
var member = Expression.Property(parameter, nameof(IAmObject.GrefType));
var contant = Expression.Constant(refName);
var body = Expression.Equal(member, contant);
var extra = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
return extra;
}
This is all assuming that your AM classes have the same interface.
Once you do that you can simple add this in your where clause.
Your query should look like this:
IEnumerable<Object> itemsRefDetail = await DocumentDBRepository<AMCaseStatus>.GetItemRefDetailAsync(p => CreateTypeSpesificExpression(refName) && p.Tag == tag, collectionId);
Also something worth pointing out is that Cosmonaut already has native support for type based collection sharing.

Obtaining an object's type in an extension method on compile-time for generic type usage

I have the following extension method in order for down-casting, mapping parent's properties to child with AutoMapper
using AutoMapper; /**/
public static TChild Downcast<TChild, TParent>(this TParent parent) {
var config = new MapperConfiguration(c => c.CreateMap<TParent, TChild>());
var mapper = config.CreateMapper();
return mapper.Map<TChild>(parent);
}
It works pretty well as expected (sample usage):
var parent = new Parent{ Name = "Bob" };
var child = parent.Downcast<Child, Parent>();
...
Assert.AreEqual(parent.Name, child.Name);
What I'm curious about is, I feel like there should be some way to actually have information about the compile time type of parent, without supplying it, after all, the class is where I call from, and it is-(should be) known at compile time.
So are there any way that I'd simplify this into something such:
public static TChild Downcast<TChild>(this TParent parent) where TParent : caller {
var config = new MapperConfiguration(c => c.CreateMap<TParent, TChild>());
var mapper = config.CreateMapper();
return mapper.Map<TChild>(parent);
}
And use it such:
var child = parent.Downcast<Child>();
Thank you for your time.
I solved this with introducing a middle struct, structure to casting:
public static Downcasting<TParent> Downcast<TParent>(this TParent parent) {
return new Downcasting<TParent>(parent);
}
public struct Downcasting<TParent> {
private readonly TParent parent;
public Downcasting(TParent parent) { this.parent = parent; }
public TChild To<TChild>() {
var config = new MapperConfiguration(c => c.CreateMap<TParent, TChild>());
var mapper = config.CreateMapper();
return mapper.Map<TChild>(parent);
}
}
This way I can make use of:
var parent = new Parent{ Name = "Bob" };
var child = parent.Downcast().To<Child>();
...
Assert.AreEqual(parent.Name, child.Name);
However I'm still curious about for other possible solutions.

Can I make the following IQueryable linq statment generic

Is there a way I can make the following db query builder generic?
private IQueryable<Foo> ByName(IQueryable<Foo> dbQuery, Query query)
{
string[] searchTerms = query.Data.Replace(" ","").ToLower().Split(',');
if (query.Exclude)
{
return dbQuery.Where(x => searchTerms.All(
y => y != x.Name.Replace(" ", "").ToLower()));
}
return dbQuery.Where(x => searchTerms.Any(
y => y == x.Name.Replace(" ", "").ToLower()));
}
I've got the same function for lots of different properties of Foo. ByCounty, ByTown, ByStreet etc etc.
I've written some functions that return linq before like the following
public Expression<Func<Foo, bool>> FoosAreWithinDistanceFromGeocode(
double distance, Geocode geocode)
{
double distanceSquare = distance * distance;
return foo => ( SqlFunctions.Square((double)(
foo.Address.Geocode.Easting - geocode.Easting)) +
SqlFunctions.Square((double)(fooAddress.Geocode.Northing -
geocode.Northing)) ) <= distanceSquare;
}
But I can't seem to find if the Linq-to-SQL stuff can't use generics or if it's possible to pass properties as generics and that kind of thing.
EDIT: I have this working generically for a single search term.
Where [query.Data == "Foo1"]
return dbQuery.Where(SearchMatch("Name", query.Data));
public Expression<Func<Foo, bool>> SearchMatch(string propertyName, string searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
var prop = Expression.Property(foo, propertyName);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
Anyone have any ideas how to make it work for an array of strings?
You need to define an interface that exposes the properties that you want to access, like so:
public interface IHaveName
{
string Name { get; }
}
Then, on your classes, you would implement the interface:
public class Foo : IHaveName
If you're using the classes generated from a DBML file, these classes are marked with the partial keyword so implementing the interface is as simple as creating a new file, and inserting:
public partial class Foo : IHaveName
Since the property is already declared as public in the other .cs file generated from the .dbml file, the interface is implemented implicitly.
Finally, you would rewrite your ByName method to take a generic type parameter with a constraint that it implement your interface IHaveName:
private IQueryable<T> ByName<T>(IQueryable<T> dbQuery, Query query)
where T : IHaveName
{
// Everything else is the same.
For your other properties (and methods which use them), you could aggregate them together into one interface, or separate them out, depending on your needs.
Based on your edit, if you want to create an expression dynamically, you don't have to give up compile-time safety:
public Expression<Func<Foo, bool>> SearchMatch(
Expression<Func<Foo, string>> property, string searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
// Get the property info from the property expression.
var prop = Expression.Property(foo,
(property.Body as MemberExpression).Member as PropertyInfo);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
Which you then call like so:
var expression = SearchMatch(f => f.Name, "searchTerm");
This ensures that the properties that you are passing to SearchMatch actually exist on Foo. Note if you wanted to make this generic for other scalar property types, you would do the following:
public Expression<Func<Foo, bool>> SearchMatch<T>(
Expression<Func<Foo, T>> property, T searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
// Get the property info from the property expression.
var prop = Expression.Property(foo,
(property.Body as MemberExpression).Member as PropertyInfo);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
If I understood what you are trying to achieve reflection might help you. At least if you play nice. Here's a simplified but working example
internal class Program
{
private class Data
{
public string Name { get; set; }
public string Address { get; set; }
public override string ToString()
{
return String.Format("My name is {0} and I'm living at {1}", Name, Address);
}
}
static Expression<Func<Data,bool>> BuildExpression(PropertyInfo prop, IQueryable<string> restrict)
{
return (data) => !restrict.Any(elem => elem == prop.GetValue(data, null));
}
static IQueryable<Data> FilterData(IQueryable<Data> input, Expression<Func<Data, bool>> filter)
{
return input.Where(filter);
}
public static void Main (string[] args)
{
List<Data> list = new List<Data>()
{
new Data {Name = "John", Address = "1st Street"},
new Data {Name = "Mary",Address = "2nd Street"},
new Data {Name = "Carl", Address = "3rd Street"}
};
var filterByNameExpression = BuildExpression(typeof (Data).GetProperty("Name"),
(new List<string> {"John", "Carl"}).AsQueryable());
var filterByAddressExpression = BuildExpression(typeof(Data).GetProperty("Address"),
(new List<string> { "2nd Street"}).AsQueryable());
IQueryable<Data> filetedByName = FilterData(list.AsQueryable(), filterByNameExpression);
IQueryable<Data> filetedByAddress = FilterData(list.AsQueryable(), filterByAddressExpression);
Console.WriteLine("Filtered by name");
foreach (var d in filetedByName)
{
Console.WriteLine(d);
}
Console.WriteLine("Filtered by address");
foreach (var d in filetedByAddress)
{
Console.WriteLine(d);
}
Console.ReadLine();
}
Hovewer, I'\m almost sure it won't work with LINQ-to-SQL. One way to workaround it is to materialize the IQueryable before passing it to such filtering methods (e.g. by calling ToList on them).

EF Many-to-many dbset.Include in DAL on GenericRepository

I can't get the QueryObjectGraph to add INCLUDE child tables if my life depended on it...what am I missing? Stuck for third day on something that should be simple :-/
DAL:
public abstract class RepositoryBase<T> where T : class
{
private MyLPL2Context dataContext;
private readonly IDbSet<T> dbset;
protected RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
DataContext.Configuration.LazyLoadingEnabled = true;
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected MyLPL2Context DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public IQueryable<T> QueryObjectGraph(Expression<Func<T, bool>> filter,
params string[] children)
{
foreach (var child in children)
{
dbset.Include(child);
}
return dbset.Where(filter);
}
...
DAL repositories
public interface IBreed_TranslatedSqlRepository : ISqlRepository<Breed_Translated>
{
}
public class Breed_TranslatedSqlRepository : RepositoryBase<Breed_Translated>,
IBreed_TranslatedSqlRepository
{
public Breed_TranslatedSqlRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{}
}
BLL Repo:
public IQueryable<Breed_Translated>
QueryObjectGraph(Expression<Func<Breed_Translated, bool>> filter,
params string[] children)
{
return _r.QueryObjectGraph(filter, children);
}
Controller:
var breeds1 = _breedTranslatedRepository
.QueryObjectGraph(b => b.Culture == culture, new string[] { "AnimalType_Breed" })
.ToList();
I can't get to Breed.AnimalType_Breed.AnimalTypeId ..I can drill as far as Breed.AnimalType_Breed then the intelisense expects an expression?
Cues if any, DB Tables: bold is many-to-many
Breed, Breed_Translated, AnimalType_Breed, AnimalType, ...
AnimalBreed_Type represents many to many relation so the AnimalBreed_Type property in Breed_Translated entity is collection! The collection type doesn't have your table properties. You must use First or Single to get single related entity from this collection and check it's AnimalTypeId.
If you look at Include it has a return type. Linq is mostly functional so it will NOT alter any objects but rather return new ones. You need to store this new object instead.
Try:
var query = dbset.Where(filter);
foreach (var child in children)
{
query = query.Include(child);
}
return query;
OTHER NOTE: You can simplify this:
var breeds1 = _breedTranslatedRepository
.QueryObjectGraph(b => b.Culture == culture, new string[] { "AnimalType_Breed" })
.ToList();
To
var breeds1 = _breedTranslatedRepository
.QueryObjectGraph(b => b.Culture == culture, "AnimalType_Breed")
.ToList();
When you methods takes a params string[] argument

generic GetById for complex PK

I am looking a way to create Generic GetById that get params object[] as parameter, knows to find the key/s field/s and know to find the relevant entity.
In the way to find a solution I thought on a generic method that returns the PK fields definition and a generic method that can return the entity based on fields.
I am looking for something I can use in table with one or more fields as primary key.
EDIT
one or more fields as primary key example =
table Customers have (CompanyId, CustomerName, Address, CreateDate).
The primary key of Customers are CompanyId are CustomerName.
I am looking for generic GetById that will know to handle also those such of tables.
You can't get "generic" approach if you don't know how many members is in the key and what types do they have. I modified my solution for single key to multiple keys but as you can see it is not generic - it uses order in which keys are defined:
// Base repository class for entity with any complex key
public abstract class RepositoryBase<TEntity> where TEntity : class
{
private readonly string _entitySetName;
private readonly string[] _keyNames;
protected ObjectContext Context { get; private set; }
protected ObjectSet<TEntity> ObjectSet { get; private set; }
protected RepositoryBase(ObjectContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
Context = context;
ObjectSet = context.CreateObjectSet<TEntity>();
// Get entity set for current entity type
var entitySet = ObjectSet.EntitySet;
// Build full name of entity set for current entity type
_entitySetName = context.DefaultContainerName + "." + entitySet.Name;
// Get name of the entity's key properties
_keyNames = entitySet.ElementType.KeyMembers.Select(k => k.Name).ToArray();
}
public virtual TEntity GetByKey(params object[] keys)
{
if (keys.Length != _keyNames.Length)
{
throw new ArgumentException("Invalid number of key members");
}
// Merge key names and values by its order in array
var keyPairs = _keyNames.Zip(keys, (keyName, keyValue) =>
new KeyValuePair<string, object>(keyName, keyValue));
// Build entity key
var entityKey = new EntityKey(_entitySetName, keyPairs);
// Query first current state manager and if entity is not found query database!!!
return (TEntity)Context.GetObjectByKey(entityKey);
}
// Rest of repository implementation
}
I don't know how useful this would be because it is generic but you could do this:
public TEntity GetById<TEntity>(params Expression<Func<TEntity, bool>>[] keys) where TEntity : class
{
if (keys == null)
return default(TEntity);
var table = context.CreateObjectSet<TEntity>();
IQueryable<TEntity> query = null;
foreach (var item in keys)
{
if (query == null)
query = table.Where(item);
else
query = query.Where(item);
}
return query.FirstOrDefault();
}
and then you could call it like this:
var result = this.GetById<MyEntity>(a => a.EntityProperty1 == 2, a => a.EntityProperty2 == DateTime.Now);
Disclaimer: this really isn't a GetByid, it's really a "let me give you a couple parameters and give me the first entity that matches". But that being said, it uses generics and it will return an entity if there is a match and you search based on primary keys.
I think you can't implement such thing because you won't be able to join between each passed value with the appropriate key field.
I'd suggest using custom method for each entity:
Assuming Code and Name are keys in Person table:
public IEnumerable<Person> ReadById(int code, string name)
{
using (var entities = new Entities())
return entities.Persons.Where(p => p.Code = code && p.Name = name);
}
Ok this is my second stab at it. I think this would work for you.
public static class QueryExtensions
{
public static Customer GetByKey(this IQueryable<Customer> query, int customerId,string customerName)
{
return query.FirstOrDefault(a => a.CustomerId == customerId && a.CustomerName == customerName);
}
}
So the beauty behind this extension method is you can now do this:
Customer customer = Db.Customers.GetByKey(1,"myname");
You obviously have to do this for every type, but probably worth it if you need it :)
I think the set up is a bit but I do think creating a reusable pattern will pay off in the long run. I just wrote this up and haven't tested, but I based off a searching pattern I use a lot.
Required Interfaces:
public interface IKeyContainer<T>
{
Expression<Func<T, bool>> GetKey();
}
public interface IGetService<T>
{
T GetByKey(IKeyContainer<T> key);
}
Example Entities:
public class Foo
{
public int Id { get; set; }
}
public class ComplexFoo
{
public int Key1 { get; set; }
public int Key2 { get; set; }
}
Implementation Example:
public class FooKeyContainer : IKeyContainer<Foo>
{
private readonly int _id;
public FooKeyContainer(int id)
{
_id = id;
}
public Expression<Func<Foo, bool>> GetKey()
{
Expression<Func<Foo, bool>> key = x => x.Id == _id;
return key;
}
}
public class ComplexFooKeyContainer : IKeyContainer<ComplexFoo>
{
private readonly int _id;
private readonly int _id2;
public ComplexFooKeyContainer(int id, int id2)
{
_id = id;
_id2 = id2;
}
public Expression<Func<ComplexFoo, bool>> GetKey()
{
Expression<Func<ComplexFoo, bool>> key = x => x.Key1 == _id && x.Key2 == _id2;
return key;
}
}
public class ComplexFooService : IGetService<ComplexFoo>
{
public ComplexFoo GetByKey(IKeyContainer<ComplexFoo> key)
{
var entities = new List<ComplexFoo>();
return entities.Where(key.GetKey()).FirstOrDefault();
}
}
Usage:
var complexFoo = ComplexFooService.GetByKey(new ComplexFooKeyContainer(1, 2));

Categories

Resources