How can you generically map a DbDataReader to a Castle.Windsor resolved type? - inversion-of-control

This is confusing me, so this question will probably be confusing.
I have a an application that uses implementations of an IJob interface to accomplish different tasks.
public interface IJob
{
int Id { get; set; }
string Name { get; set; }
void Run();
}
I am using the Castle.Windsor.WindsorContainer to resolve these implementations, and using the service id to help identify them.
WindsorContainer container = new WindsorContainer(new XmlInterpreter());
IJob jobToExecute = container.Resolve<IJob>("nameOfJob");
I wrote a little generic extension method that simply puts the values of SQL columns into their corresponding properties.
public static void MapTo<T>(this DbDataReader reader, ref T instance) where T : class
{
Type objectType = typeof(T);
foreach (PropertyInfo propertyInfo in objectType.GetProperties())
{
if (propertyInfo.CanWrite)
{
int ordinal = -1;
try
{
ordinal = reader.GetOrdinal(propertyInfo.Name);
object value = reader[ordinal] == DBNull.Value ? null : reader[ordinal];
propertyInfo.SetValue(instance, value, null);
}
catch (IndexOutOfRangeException ex)
{
continue;
}
}
}
}
Now, because you can't instantiate an instance of an interface, passing an IJob to this method won't work. However, in order to gain the benefits of the IoC container, I need to do everything in my repository using the IJob interface. So, I wrote with this to resolve the IJob implementation, and pass it to the MapTo method to populate the necessary properties:
public IJob GetJobById(int id)
{
string cmdTxt = "SELECT Id, Name, Description, DateStarted, ScheduledCompletion, Completed FROM Jobs WHERE Id = #id";
using (DbCommand cmd = _dataFactory.CreateCommand(cmdTxt))
{
_dataFactory.AddParam(cmd, "id", id, DbType.Int32);
using (DbDataReader rdr = cmd.ExecuteReader())
{
if (rdr.Read())
{
IJob job = _container.Resolve<IJob>("job.implementation");
rdr.MapTo<IJob>(ref job);
return job;
}
else
{
return null;
}
}
}
}
Is this an OK design decision? Do you see any problems?

Well, for one, calling methods via reflection is usually not nice... and it looks like you're using Windsor as a type dictionary, which it is not...
I would write a non-generic MapTo (which would take a Type as parameter) that operates on an already-existing instance (when you create a new instance with Activator.CreateInstance you discard the instance Windsor had resolved) and then use it from the ComponentCreatedEvent event in IKernel. Something like this:
container.Kernel.ComponentCreated += (model, instance) => {
if (model.Service == typeof(IJob)) {
// select id,name from jobs where id = model.Name
// use MapTo to fill id,name into instance
}
}

Related

How can I pass a LambdaExpression to an IncludeFilter?

