entity framework core 3 dynamic order by not working - entity-framework

public class Branch
{
[Sortable(OrderBy = "BranchId")]
public long BranchId { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Type { get; set; }
}
this is my Model class and I also create a custom attribute
public class SortableAttribute : Attribute
{
public string OrderBy { get; set; }
}
now i create a pagination with orderby descending but this code not working
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source,
GeneralPagingRequest pagingRequest, int indexFrom = 0,
CancellationToken cancellationToken = default(CancellationToken))
{
if (indexFrom > pagingRequest.PageNumber)
{
throw new ArgumentException(
$"indexFrom: {indexFrom} > pageNumber: {pagingRequest.PageNumber}, must indexFrom <= pageNumber");
}
var count = await source.CountAsync(cancellationToken).ConfigureAwait(false);
var items = source.Skip(((pagingRequest.PageNumber - 1) - indexFrom) * pagingRequest.PageSize)
.Take(pagingRequest.PageSize);
var props = typeof(T).GetProperties();
PropertyInfo orderByProperty;
orderByProperty =
props.FirstOrDefault(x=>x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);
if (pagingRequest.OrderBy == "desc")
{
items = items.OrderBy(x => orderByProperty.GetValue(x));
}
else
{
items = items.OrderBy(x => orderByProperty.GetValue(x));
}
var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
var pagedList = new PagedList<T>
{
PageNumber = pagingRequest.PageNumber,
PageSize = pagingRequest.PageSize,
IndexFrom = indexFrom,
TotalCount = count,
Items = result,
TotalPages = (int) Math.Ceiling(count / (double) pagingRequest.PageSize)
};
return pagedList;
}
but the result variable create exception

.OrderBy() requires a delegate that would tell it HOW to select a key, not the key value itself. So you are looking at some meta-programming here.
Naturally, you will look at building a dynamic LINQ Expression tree that will fetch a property for you:
// your code up above
PropertyInfo orderByProperty = props.FirstOrDefault(x => x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);
var p = Expression.Parameter(typeof(T), "x"); // you define your delegate parameter here
var accessor = Expression.Property(p, orderByProperty.GetMethod); // this basically becomes your `x => x.BranchId` construct
var predicate = Expression.Lambda(accessor, p).Compile(); // here's our problem: as we don't know resulting type at compile time we can't go `Expression.Lambda<T, long>(accessor, p)` here
if (pagingRequest.OrderBy == "desc")
{
items = items.OrderByDescending(x => predicate(x)); // passing a Delegate here will not work as OrderBy requires Func<T, TKey>
}
else
{
items = items.OrderBy(x => predicate(x)); // passing a Delegate here will not work as OrderBy requires Func<T, TKey>
}
var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
// your code down below
Problem with the above code - you don't know TKey upfront. Therefore we will have to go a level deeper and build the whole items.OrderBy(x => x.BranchId) expression dynamically. The biggest leap of faith here will be the fact the OrderBy is an extension method and it actually resides on IQueryable type. After you've got the generic method reference, you will need to build a specific delegate type when you know your property type. So your method becomes something like this:
public static class ExtToPagedListAsync
{
private static readonly MethodInfo OrderByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderBy" && method.GetParameters().Length == 2); // you need your method reference, might as well find it once
private static readonly MethodInfo OrderByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderByDescending" && method.GetParameters().Length == 2); // you need your method reference, might as well find it once
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source, GeneralPagingRequest pagingRequest, int indexFrom = 0, CancellationToken cancellationToken = default(CancellationToken))
{
if (indexFrom > pagingRequest.PageNumber)
{
throw new ArgumentException(
$"indexFrom: {indexFrom} > pageNumber: {pagingRequest.PageNumber}, must indexFrom <= pageNumber");
}
var count = await source.CountAsync(cancellationToken).ConfigureAwait(false);
var items = source.Skip(((pagingRequest.PageNumber - 1) - indexFrom) * pagingRequest.PageSize)
.Take(pagingRequest.PageSize);
var props = typeof(T).GetProperties();
PropertyInfo orderByProperty = props.FirstOrDefault(x => x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);
var p = Expression.Parameter(typeof(T), "x");
var accessor = Expression.Property(p, orderByProperty.GetMethod);
var predicate = Expression.Lambda(accessor, p); // notice, we're not yet compiling the predicate. we still want an Expression here
// grab the correct method depending on your condition
MethodInfo genericMethod = (pagingRequest.OrderBy == "desc") ? OrderByDescendingMethod.MakeGenericMethod(typeof(T), orderByProperty.PropertyType)
:OrderByMethod.MakeGenericMethod(typeof(T), orderByProperty.PropertyType);
object ret = genericMethod.Invoke(null, new object[] { items, predicate });
items = (IQueryable<T>)ret; // finally cast it back to Queryable with your known T
var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
var pagedList = new PagedList<T>
{
PageNumber = pagingRequest.PageNumber,
PageSize = pagingRequest.PageSize,
IndexFrom = indexFrom,
TotalCount = count,
Items = result,
TotalPages = (int)Math.Ceiling(count / (double)pagingRequest.PageSize)
};
return pagedList;
}
}
I must disclose I did get some inspiration from this answer here, so do check it out for further reading.

Related

Analyse the tables involved in an IQueryable