I'm trying to pass a dynamically generated LambdaExpression to an IncludeFilter, as follows:
EDIT: I've changed my test code to the following, as (correctly) I wasn't implementing my "Where" statement. The correct where statement is being generated, but I can't pass the lambda statement into the IncludeFilter call:
DbSet<MyTestEntity> dbSet = db.Set<MyTestEntity>();
ParameterExpression parameter = Expression.Parameter(typeof(MyTestEntity), "t");
Expression idProperty = Expression.Property(parameter, "mytestentityid");
Expression delProperty = Expression.Property(parameter, "deleted");
Expression delTarget = Expression.Constant(false, typeof(bool));
Expression deletedMethod = Expression.Call(delProperty, "Equals", null, delTarget);
Expression<Func<MyTestEntity, bool>> lambda = Expression.Lambda<Func<MyTestEntity, bool>>(deletedMethod, parameter);
IQueryable<MyTestEntity> query = dbSet.Where(lambda);
Console.WriteLine("Current Query: {0}", query.ToString());
foreach (string include in includes)
{
Type subType = db.GetType().Assembly.GetTypes().SingleOrDefault(x => x.Name.EndsWith(include));
Assert.IsNotNull(subType);
ParameterExpression innerParam = Expression.Parameter(subType, subType.Name);
Assert.IsNotNull(innerParam);
MemberExpression inrDelProp = Expression.Property(innerParam, "deleted");
Assert.IsNotNull(inrDelProp);
ConstantExpression inrDelCstProp = Expression.Constant(false, typeof(bool));
Assert.IsNotNull(inrDelCstProp);
MethodCallExpression inrDelMthd = Expression.Call(inrDelProp, "Equals", null, inrDelCstProp);
Assert.IsNotNull(inrDelMthd);
var delegateType = typeof(Func<,>).MakeGenericType(subType, typeof(bool));
dynamic inrLmbdaExpr = Expression.Lambda(delegateType, inrDelMthd, innerParam);
Assert.IsNotNull(inrLmbdaExpr);
Console.WriteLine("inrLmbdaExpr: {0}", inrLmbdaExpr.ToString()); // Result: MyTestEntityChild => MyTestEntityChild.deleted.Equals(false)
query = query.IncludeFilter(inrLmbdaExpr); // ERROR HERE
Assert.IsNotNull(query);
Console.WriteLine("-------------------------------------------------");
Console.WriteLine("Current Query: {0}", query.ToString());
}
This is built into an abstract class allowing me to pass in an entity type, retrieve the records, and reuse the method irrespective of the entity type; however, I'm also trying to filter out child entities that are marked as deleted (thus the use of EF+).
How can I do this?
EDIT 2: So, I realized I also have Linq.Dynamic.Core (!) in my solution, so I already have access to parsing a LambdaExpression from string. However, the error I get says that IncludeFilter doesn't know which method it's trying to use. (I see in the Object Browser that one uses Expression> and one uses Expression>>. If I could just figure out how to get the IncludeFilter to recognize which method, I think I'd be done! Here's a sample of the code I've rewritten:
string myIncStr = String.Format("x => x.{0}.Where(s => s.deleted.Equals(false)).Where(x => x.MyEntityId.Equals(IncomingId)",includedEntityName);
IEnumerable<MyEntity> result = db.MyEntity.IncludeFilter(System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(typeof(MyChildEntity), myIncStr, null));
Is there a way to "force" (for lack of a better term) the IncludeFilter to use one method? Is it by passing a value instead of null in the Parser?
BTW, thanks for your help. Your EFP library is actually excellent.
Disclaimer: I'm the owner of the project Entity Framework Plus
Yes, it's possible but only if you can specify the generic argument type required by the method explicitly for the QueryFilter (As you mentioned in your comment).
Otherwise, you will need to also call the QueryFilter via the expression to make everything generic.
However, your current expression seems to have some error such as not calling the Where methods.
What you want to achieve is probably something similar to this:
query = query.IncludeFilter(x => x.Childs.Where(y => !y.Deleted));
Disclaimer: I'm the owner of the project Eval-Expression.NET
This library is not free but makes working with a dynamic expression easier and faster.
Once you get used, you can quickly create a dynamic expression in only a few lines as you normally write LINQ. Here is a code that could handle a similar scenario as your:
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Windows.Forms;
using Z.Expressions;
namespace Z.EntityFramework.Plus.Lab.EF6
{
public partial class Form_Request_IncludeFilter_Dynamic : Form
{
public Form_Request_IncludeFilter_Dynamic()
{
InitializeComponent();
// CLEAN
using (var context = new EntityContext())
{
context.MyEntityClasses.RemoveRange(context.MyEntityClasses);
context.MyEntityClassToFilters.RemoveRange(context.MyEntityClassToFilters);
context.SaveChanges();
}
// SEED
using (var context = new EntityContext())
{
var entity1 = context.MyEntityClasses.Add(new MyEntityClass {ColumnInt = 1, Childs = new List<MyEntityClassToFilter>()});
entity1.Childs.Add(new MyEntityClassToFilter {ColumnInt = 1, Deleted = true});
entity1.Childs.Add(new MyEntityClassToFilter {ColumnInt = 2, Deleted = false});
context.MyEntityClasses.Add(new MyEntityClass {ColumnInt = 2});
context.MyEntityClasses.Add(new MyEntityClass {ColumnInt = 3});
context.SaveChanges();
}
// TEST
using (var context = new EntityContext())
{
// You must register extension method only once
// That should not be done here, but for example purpose
EvalManager.DefaultContext.RegisterExtensionMethod(typeof(QueryIncludeFilterExtensions));
// That could be also dynamic. I believe you already handle this part
IQueryable<MyEntityClass> query = context.MyEntityClasses;
// The path to include
var include = "Childs";
// The dynamic expression to execute
var dynamicExpression = "IncludeFilter(x => x." + include + ".Where(y => !y.Deleted));";
query = query.Execute<IQueryable<MyEntityClass>>(dynamicExpression);
// The result
var list = query.ToList();
}
}
public class EntityContext : DbContext
{
public EntityContext() : base("CodeFirstEntities")
{
}
public DbSet<MyEntityClass> MyEntityClasses { get; set; }
public DbSet<MyEntityClassToFilter> MyEntityClassToFilters { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Types().Configure(x =>
x.ToTable(GetType().DeclaringType != null
? GetType().DeclaringType.FullName.Replace(".", "_") + "_" + x.ClrType.Name
: ""));
base.OnModelCreating(modelBuilder);
}
}
public class MyEntityClass
{
public int ID { get; set; }
public int ColumnInt { get; set; }
public List<MyEntityClassToFilter> Childs { get; set; }
}
public class MyEntityClassToFilter
{
public int ID { get; set; }
public int ColumnInt { get; set; }
public bool Deleted { get; set; }
}
}
}
EDIT: Answer question
Please review my changed code
You are still missing the where clause.
What you have is something similar to this as you commented
// Result: MyTestEntityChild => MyTestEntityChild.deleted.Equals(false)
What you want is something similar to this
// Result: MyTestEntityChild => MyTestEntityChild.Where(x => x.deleted.Equals(false))
EDIT: Answer question
Oh sorry, I now understand the problem with it.
If you don't know the type, you will need to call the IncludeFilter in an expression as well to make everything generic. It cannot be called explicitely like you are trying to do.

Entity Framework Core: Dynamically build select list with navigational properties

The question is similar to this one, but answer does not provide 2 critical things to me:
I need the code to work over navigational properties
I am trying to build extension method
I want to write queries like this:
this.context.User
.Where(t => t.Id > 10)
.SelectCustom(t => t.Address.Country.Title)
.OrderBy(t => t.DisplayName)
.Skip(10).Take(5);
With answer in provided link I get this far:
public class SelectList<TSource>
{
private List<MemberInfo> members = new List<MemberInfo>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
var member = ((MemberExpression)selector.Body).Member;
members.Add(member);
return this;
}
public Expression<Func<TSource, TResult>> ToDynamicColumns()
{
return this.members.??????????;
}
}
public static IQueryable<T> SelectCustom<T>(this IQueryable<T> query, Expression<Func<TSource, TKey>> FirstAdditional = null)
{
var columns = new SelectList<T>();
columns.Add(t => t.Id);
columns.Add(t => t.DisplayName)
if (FirstAdditional != null)
columns.Add(FirstAdditional);
return query.Select(columns.ToDynamicColumns);
}
Can this be done with EF Core 2.0?
EF will look through lambda invoke operations as if the body of that expression was inlined. So I would recommend leaving the source expressions alone and just generate expressions to invoke them.
Also I would keep the result type simple, and just return each row as an array of object. This should result in less overhead than creating lots of dictionaries. If you do need to access fields by name, you should create a single dictionary to maintain the relationship between names and column numbers.
public class SelectList<TSource>
{
private List<LambdaExpression> members = new List<LambdaExpression>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
members.Add(selector);
return this;
}
public Expression<Func<TSource, TResult>> ToDynamicColumns()
{
var parameter = Expression.Parameter(typeof(TSource), "e");
return Expression.Lambda<Func<TSource, object[]>>(
Expression.NewArrayInit(
typeof(object),
members.Select(m =>
Expression.Convert(Expression.Invoke(m, parameter), typeof(object))
)
),
parameter);
}
}
Though in your case, since you are writing an extension method to only return the same key details and a single additional field, you could probably define a single generic type to hold the results, and avoid any mucking around with Linq expressions at all;
public class UserResult<V>{
public int Id { get; set; }
public string DisplayName { get; set; }
public V Value { get; set; }
}
public static IQueryable<UserResult<V>> SelectCustom<V>(this IQueryable<User> query, Expression<Func<User, V>> ValueGetter)
{
return query.Select(u => new UserResult<V>{
Id = u.Id,
DisplayName = u.DisplayName,
Value = ValueGetter(u)
});
}
Well almost, if c# would just allow you to compile a call of one Expression<Delegate> from within another. Instead we can implement an ExpressionVisitor to unwrap any call to Compile;
public class DontCompile : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
// Inline any lambda arguments that are expressions
if (node.Expression is ConstantExpression lambdaArgs
&& node.Member is FieldInfo field
&& typeof(Expression).IsAssignableFrom(field.FieldType))
return (Expression)field.GetValue(lambdaArgs.Value);
return base.VisitMember(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// Don't compile lambda expressions
if (node.Method.Name == "Compile"
&& typeof(LambdaExpression).IsAssignableFrom(node.Object.Type))
return Visit(node.Object);
return base.VisitMethodCall(node);
}
public static Expression<T> Tidy<T>(Expression<T> func) => (Expression<T>)new DontCompile().Visit(func);
}
...
return query.Select(DontCompile.Tidy<...>(u => new UserResult<V>{
Id = u.Id,
DisplayName = u.DisplayName,
Value = ValueGetter.Compile()(u)
});
...
But that's starting to look a bit messy.
You could do this with Expression.ListInit, here the TResult must have an Add instance method e.g. Dictionary<string, object>. This could just work but I havent even compiled it. In any way this should give enough hint on how to build this the way you want.
public Expression<Func<TSource, Dictionary<string, object>>> ToDynamicColumns()
{
var addMethod = typeof(TResult).GetMethod("Add");
var paramX = Expression.Parameter(typeof(TSource), "e");
var bindings =
this.members.Select (
member =>
return Expression.ElementInit(
addMethod,
Expression.Constant(mem.Name),
Expression.Convert(Expression.Property(paramX, member), typeof(object))
);
)
var listInit = Expression.ListInit(
Expression.New(typeof(TResult)),
bindings
);
return Expression.Lambda<Func<TSource, Dictionary<string, object>>(
listInit,
paramX
);
}

Entity Framework: Explicity set Primary key Value

Is it possible to set a primary key value of your own choice ?
I'm working with data from an API and i'd like the objects to have the same id in my database as they have originally.
For example, i have an object with these attributes:
_context = new ApplicationDbContext();
Object
{
id = 1234,
Name = "Pitbull",
Owner = "Greg"
};
_context.saveChanges(Object);
id is the PK for object in the database. But, if i save this the id is discarded and the database creates it's own value.
Thanks for reading ! :)
Yes there is.
The proper way to do this is to override your three DbContext.SaveChanges methods. In these methods you'll give the primary keys of all added objects an Id.
In the example below this is done in method GenerateIds:
public override int SaveChanges()
{
GenerateIds();
return base.SaveChanges();
}
public override Task<int> SaveChangesAsync()
{
GenerateIds();
return await base.SaveChangesAsync();
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
GenerateIds();
return await base.SaveChangesAsync(cancellationToken);
}
private void GenerateIds()
{
var addedEntries = this.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added)
foreach (var addedEntry in addedEntries)
{
((IId)addedEntry.Entity).Id = this.CreateId();
}
}
CreateId should create a unique Id.
One of the Id generators I use often is nuget package IDgen (by RobIII). It is simple to install and to use. It generates unique System.Int64 identifiers, which have the advantage of being much smaller than a GUID. It works even if you have generators for the same database on multiple servers. The method it uses is the method twitter uses to generate ids for all its servers
Code is a one liner. In your DbContext:
private static IdGen.IdGenerator idGenerator = new IdGen.IdGenerator(0);
private long CreatedId()
{
return idGenerator.CreateId();
}

generalize dbcontext AddOrUpdate

My code :
public class BaseController
{
public object AddUpdate(object obj)
{
using (var db = new StoreModel())
{
string nameObj = obj.ToString().Substring(obj.ToString().LastIndexOf(".") + 1);
var property = db.GetType().GetProperty(nameObj);
((DbSet<CrmTicket>)property.GetValue(db)).AddOrUpdate((CrmTicket)obj);
db.SaveChanges();
return obj;
}
}
}
I would like generalize AddOrUpdate.
This code work but it's not generic, you can see CrmTicket.
I can not put a Type in his place.
((DbSet<obj.GetType()>)property.GetValue(db)).AddOrUpdate((obj.GetType())obj);
Could you help me ?
Thank you.
You could simply use generics. There are multiple ways of doing this pretty easily. Here's one way:
public class BaseController
{
protected T AddOrUpdate<T>(T obj) where T : BaseEntity
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
using (StoreModel context = new StoreModel())
{
T entity = context.Set<T>().Find(obj.Id);
// the entity doesn't exists yet, so we add it
if (entity == null)
{
context.Set<T>().Add(entity);
}
// the entity exists, so we must update it
else
{
// do you update logic, like : entity.MyString = obj.MyString
// ...
// Note : there is no need to attach the entity because the Find method has already done it.
}
// Everything is done.
context.SaveChanges();
}
return obj;
}
}
// This is your base class for all entity.
// If you want to use generics and have an AddOrUpdate method,
// you must have something to rely on where you want to check if the object you want to insert is already in Db.
public class BaseEntity
{
public int Id { get; set; } // you should configure this as an Primary Key with Identity
}
But I think this is not a good idea... With that kind of considerations, you should look at repositories: http://www.tugberkugurlu.com/archive/generic-repository-pattern-entity-framework-asp-net-mvc-and-unit-testing-triangle