I have a requirement where I need to look at all of the tables involved in a query about to be run by EF Core via an IQueryable
Has anyone ever been able to do this?
Lets use an example
var cars = await (from car in DbContext.Cars
from salesperson in DbContext.SalesPersons.Where(x=>x.Id == car.SalesPersonId)
.Select(x=>x.Car));
Lets now say that there is a CountryId column in the salespersons table and the Cars table
I need to detect that the IQueryable above is using Cars and SalesPerson
Then I will add to the IQueryable so it becomes
var cars = await (from car in DbContext.Cars
from salesperson in DbContext.SalesPersons.Where(x=>x.Id == car.SalesPersonId).Select(x=>x.Car)
.Where(car.CountryId = 1).Where(salesPerson.CountryId = 1);
So we are basically adding a filter at runtime automatically
Cheers
Paul
It is not needed to analyze LINQ query for your task. For filtering by tenant there is Global Query Filters. After configuring EF Core should apply filter for every entity which has defined Query Filter.
public class TenantContext : DbContext
{
public int? TenantId { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
... // entities configuration
modelBuilder.Entity<Some>().HasQueryFilter(e => TenantId == null || TenantId == e.TenantId)
}
}
All that you need before querying data - set TenantId for context
context.TeantId = 2;
context.Some.ToList() // table will be filtered by TenantId == 2
I have prepared small sample how to apply tenant filter for all entities in one function call:
public class TenantContext : DbContext
{
public int? TenantId { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
... // entities configuration
ApplyTenantQueryFilters(modelBuilder, "TenantId", () => TenantId);
}
private void ApplyTenantQueryFilters<TProp>(ModelBuilder builder, string tenantPropName, Expression<Func<TProp>> tenantPropExpr)
{
foreach (var entityType in builder.Model.GetEntityTypes())
{
var tenantProp = entityType.GetProperties().FirstOrDefault(p => p.Name == tenantPropName);
if (tenantProp == null)
continue;
var entityParam = Expression.Parameter(entityType.ClrType, "e");
var contextTenantPropAccess = tenantPropExpr.Body;
var propertyExpression = GetPropertyExpression(entityParam, tenantProp);
if (propertyExpression.Type != contextTenantPropAccess.Type)
propertyExpression = Expression.Convert(propertyExpression, contextTenantPropAccess.Type);
// ctx.TenantId == null || ctx.TenantId == e.TenantId
var filterBody = (Expression)Expression.OrElse(
Expression.Equal(contextTenantPropAccess, Expression.Default(contextTenantPropAccess.Type)),
Expression.Equal(contextTenantPropAccess,
propertyExpression));
var filterLambda = entityType.GetQueryFilter();
// we have to combine filters
if (filterLambda != null)
{
filterBody = ReplacingExpressionVisitor.Replace(entityParam, filterLambda.Parameters[0], filterBody);
filterBody = Expression.AndAlso(filterLambda.Body, filterBody);
filterLambda = Expression.Lambda(filterBody, filterLambda.Parameters);
}
else
{
filterLambda = Expression.Lambda(filterBody, entityParam);
}
entityType.SetQueryFilter(filterLambda);
}
}
private static Expression GetPropertyExpression(Expression objExpression, IProperty property)
{
Expression propExpression;
if (property.PropertyInfo == null)
{
// 'property' is Shadow property, so call via EF.Property(e, "name")
propExpression = Expression.Call(typeof(EF), nameof(EF.Property), new[] { property.ClrType },
objExpression, Expression.Constant(property.Name));
}
else
{
propExpression = Expression.MakeMemberAccess(objExpression, property.PropertyInfo);
}
return propExpression;
}
}

Asp.Net-Core + MongoDb - How to search database by "code" and return the original url?

I am unsure how to go about searching for the "Code" stored in my Database in order to return the "OriginalUrl".
I know I can search for the ObjectId but I want to be able to search by the "Code" assigned to that ObjectId.
Currently I have a working program that takes a Url as well as a "title" and sends it to the database:
It is assigned an Objectid _id and a randomly generated 12 character "Code":
If it helps this is my Controller class:
namespace ShortenUrls.Controllers
{
[Route("api/codes")]
public class ShortUrlsController : Controller
{
private readonly ShortUrlRepository _repo;
public ShortUrlsController(ShortUrlRepository repo)
{
_repo = repo;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
var su = await _repo.GetAsync(id);
if (su == null)
return NotFound();
return Ok(su);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] ShortUrl su)
{
await _repo.CreateAsync(su);
return Ok(su);
}
}
And Repository class:
namespace ShortenUrls.Models.Repository
{
public class ShortUrlRepository
{
private const string alphabet = "23456789bcdfghjkmnpqrstvwxyz-_";
private static readonly Random rand = new Random();
private readonly Database _db;
public ShortUrlRepository(Database db)
{
_db = db;
}
private static string GenerateCode()
{
const int codeLength = 12;
var chars = new char[codeLength];
for (var i = 0; i < codeLength; i++)
{
chars[i] = alphabet[rand.Next(0, alphabet.Length)];
}
return new string(chars);
}
public Task<ShortUrl> GetAsync(string id)
{
var objId = ObjectId.Parse(id);
return _db.Urls.Find(x => x.Id == objId).FirstOrDefaultAsync();
}
public Task CreateAsync(ShortUrl su)
{
su.Code = GenerateCode();
return _db.Urls.InsertOneAsync(su);
}
}
Just use a filter. Doing it this way let's you create a query specifically for the "code".
public async Task<ShortUrl> GetAsync(string code)
{
var filterBuilder = new FilterDefinitionBuilder<ShortUrl>();
var filter = filterBuilder.Eq(s => s.Code, code);
var cursor = await _db.Urls.FindAsync(filter);
return await cursor.FirstOrDefaultAsync();
}
Assuming you already know the code when calling this and that ObjectId is created on InsertOneAsync call. First change your repository to take Code as searchable input.
public Task<ShortUrl> GetAsync(string code)
{
return await _db.Urls.FirstOrDefaultAsync(x => x.Code == code);
}
Then change your controller Get to this:
[HttpGet("{code}")]
public async Task<IActionResult> Get(string code)
{
var su = await _repo.GetAsync(code);
if (su == null)
return NotFound();
return Ok(su);
}
In your controller you can access su.OriginalUrl if you need to only return that after getting the object.
Then in postman you can just call http://localhost:51767/api/codes?code=cmg3fjjr_gtv
Remember only Id works for default url parameters as setup by your default routes in Startup.cs.
app.UseMvc(routes => { /*...*/ })
So this wont work: /api/codes/cmg3fjjr_gtv unless you specifically set up routing or change {code} back to {id}. Readability of your code suffers though.

Using DbContext in MS unit test

I am not sure how to fit EF into my business logic tests. Let me give an example of how it works at runtime (no testing, regular application run):
Context.Set<T>.Add(instance);
When I add the entity using the above generic method, an instance is added to context, and EF fixes all the navigation properties behind the scenes. For example, if exists [instance.Parent] property, and [parent.Instances] collection property (1-to-many relationship), EF will automatically add the instance to parent.Instances collection behind the scenes.
My code depends on the [parent.Instances] collection, and if it is empty, it will fail. When I am writing unit tests using MS testing framework, how can I reuse the power of EF, so it can still do its behind-the-scenes job, but uaing the memory as data storage, and not the actual database? I am not really interested whether EF successfully added, modified or deleted something in the database, I am just interested in getting the EF magic on the in-memory sets.
I've been doing this with a mock DbContext and mock DbSet that I've created. They store test data in memory and allow you to do most of the standard things you can do on a DbSet.
Your code that acquires the DbContext initially will have to be changed so that it acquires a MockDbContext when it is running under unit test. You can determine if you are running under MSTest with the following code:
public static bool IsInUnitTest
{
get
{
return AppDomain.CurrentDomain.GetAssemblies()
.Any(assembly =>
assembly.FullName.StartsWith(
"Microsoft.VisualStudio.QualityTools.UnitTestFramework"));
}
}
Here is the code for MockDbContext:
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication5
{
// ProductionDbContext would be DbContext class
// generated by Entity Framework
public class MockDbContext: ProductionDbContext
{
public MockDbContext()
{
LoadFakeData();
}
// Entities (for which we'll provide MockDbSet implementation
// and test data)
public override DbSet<Account> Accounts { get; set; }
public override DbSet<AccountGenLink> AccountGenLinks { get; set; }
public override DbSet<AccountPermit> AccountPermits { get; set; }
public override DbSet<AcctDocGenLink> AcctDocGenLinks { get; set; }
// DbContext method overrides
private int InternalSaveChanges()
{
// Just return 0 in the mock
return 0;
}
public override int SaveChanges()
{
return InternalSaveChanges();
}
public override Task<int> SaveChangesAsync()
{
return Task.FromResult(InternalSaveChanges());
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
// Just ignore the cancellation token in the mock
return SaveChangesAsync();
}
private void LoadFakeData()
{
// Tables
Accounts = new MockDbSet<Account>(this);
Accounts.AddRange(new List<Account>
{
new Account
{
SSN_EIN = "123456789", CODE = "A", accttype = "CD",
acctnumber = "1", pending = false, BankOfficer1 = string.Empty,
BankOfficer2 = null, Branch = 0, type = "18", drm_rate_code = "18",
officer_code = string.Empty, open_date = new DateTime(2010, 6, 8),
maturity_date = new DateTime(2010, 11, 8), HostAcctActive = true,
EffectiveAcctStatus = "A"
},
new Account
{
SSN_EIN = "123456789", CODE = "A", accttype = "DD",
acctnumber = "00001234", pending = false, BankOfficer1 = "BCK",
BankOfficer2 = string.Empty, Branch = 0, type = "05", drm_rate_code = "00",
officer_code = "DJT", open_date = new DateTime(1998, 9, 14),
maturity_date = null, HostAcctActive = true,
EffectiveAcctStatus = "A"
},
new Account
{
SSN_EIN = "123456789", CODE = "A", accttype = "LN", acctnumber = "1",
pending = false, BankOfficer1 = "LMP", BankOfficer2 = string.Empty,
Branch = 0, type = "7", drm_rate_code = null, officer_code = string.Empty,
open_date = new DateTime(2001, 10, 24),
maturity_date = new DateTime(2008, 5, 2), HostAcctActive = true,
EffectiveAcctStatus = "A"
}
});
AccountGenLinks = new MockDbSet<AccountGenLink>(this);
AccountGenLinks.AddRange(new List<AccountGenLink>
{
// Add your test data here if needed
});
AccountPermits = new MockDbSet<AccountPermit>(this);
AccountPermits.AddRange(new List<AccountPermit>
{
// Add your test data here if needed
});
AcctDocLinks = new MockDbSet<AcctDocLink>(this);
AcctDocLinks.AddRange(new List<AcctDocLink>
{
new AcctDocLink { ID = 1, SSN_EIN = "123456789", CODE = "A", accttype = "DD",
acctnumber = "00001234", DocID = 50, DocType = 5 },
new AcctDocLink { ID = 25, SSN_EIN = "123456789", CODE = "6", accttype = "CD",
acctnumber = "1", DocID = 6750, DocType = 5 }
});
}
}
}
And here is the code for MockDbSet:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication5
{
public sealed class MockDbSet<TEntity> : DbSet<TEntity>, IQueryable,
IEnumerable<TEntity>, IDbAsyncEnumerable<TEntity> where TEntity : class
{
public MockDbSet(MockDbContext context)
{
// Get entity set for entity
// Used when we figure out whether to generate
// IDENTITY values
EntitySet = ((IObjectContextAdapter) context).ObjectContext
.MetadataWorkspace
.GetItems<EntityContainer>(DataSpace.SSpace).First()
.BaseEntitySets
.FirstOrDefault(item => item.Name == typeof(TEntity).Name);
Data = new ObservableCollection<TEntity>();
Query = Data.AsQueryable();
}
private ObservableCollection<TEntity> Data { get; set; }
Type IQueryable.ElementType
{
get { return Query.ElementType; }
}
private EntitySetBase EntitySet { get; set; }
Expression IQueryable.Expression
{
get { return Query.Expression; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return Data.GetEnumerator();
}
public override ObservableCollection<TEntity> Local
{
get { return Data; }
}
IQueryProvider IQueryable.Provider
{
get { return new MockDbAsyncQueryProvider<TEntity>(Query.Provider); }
}
private IQueryable Query { get; set; }
public override TEntity Add(TEntity entity)
{
GenerateIdentityColumnValues(entity);
Data.Add(entity);
return entity;
}
public override IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities)
{
foreach (var entity in entities)
Add(entity);
return entities;
}
public override TEntity Attach(TEntity entity)
{
return Add(entity);
}
public override TEntity Create()
{
return Activator.CreateInstance<TEntity>();
}
public override TDerivedEntity Create<TDerivedEntity>()
{
return Activator.CreateInstance<TDerivedEntity>();
}
public override TEntity Find(params object[] keyValues)
{
throw new NotSupportedException();
}
public override Task<TEntity> FindAsync(params object[] keyValues)
{
return FindAsync(CancellationToken.None, keyValues);
}
public override Task<TEntity> FindAsync(CancellationToken cancellationToken, params object[] keyValues)
{
throw new NotSupportedException();
}
private void GenerateIdentityColumnValues(TEntity entity)
{
// The purpose of this method, which is called when adding a row,
// is to ensure that Identity column values are properly initialized
// before performing the add. If we were making a "real" Entity Framework
// Add() call, this task would be handled by the data provider and the
// value(s) would then be propagated back into the entity. In the case
// of this mock, there is nothing that will do that, so we have to make
// this at-least token effort to ensure the columns are properly initialized.
// In SQL Server, an Identity column can be of one of the following
// data types: tinyint, smallint, int, bigint, decimal (with a scale of 0),
// or numeric (with a scale of 0); This method handles the integer types
// (the others are typically not used).
foreach (var member in EntitySet.ElementType.Members.ToList())
{
if (member.IsStoreGeneratedIdentity)
{
// OK, we've got a live one; do our thing.
//
// Note that we'll get the current value of the column and,
// if it is nonzero, we'll leave it alone. We do this because
// the test data in our mock DbContext provides values for the
// Identity columns and many of those values are foreign keys
// in other entities (where we also provide test data). We don't
// want to disturb any existing relationships defined in the test data.
Type columnDataType = null;
foreach (var metadataProperty in member.TypeUsage.EdmType.MetadataProperties.ToList())
{
if (metadataProperty.Name != "PrimitiveTypeKind")
continue;
switch ((PrimitiveTypeKind)metadataProperty.Value)
{
case PrimitiveTypeKind.SByte:
columnDataType = typeof(SByte);
break;
case PrimitiveTypeKind.Int16:
columnDataType = typeof(Int16);
break;
case PrimitiveTypeKind.Int32:
columnDataType = typeof(Int32);
break;
case PrimitiveTypeKind.Int64:
columnDataType = typeof(Int64);
break;
default:
throw new InvalidOperationException();
}
var identityColumnGetter = entity.GetType().GetProperty(member.Name).GetGetMethod();
var identityColumnSetter = entity.GetType().GetProperty(member.Name).GetSetMethod();
Int64 specifiedColumnValue = 0;
switch (columnDataType.Name)
{
case "SByte":
specifiedColumnValue = (SByte)identityColumnGetter.Invoke(entity, null);
break;
case "Int16":
specifiedColumnValue = (Int16)identityColumnGetter.Invoke(entity, null);
break;
case "Int32":
specifiedColumnValue = (Int32)identityColumnGetter.Invoke(entity, null);
break;
case "Int64":
specifiedColumnValue = (Int64)identityColumnGetter.Invoke(entity, null);
break;
}
if (specifiedColumnValue != 0)
break;
Int64 maxExistingColumnValue = 0;
switch (columnDataType.Name)
{
case "SByte":
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (SByte)identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] { (SByte)(++maxExistingColumnValue) });
break;
case "Int16":
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (Int16)identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] { (Int16)(++maxExistingColumnValue) });
break;
case "Int32":
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (Int32)identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] { (Int32)(++maxExistingColumnValue) });
break;
case "Int64":
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (Int64)identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] { (Int64)(++maxExistingColumnValue) });
break;
}
}
}
}
}
IDbAsyncEnumerator<TEntity> IDbAsyncEnumerable<TEntity>.GetAsyncEnumerator()
{
return new MockDbAsyncEnumerator<TEntity>(Data.GetEnumerator());
}
IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
{
return Data.GetEnumerator();
}
public override TEntity Remove(TEntity entity)
{
Data.Remove(entity);
return entity;
}
public override IEnumerable<TEntity> RemoveRange(IEnumerable<TEntity> entities)
{
foreach (var entity in entities)
Remove(entity);
return entities;
}
public override DbSqlQuery<TEntity> SqlQuery(string sql, params object[] parameters)
{
throw new NotSupportedException();
}
}
internal class MockDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
internal MockDbAsyncQueryProvider(IQueryProvider queryProvider)
{
QueryProvider = queryProvider;
}
private IQueryProvider QueryProvider { get; set; }
public IQueryable CreateQuery(Expression expression)
{
return new MockDbAsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new MockDbAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return QueryProvider.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return QueryProvider.Execute<TResult>(expression);
}
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute(expression));
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class MockDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public MockDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{
}
public MockDbAsyncEnumerable(Expression expression)
: base(expression)
{
}
IQueryProvider IQueryable.Provider
{
get { return new MockDbAsyncQueryProvider<T>(this); }
}
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new MockDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
}
internal class MockDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
public MockDbAsyncEnumerator(IEnumerator<T> enumerator)
{
Enumerator = enumerator;
}
public void Dispose()
{
Enumerator.Dispose();
}
public T Current
{
get { return Enumerator.Current; }
}
object IDbAsyncEnumerator.Current
{
get { return Current; }
}
private IEnumerator<T> Enumerator { get; set; }
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Enumerator.MoveNext());
}
}
}
If you are using the EntityFramework-Reverse-POCO-Code-First-Generator from Simon Hughes, with its generated FakeContext, Jeff Prince's approach is still possible with some tweaking. The little difference here is we are using the partial class support and implementing the InitializePartial() methods in the FakeContext and FakeDbSet. The bigger difference is that the Reverse POCO FakeContext does not inherit from a DbContext, so we can't easily get the MetadataWorkspace to know which columns are identities. The answer is to create a 'real' context with a bogus connection string and use that to get the EntitySetBase for the FakeDbSet. This should be pasted inside the proper namespace of a new source file, renaming the context, and you shouldn't need to do anything further in the rest of your project.
/// <summary>
/// This code will set Identity columns to be unique. It behaves differently from the real context in that the
/// identities are generated on add, not save. This is inspired by https://stackoverflow.com/a/31795273/1185620 and
/// modified for use with the FakeDbSet and FakeContext that can be generated by EntityFramework-Reverse-POCO-Code-
/// First-Generator from Simon Hughes.
///
/// Aside from changing the name of the FakeContext and the type used to in its InitializePartial() as
/// the 'realContext' this file can be pasted into another namespace for a completely unrelated context. If you
/// have additional implementation for the InitializePartial methods in the FakeContext or FakeDbSet, change the
/// name to InitializePartial2 and they will be called after InitializePartial is called here. Please don't add
/// code unrelated to the above purpose to this file - make another file to further extend the partial class.
/// </summary>
partial class FakeFooBarBazContext
{
/// <summary> Initialization of FakeContext to handle setting an identity for columns marked as
/// <c>IsStoreGeneratedIdentity</c> when an item is Added to the DbSet. If this signature
/// conflicts with another partial class, change that signature to implement
/// <see cref="InitializePartial2"/>, as that will be called when this is complete. </summary>
partial void InitializePartial()
{
// Here we need to get a 'real' ObjectContext so we can get the metadata for determining
// identity columns. Since FakeContext doesn't inherit from DbContext, create
// the real one with a bogus connection string.
using (var realContext = new FooBarBazContext("Server=."))
{
var objectContext = (realContext as IObjectContextAdapter).ObjectContext;
// Reflect over the public properties that return DbSet<> and get it. If it is
// of type FakeDbSet<>, call InitializeWithContext() on it.
var fakeDbSetGenericType = typeof(FakeDbSet<>);
var dbSetGenericType = typeof(DbSet<>);
var properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in properties)
{
if (!prop.PropertyType.IsGenericType || prop.GetMethod == null)
continue;
if (prop.PropertyType.GetGenericTypeDefinition() != dbSetGenericType)
continue;
var dbSetObj = prop.GetMethod.Invoke(this, null);
var dbSetObjType = dbSetObj?.GetType();
if (dbSetObjType?.GetGenericTypeDefinition() != fakeDbSetGenericType)
continue;
var initMethod = dbSetObjType.GetMethod(nameof(FakeDbSet<object>.InitializeWithContext),
BindingFlags.NonPublic | BindingFlags.Instance,
null, new[] {typeof(ObjectContext)}, new ParameterModifier[] { });
initMethod.Invoke(dbSetObj, new object[] {objectContext});
}
}
InitializePartial2();
}
partial void InitializePartial2();
}
partial class FakeDbSet<TEntity>
{
private EntitySetBase EntitySet { get; set; }
/// <summary> Initialization of FakeDbSet to handle setting an identity for columns marked as
/// <c>IsStoreGeneratedIdentity</c> when an item is Added to the DbSet. If this signature
/// conflicts with another partial class, change that signature to implement
/// <see cref="InitializePartial2"/>, as that will be called when this is complete. </summary>
partial void InitializePartial()
{
// The only way we know something was added to the DbSet from this partial class
// is to hook the CollectionChanged event.
_data.CollectionChanged += DataOnCollectionChanged;
InitializePartial2();
}
internal void InitializeWithContext(ObjectContext objectContext)
{
// Get entity set for entity. Used when we figure out whether to generate IDENTITY values
EntitySet = objectContext
.MetadataWorkspace
.GetItems<EntityContainer>(DataSpace.SSpace).First()
.BaseEntitySets
.FirstOrDefault(item => item.Name == typeof(TEntity).Name);
}
private void DataOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action != NotifyCollectionChangedAction.Add)
return;
foreach (TEntity entity in e.NewItems)
GenerateIdentityColumnValues(entity);
}
/// <summary> The purpose of this method, which is called after a row is added, is to ensure that Identity column values are
/// properly initialized. If this was a real Entity Framework, this task would be handled by the data provider
/// when SaveChanges[Async]() is called and the value(s) would then be propagated back into the entity.
/// In the case of FakeDbSet, there is nothing that will do that, so we have to make this at-least token effort
/// to ensure the columns are properly initialized, even if it is done at the incorrect time.
/// </summary>
private void GenerateIdentityColumnValues(TEntity entity)
{
foreach (var member in EntitySet.ElementType.Members)
{
if (!member.IsStoreGeneratedIdentity)
continue;
foreach (var metadataProperty in member.TypeUsage.EdmType.MetadataProperties)
{
if (metadataProperty.Name != "PrimitiveTypeKind")
continue;
var entityProperty = entity.GetType().GetProperty(member.Name);
var identityColumnGetter = entityProperty.GetGetMethod();
// Note that we'll get the current value of the column and,
// if it is nonzero, we'll leave it alone. We do this because
// the test data in our mock DbContext provides values for the
// Identity columns and many of those values are foreign keys
// in other entities (where we also provide test data). We don't
// want to disturb any existing relationships defined in the test data.
bool isDefaultForType;
var columnType = (PrimitiveTypeKind)metadataProperty.Value;
switch (columnType)
{
case PrimitiveTypeKind.SByte:
isDefaultForType = default(SByte) == (SByte)identityColumnGetter.Invoke(entity, null);
break;
case PrimitiveTypeKind.Int16:
isDefaultForType = default(Int16) == (Int16)identityColumnGetter.Invoke(entity, null);
break;
case PrimitiveTypeKind.Int32:
isDefaultForType = default(Int32) == (Int32)identityColumnGetter.Invoke(entity, null);
break;
case PrimitiveTypeKind.Int64:
isDefaultForType = default(Int64) == (Int64)identityColumnGetter.Invoke(entity, null);
break;
case PrimitiveTypeKind.Decimal:
isDefaultForType = default(Decimal) == (Decimal)identityColumnGetter.Invoke(entity, null);
break;
default:
// In SQL Server, an Identity column can be of one of the following data types:
// tinyint (SqlByte, byte), smallint (SqlInt16, Int16), int (SqlInt32, Int32),
// bigint (SqlInt64, Int64), decimal (with a scale of 0) (SqlDecimal, Decimal),
// or numeric (with a scale of 0) (SqlDecimal, Decimal). Those are handled above.
// 'If we don't know, we throw'
throw new InvalidOperationException($"Unsupported Identity Column Type {columnType}");
}
// From this point on, we can return from the method, as only one identity column is
// possible per table and we found it.
if (!isDefaultForType)
return;
var identityColumnSetter = entityProperty.GetSetMethod();
lock (Local)
{
switch (columnType)
{
case PrimitiveTypeKind.SByte:
{
SByte maxExistingColumnValue = 0;
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (SByte) identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] {(SByte) (++maxExistingColumnValue)});
return;
}
case PrimitiveTypeKind.Int16:
{
Int16 maxExistingColumnValue = 0;
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (Int16) identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] {(Int16) (++maxExistingColumnValue)});
return;
}
case PrimitiveTypeKind.Int32:
{
Int32 maxExistingColumnValue = 0;
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (Int32) identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] {(Int32) (++maxExistingColumnValue)});
return;
}
case PrimitiveTypeKind.Int64:
{
Int64 maxExistingColumnValue = 0;
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (Int64) identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] {(Int64) (++maxExistingColumnValue)});
return;
}
case PrimitiveTypeKind.Decimal:
{
Decimal maxExistingColumnValue = 0;
foreach (var item in Local.ToList())
maxExistingColumnValue = Math.Max(maxExistingColumnValue, (Decimal) identityColumnGetter.Invoke(item, null));
identityColumnSetter.Invoke(entity, new object[] {(Decimal) (++maxExistingColumnValue)});
return;
}
}
}
}
}
}
partial void InitializePartial2();
}

Reactive Extensions Group By, Unique BufferWindow till time span with cancellation

I have a Hot stream of events coming of following type:
Event
{
string name;
int state ; // its 1 or 2 ie active or unactive
}
there is a function which provides parent name of given name - string GetParent(string name)
I need to buffer event per parent for 2 minutes, if during this 2 minute , i recv any event for child with state =2 for a given parent , this buffer should cancel and should output 0 otherwise i get the count of the events recvd .
I know I have to use GroupBy to partition, and then buffer and then count but i am unable to think of a way by which i create Buffer which is unique per parent, i though of using Distinct but this doesnt solve the problem, for i only dont want to create buffer till the parent is active (as once the parent's buffer gets cancelled or 2 minutes is over, the parent buffer can be created again)
So I understand I need to create a custom buffer which checks the condition for creating buffer, but how do i do this via reactive extensions.
Any help will be greatly appreciated.
regards
Thanks Brandon for your help. This is the main program I am using for testing. Its not working.As I am new to reactive extension problem can be in the way i am testing
namespace TestReactive
{
class Program
{
static int abc = 1;
static void Main(string[] args)
{
Subject<AEvent> memberAdded = new Subject<AEvent>();
//ISubject<AEvent, AEvent> syncedSubject = new ISubject<AEvent, AEvent>();
var timer = new Timer { Interval = 5 };
timer.Enabled = true;
timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, memberAdded);
var bc = memberAdded.Subscribe();
var cdc = memberAdded.GroupBy(e => e.parent)
.SelectMany(parentGroup =>
{
var children = parentGroup.Publish().RefCount();
var inactiveChild = children.SkipWhile(c => c.state != 2).Take(1).Select(c => 0);
var timer1 = Observable.Timer(TimeSpan.FromSeconds(1));
var activeCount = children.TakeUntil(timer1).Count();
return Observable.Amb(activeCount, inactiveChild)
.Select(count => new { ParentName = parentGroup.Key, Count = count });
});
Observable.ForEachAsync(cdc, x => WriteMe("Dum Dum " + x.ParentName+x.Count));
// group.Dump("Dum");
Console.ReadKey();
}
static void WriteMe(string sb)
{
Console.WriteLine(sb);
}
static void MyElapsedMethod(object sender, ElapsedEventArgs e, Subject<AEvent> s)
{
AEvent ab = HelperMethods.GetAlarm();
Console.WriteLine(abc + " p =" + ab.parent + ", c = " + ab.name + " ,s = " + ab.state);
s.OnNext(ab);
}
}
}
public static AEvent GetAlarm()
{
if (gp> 4)
gp = 1;
if (p > 4)
p = 1;
if (c > 4)
c = 1;
AEvent a = new AEvent();
a.parent = "P" + gp + p;
a.name = "C" + gp + p + c;
if (containedKeys.ContainsKey(a.name))
{
a.state = containedKeys[a.name];
if (a.state == 1)
containedKeys[a.name] = 2;
else
containedKeys[a.name] = 1;
}
else
{
containedKeys.TryAdd(a.name, 1);
}
gp++; p++; c++;
return a;
}
So this method , generates a event for Parent at each tick. It generates event for parent P11,P22,P33,P44 with State =1 and then followed by events for Parent P11,P22,P33,P44 with State =2
I am using Observable.ForEach to print the result, I see its being called 4 times and after that its nothing, its like cancellation of group is not happening
Assuming that a two minute buffer for each group should open as soon as the first event for that group is seen, and close after two minutes or a zero state is seen, then I think the following works:
public static IObservable<EventCount> EventCountByParent(
this IObservable<Event> source, IScheduler scheduler)
{
return Observable.Create<EventCount>(observer => source.GroupByUntil(
evt => GetParent(evt.Name),
evt => evt,
group =>
#group.Where(evt => evt.State == 2)
.Merge(Observable.Timer(
TimeSpan.FromMinutes(2), scheduler).Select(_ => Event.Null)))
.SelectMany(
go =>
go.Aggregate(0, (acc, evt) => (evt.State == 2 ? 0 : acc + 1))
.Select(count => new EventCount(go.Key, count))).Subscribe(observer));
}
With EventCount (implementing equality overrides for testing) as:
public class EventCount
{
private readonly string _name;
private readonly int _count;
public EventCount(string name, int count)
{
_name = name;
_count = count;
}
public string Name { get { return _name; } }
public int Count { get { return _count; } }
public override string ToString()
{
return string.Format("Name: {0}, Count: {1}", _name, _count);
}
protected bool Equals(EventCount other)
{
return string.Equals(_name, other._name) && _count == other._count;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((EventCount) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_name != null ? _name.GetHashCode() : 0)*397) ^ _count;
}
}
}
And Event as:
public class Event
{
public static Event Null = new Event(string.Empty, 0);
private readonly string _name;
private readonly int _state;
public Event(string name, int state)
{
_name = name;
_state = state;
}
public string Name { get { return _name; } }
public int State { get { return _state; } }
}
I did a quick (i.e. not exhaustive) test with Rx-Testing:
public class EventCountByParentTests : ReactiveTest
{
private readonly TestScheduler _testScheduler;
public EventCountByParentTests()
{
_testScheduler = new TestScheduler();
}
[Fact]
public void IsCorrect()
{
var source = _testScheduler.CreateHotObservable(
OnNext(TimeSpan.FromSeconds(10).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(20).Ticks, new Event("B", 1)),
OnNext(TimeSpan.FromSeconds(30).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(40).Ticks, new Event("B", 1)),
OnNext(TimeSpan.FromSeconds(50).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(60).Ticks, new Event("B", 2)),
OnNext(TimeSpan.FromSeconds(70).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(140).Ticks, new Event("A", 1)),
OnNext(TimeSpan.FromSeconds(150).Ticks, new Event("A", 1)));
var results = _testScheduler.CreateObserver<EventCount>();
var sut = source.EventCountByParent(_testScheduler).Subscribe(results);
_testScheduler.Start();
results.Messages.AssertEqual(
OnNext(TimeSpan.FromSeconds(60).Ticks, new EventCount("B", 0)),
OnNext(TimeSpan.FromSeconds(130).Ticks, new EventCount("A", 4)),
OnNext(TimeSpan.FromSeconds(260).Ticks, new EventCount("A", 2)));
}
}
something like....
source.GroupBy(e => GetParent(e.name))
.SelectMany(parentGroup =>
{
var children = parentGroup.Publish().RefCount();
var inactiveChild = children.SkipWhile(c => c.state != 2).Take(1).Select(c => 0);
var timer = Observable.Timer(TimeSpan.FromMinutes(2));
var activeCount = children.TakeUntil(timer).Count();
return Observable.Amb(activeCount, inactiveChild)
.Select(count => new { ParentName = parentGroup.Key, Count = count };
});
This will give you a sequence of { ParentName, Count } objects.

From AutoMapper to Emit Mapper

I've recently discovered AutoMapper for bridging ViewModels and my actual DB objects. I use it in the way decribed here: http://automapper.codeplex.com/wikipage?title=Projection&referringTitle=Home
I've discovered Emit Mapper to :), but I can't find anytning similar to (where I can specify custom projecting rules):
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
Thanks in advance!
For the Record this is the best solution that I came across on how to do it:
http://emitmapper.codeplex.com/discussions/259655
Check the solution on the last post. It works really well.
Update: The code for future reference:
public class ExtDefaultMapConfig<TSrc, TDst> : DefaultMapConfig
{
private readonly Dictionary<string, Func<TSrc, object>> _properties = new Dictionary<string, Func<TSrc, object>>();
public ExtDefaultMapConfig<TSrc, TDst> ForMember(string property, Func<TSrc, object> func)
{
if (!_properties.ContainsKey(property))
_properties.Add(property, func);
return this;
}
public ExtDefaultMapConfig<TSrc, TDst> ForMember(Expression<Func<TDst, object>> dstMember, Func<TSrc, object> func)
{
var prop = ReflectionHelper.FindProperty(dstMember);
return ForMember(prop.Name, func);
}
public ExtDefaultMapConfig<TSrc, TDst> Ignore(Expression<Func<TDst, object>> dstMember)
{
var prop = ReflectionHelper.FindProperty(dstMember);
IgnoreMembers<TSrc, TDst>(new[] { prop.Name });
return this;
}
public override IMappingOperation[] GetMappingOperations(Type from, Type to)
{
var list = new List<IMappingOperation>();
list.AddRange(base.GetMappingOperations(from, to));
list.AddRange(
FilterOperations(
from,
to,
ReflectionUtils.GetPublicFieldsAndProperties(to)
.Where(f => _properties.ContainsKey(f.Name))
.Select(
m =>
(IMappingOperation)new DestWriteOperation
{
Destination = new MemberDescriptor(m),
Getter =
(ValueGetter<object>)
(
(value, state) =>
{
Debug.WriteLine(string.Format("Mapper: getting value of field or property {0}", m.Name));
return ValueToWrite<object>.ReturnValue(_properties[m.Name]((TSrc) value));
}
)
}
)
)
);
return list.ToArray();
}
}
class ReflectionHelper
{
public static MemberInfo FindProperty(LambdaExpression lambdaExpression)
{
Expression expression = lambdaExpression;
bool flag = false;
while (!flag)
{
switch (expression.NodeType)
{
case ExpressionType.Convert:
expression = ((UnaryExpression)expression).Operand;
break;
case ExpressionType.Lambda:
expression = ((LambdaExpression)expression).Body;
break;
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)expression;
if (memberExpression.Expression.NodeType != ExpressionType.Parameter && memberExpression.Expression.NodeType != ExpressionType.Convert)
throw new ArgumentException(string.Format("Expression '{0}' must resolve to top-level member.", lambdaExpression), "lambdaExpression");
return memberExpression.Member;
default:
flag = true;
break;
}
}
return null;
}
public static object GetValue(string property, object obj)
{
PropertyInfo pi = obj.GetType().GetProperty(property);
return pi.GetValue(obj, null);
}
}