Problem with generic list and extension method(C#3.0)

I have an issue. I am making an extension class for a Collection and it is generic.. like
public static class ListExtensions
{
public static ICollection<T> Search<T>(this ICollection<T> collection, string stringToSearch)
{
ICollection<T> t1=null;
foreach (T t in collection)
{
Type k = t.GetType();
PropertyInfo pi = k.GetProperty("Name");
if (pi.GetValue(t,null).Equals(stringToSearch))
{
t1.Add(t);
}
}
return t1;
}
}
But I cannot add items to t1 as it is declared null.
Error: object reference not set to an instance of the object.
I am calling the method like
List<TestClass> listTC = new List<TestClass>();
listTC.Add(new TestClass { Name = "Ishu", Age = 21 });
listTC.Add(new TestClass { Name = "Vivek", Age = 40 });
listTC.Add(new TestClass { Name = "some one else", Age = 12 });
listTC.Search("Ishu");
And the test class is
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
}
Using : (C#3.0) & Framework - 3.5
Thanks
As you probably don't want to manipulate (add, remove,...) the Search results after you performed a Search it is better practice to return IEnumerable<T> instead of ICollection<T>. Also C# has a special syntax for this: yield
public static class ListExtensions
{
public static IEnumerable<T> Search<T>(this ICollection<T> collection, string stringToSearch)
{
foreach (T t in collection)
{
Type k = t.GetType();
PropertyInfo pi = k.GetProperty("Name");
if (pi.GetValue(t,null).Equals(stringToSearch))
{
yield return t;
}
}
}
}
Well what kind of collection do you want to use? You've got to have an actual collection to add your results to. List<T> is probably the simplest suggestion. Just change the first line of the method:
ICollection<T> t1 = new List<T>();
EDIT: Although this is the simplest change to the code, you should definitely consider using an iterator block as per Thomas's answer.