I have some linq entities that inherit something like this:
public abstract class EntityBase { public int Identifier { get; } }
public interface IDeviceEntity { int DeviceId { get; set; } }
public abstract class DeviceEntityBase : EntityBase, IDeviceEntity
{
public abstract int DeviceId { get; set; }
}
public partial class ActualLinqGeneratedEntity : DeviceEntityBase
{
}
In a generic method I am querying DeviceEnityBase derived entities with:
return unitOfWork.GetRepository<TEntity>().FindOne(x => x.DeviceId == evt.DeviceId);
where TEntity has a contraint that is it a DeviceEntityBase. This query is always failing with an InvalidOperationException with the message "Class member DeviceEntityBase.DeviceId is unmapped". Even if I add some mapping info in the abstract base class with
[Column(Storage = "_DeviceId", DbType = "Int", Name = "DeviceId", IsDbGenerated = false, UpdateCheck = UpdateCheck.Never)]
Wow, looks like for once I may be able to one-up #MarcGravell!
I had the same problem, then I discovered this answer, which solved the problem for me!
In your case, you would say:
return unitOfWork.GetRepository<TEntity>().Select(x => x).FindOne(x => x.DeviceId == evt.DeviceId);
and Bob's your uncle!
LINQ-to-SQL has some support for inheritance via a discriminator (here, here), but you can only query on classes that are defined in the LINQ model - i.e. data classes themselves, and (more perhaps importantly for this example) the query itself must be phrased in terms of data classes: although TEntity is a data class, it knows that the property here is declared on the entity base.
One option might be dynamic expressions; it the classes themselves declared the property (i.e. lose the base class, but keep the interface) - but this isn't trivial.
The Expression work would be something like below, noting that you might want to either pass in the string as an argument, or obtain the primary key via reflection (if it is attributed):
static Expression<Func<T, bool>> BuildWhere<T>(int deviceId) {
var id = Expression.Constant(deviceId, typeof(int));
var arg = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(arg, "DeviceId");
return Expression.Lambda<Func<T, bool>>(
Expression.Equal(prop, id), arg);
}
This kind of heirarchial mapping isnot possible with LinqToSql. The the mapping is setup it cannot map to properties in base classes. I went around on this for a couple of months when it first came out. The best solution is to use the entity framework. It gives you much more flexibility with creating your object model. It will allow you to do exactly what your trying to do here.
Here is some information on the entity framework: MSDN Article
Try .OfType<>() as posted here https://stackoverflow.com/a/17734469/3936440, it works for me having the exact same issue.
Related
If you're unfamiliar with the Entity Framework, it generates a class that looks like
public partial class contextontext : DbContext
{
public virtual DbSet<foo> foo { get; set; }
public virtual DbSet<bar> bar { get; set; }
//Etc, there could be lots of these DbSet properties
}
I'm trying to build a list of types that there are DbSet<T> collections for, but I'm not sure how to check for a generic type. I got it to work using PropertyType.ToString().StartsWith("System.Data.Entity.DbSet`1") but that seems like a messy and unnecessarily complicated way to do it. This is the full LINQ I use to get the properties.
foreach (var property in context.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).Where(p => p.PropertyType.ToString().StartsWith("System.Data.Entity.DbSet`1")))
{
Type type = property.PropertyType.GenericTypeArguments[0];
//other stuff...
}
I poked around in the property object, but didn't find any leads. There's a Type in there, but that's always the type specific implementation of the generic DbSet (as it should be). How would I check to see if it's the generic collection, regardless of what type it is?
I think you want simply:
var propertyType = property.PropertyType;
if (propertyType.IsGenericType
&& propertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
{
// ...
}
I see now your Where far to the right in your code. That would be:
.Where(p => p.PropertyType.IsGenericType
&& p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
The method GetGenericTypeDefinition goes from the concrete constructed ("closed") generic type (e.g. DbSet<foo>), to the definition of the type (here DbSet<TEntity>).
I've looked at the other questions around this and I just can't work out how to apply the answers to my particular situation. Say you have a couple of models that look like this:
public class Person
{
public int PersonId { get; set; }
}
public class Business
{
public int BusinessId { get; set; }
}
I want to be able to write a couple of different generic methods: one that gets the models using a provided Lambda that might look something like this:
GetWhere(p => p.PersonId == 1)
And one to get the models using a unique key - to make this flexible, I'd like to be able to specify the unique key using a Lambda:
GetByUniqueKey(p => p.PersonId, 1)
Or
GetByUniqueKey(b => b.BusinessId, 1)
Ideally GetByUniqueKey would just be a shorthand method to build up an expression to send to GetWhere, and then return the FirstOrDefault() result. But the logic to do this is completely escaping me. What I want to do:
public IEnumerable<TModel> GetWhere(Expression<Func<TModel, bool>> whereExpression)
{
// Get from DB using expression provided
}
public TModel GetByUniqueKey<TUniqueKey>(
Expression<Func<TModel, TUniqueKey>> uniqueKeyProperty,
TUniqueKey value)
{
return GetWhere(m => uniqueKeyProperty(m) == value).FirstOrDefault();
}
So I want to take the uniqueKeyProperty expression, invoke it on the supplied parameter somehow to get the property, and then use that property in the whereExpression expression.
A note on duplicate questions:
I know this looks like a duplicate of other similar questions, but please note I have read those and I just can't figure out how to apply those answers to my specific use case.
Some clarification in response to comments:
Put simply, I want to do the following:
I want to take the Expression p => p.PersonId and the value 1, and generate a whereExpression that looks like this p => p.PersonId == 1. (Thanks #Rob)
You can build a new expression from the key selector and value provided like so:
public TModel GetByUniqueKey<TUniqueKey>(
Expression<Func<TModel, TUniqueKey>> uniqueKeySelector,
TUniqueKey value)
{
return GetWhere(Expression.Lambda<Func<TModel,bool>>(
Expression.MakeBinary(
ExpressionType.Equal,
uniqueKeySelector.Body,
Expression.Constant(value, typeof(TUniqueKey))),
uniqueKeySelector.Parameters));
}
For querying by ID I wouldn't bother with this approach. Check out the other static methods on the Expression class.
As apparently you would like to implement some kind of polymorphism, one possibility would be to have Person and Business inherit from the same base class or inherit from the same interface and share an Id property. You could define
public class Identifiable
{
public int Id { get; set; }
}
and make Person and Business inherit from in. Then the callback
Func<Identifiable,bool> = iIdentifiable => iIdentifiable.Id == 1
could be called for objects of both classes. However, the original classes would have to be changed for that approach to work.
Let's say we have a class
class ComplexCls
{
public int Fld1;
public string Fld2;
//could be more fields
}
class Cls
{
public int SomeField;
}
and then some code
class ComplexClsList: List<ComplexCls>;
ComplexClsList myComplexList;
// fill myComplexList
// same for Cls
class ClsList : List<Cls>;
ClsList myClsList;
We want to populate myClsList from myComplexList, something like (pseudocode):
foreach Complexitem in myComplexList
{
Cls ClsItem = new Cls();
ClsItem.SomeField = ComplexItem.Fld1;
}
The code to do this is easy and will be put in some method in myClsList.
However I'd like to design this as generic as possible, for generic ComplexCls.
Note that the exact ComplexCls is known at the moment of using this code, only the algorithm shd be generic.
I know it can be done using (direct) reflection but is there other solution?
Let me know if the question is not clear enough. (probably isn't).
[EDIT] Basically, what I need is this: having myClsList, I need to specify a DataSource (ComplexClsList) and a field from that DataSource (Fld1) that will be used to populate my SomeField
This is just a mapping, so use some simple LINQ:
ClsList myClsList = new ClsList();
myClsList.AddRange(
myComplexList.Select(Complexitem => new Cls { SomeField = Complexitem.Fld1 })
);
Okay, the easier version assuming we have a known target field on a class (I've written this as an extension method, no need to do
public IEnumerable<Cls> MapField<TSource>(IEnumerable<TSource> sourceList,
Func<TSource, int> sourceSelector)
{
return sourceList.Select(x => new Cls {SomeField = sourceSelector(x)});
}
Called this way
IEnumerable<Cls> result = MapField(myComplexList, x => x.Fld1);
Aside: Since your myComplexList of type ComplexClsList inherits from List (which implements IEnumerable this will work. The result isn't of type ClsList that you wanted, but you could easily call .ToList() on the result and provide a constructor on ClsList that takes a List<Cls>.
And the more complicated version for when we don't know the target field (or type)...
public IEnumerable<TResult> MapField<TSource, TResult, TMap>(
IEnumerable<TSource> sourceList,
Func<TSource, TMap> sourceSelector,
Func<TMap, TResult> resultCreator)
{
return sourceList.Select(x => resultCreator(sourceSelector(x)));
}
Not as pretty to call....
IEnumerable<Cls> result = MapField(
myComplexList,
source => source.Fld1,
valueToMap => new Cls() {SomeField = valueToMap});
Might be a better way, but it's not occurring to me at the moment.
Edit: Actually, you could combine the two Func on the last one into a single one that takes a TSource and creates and maps the necessary fields to TResult, but I'm really not sure what you're gaining with that extra layer of abstraction...
You may want to reconsider extending List classes in the first place. What does inheritance give you, in this case? I suspect that you'll be better off favoring composition over inheritance here. One possible approach would be:
// If you would say that a ComplexCls "is a" Cls, then maybe your inheritance
// relationship belongs here instead.
public class ComplexCls : Cls {
}
public class ClsList
{
public IReadOnlyCollection<Cls> Items {get;set;}
}
public class ComplexClsList
{
public IReadOnlyCollection<ComplexCls> Items {get;set;}
}
Then you can create a ClsClist easily.
ClsList basicList = new ClsList{Items = complexList.Items};
But you may want to take it a step farther and question why the ClsList and ComplexClsList classes exist at all. Why not simply pass around Lists directly. I mean, what's the difference between a ClsList and a "List of Clses" (List<Cls>)?
I would like find a workaround to accomplish a simple solution in order to automatize certain operation through EF.
What I need it's takeover during saving and retrieving process to modifying query result, but this class will be able to make that work for any type entities.
Example: I have a MyTestDb. So in my C# project I create a new entity model (MyTEstDbModel.edmx), with relative POCO class generation.
Well, a point of interest could be implementing a new custom class like following:
class Example
{
private ObjectContext _context;
private Example(ObjectContext obj) { _context = obj; }
public void Store(ObjectSet<???generic???> os)
{
// problem here: I dont't know the type contained in ObjectSet
// but if I Knew its type, I could make a work like this:
// -> foreach every instance in objectSet to check if exist some property
// via reflection, if i found them, then I set always the same values.
// Why this? Because all my db contains some common filed
// like (createdByUser, TimeToUpdate, and so on..), so it wold be boring
// setting all those fileds from any point of program.
}
public void Retrive(ObjectSet<???generic???> os)
{
// here problem too: all my queries will be filtered by one or more value
// fileds, but I cannot use lambaExpression cos I don't Know the type
// contained in objectSet<..>
}
//....
finally, by any point of program, the code should appear like following:
Example obj = new Example(myEntityContext); //-> global
var result = myEntityContext.ObjectSetTyped.Where(..lambaExpression..condition)
result.someProperty = "...";
obj.Store(result); // store method will fill all other boring filed automatically.
Can anyone give me some tips, help, suggestion about my issue?
Thanks in advance...
Update
Now, just only another problem. I'd to filter my ObjectSet through retrieve method like following:
public void Retrieve<TEntity>(IQueryable<TEntity> ooo) where TEntity : IC
{
ooo = ooo.Where(p => p.Filed == "MyDefaultValue");
}
But, from external method, not objectSet result is affect by my filter.
How so..?
MyEntities ent = new...
MyWrapper wrap = new MyWrapper();
wrap.Retrieve(ent.Users);
//problem here -> users objectSet is always the same..
Define interfaces which will allow you to do this. For example:
public interface IEntity
{
DateTime CreatedAt { get; set; }
string CreatedBy { get; set; }
}
You need to "implement" this interface in your entities. You can for example either modify T4 template generating entities or implement it in partial class. Both properties must be already defined in the model so the implementation is only declarative:
public partial class MyEntity : IEntity // That's all
{ }
Now you can define Store like:
public void Store<TEntity>(TEntity entity) where TEntity : IEntity
{
...
}
Same can be done with query but you can for example define custom extension method:
public static IQueryable<TEntity> GetUserEntities(this IQueryable<TEntity> query, string user)
where TEntity : IEntity
{
return query.Where(e => e.CreatedBy == user);
}
You will simply define your query like:
var result = myEntityContext.MyEntities.GetUserEntities("someName");
Other approach is defining simply GetQuery on your custom context:
public IQueryable<T> GetQuery<T>() where T : IEntity
{
var query = GetObjectSetSomehow;
return query.ApplyGlobalConditions(); // Just another extension with your conditions
}
I'm not a big fan of the repository pattern but generally what you are trying to do is close to generic repository so check for example this post. It is just some example which can be further extended.
yeah, I just want a generic approach, likewise I realized with dataset and datatable.. but it seems impossible to achieve..
..ehmm..however, let me show yuo following code snippet, dynamic keyword looks like something to hopeful.... maybe I'm colse to solution..?
public ObjectSet<dynamic> Retrieve(string entity, string context)
{
Type myObjectContextType = Type.GetType(context);
ConstructorInfo cs = myObjectContextType .GetConstructor(new Type[] { });
dynamic myObjContext = cs.Invoke(new object[] { });
Type t = Type.GetType(entity);
ConstructorInfo xi = t.GetConstructor(new Type[] { });
dynamic UserEntity = xi.Invoke(new object[] { });
!problem here!
ObjectSet<?????> os = myObjContext.UserEntity.Where(...)
return ...
}
I'm very surprised..EF is a great instruments to develope more efficiently ways but too little "generalizalbe"
I'm trying to writing a generic method that will load a record of a specific type, with a specific ID. Here's one way that works:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.ID == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
public abstract long ID { get; set; }
}
You can ignore the call to LinqUtils.GetDataContext<T>(); that's a utility function I've got to deal with the fact that I have multiple data contexts in my program. The point is that now I can declare any of my classes as subclasses of LinqableTable, and I can easily instantiate a record of that table just by calling LinqedTable<MyType>.Get(ID).
This has some limitations, however. Firstly, it forces all of my tables to have an I
identity field of type long, named ID. Secondly, because I'm using an abstract method, I am forced to go to the O/R designer and change the inheritance property of every ID field in my system to "override".
I want more flexibility than that. So naturally, I tried reflection, and came out with the following:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.IDValue == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
internal long IDValue {
get { return (long)IDProperty.GetValue(this, null); }
set { IDProperty.SetValue(this, value, null); }
}
internal PropertyInfo IDProperty {
get { return this.GetType().GetProperty(IDPropertyName); }
}
internal protected virtual string IDPropertyName {
get { return "ID"; }
}
}
Theoretically, this allows me to override the ID column name, the cast to long should be OK with any integral data type, and I don't need to go defining all my ID columns as overrides.
BUT
Linq doesn't like this. On the call to q.Single<T>(); I get a runtime error:
The member 'EISS.Utils.LinqableTable.IDValue' has no supported translation to SQL.
OK, today I learned that Linq does some kind of magic on the back end; it doesn't instantiate obj and just read the IDValue property. So must be there's some attribute that needs to be set on the IDValue property that lets Linq do its thing.
But what?
Linq to SQL tries to translate your linq-query into SQL, but it does not know how to translate your property to a column name in the DB.
A good explanation can be found here on SO:
simple linq to sql has no supported translation to SQL
But how to solve it, is another matter. I have with succes used the apporoach from this thread:
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/df9dba6e-4615-478d-9d8a-9fd80c941ea2/
Or you can use dynamic query as mentioned here by scott guthrie:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Having read these posts: Generic Data Access using LINQ to SQL and C#,
LINQ-to-SQL: Generic Primary Key function and
Calling a generic method with Type
My colleague and I came up with the following digest:
We added the following method to our datacontext (in a partial class).
public T GetInstanceByPrimaryKey<T>(object primaryKeyValue) where T : class
{
var table = this.GetTable<T>();
var mapping = this.Mapping.GetTable(typeof(T));
var pkfield = mapping.RowType.DataMembers.SingleOrDefault(d => d.IsPrimaryKey);
if (pkfield == null)
throw new Exception(String.Format("Table {0} does not contain a Primary Key field", mapping.TableName));
var param = Expression.Parameter(typeof(T), "e");
var predicate =
Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(param, pkfield.Name), Expression.Constant(primaryKeyValue)), param);
return table.SingleOrDefault(predicate);
}
Then, where we need to instanciate from the type name and primary key value:
string name = "LinqObjectName";
int primaryKey = 123;
var dc = new YourDataContext();
Type dcType = dc.GetType();
Type type = dcType.Assembly.GetType(String.Format("{0}.{1}", dcType.Namespace, name));
MethodInfo methodInfoOfMethodToExcute = dc.GetType().GetMethod("GetInstanceByPrimaryKey");
MethodInfo methodInfoOfTypeToGet = methodInfoOfMethodToExcute.MakeGenericMethod(name);
var instance = methodInfoOfTypeToGet.Invoke(dc, new object[] { primaryKey });
return instance;
Hope this helps!
Since LINQ statements referred to a LINQ-to-SQL IQueryable are translated to SQL queries, you will have to use the AsEnumerable extension (which will in turn cause a read of all the items in the database) and do reflection-related stuff on that IEnumerable.
EDIT
As required here's a clarificationAs specified in a comment, what I meant was something like:
(from obj in context.GetTable<T>() select obj).AsEnumerable().Where(x => x.IDValue == ID)
Unlike a query executed on an IQueryable, which can be perfectly translated to SQL such as
context.GetTable().Where(x => x.Text == "Hello")
which gets converted to something similar to
SELECT * FROM TABLE_MAPPED_TO_TYPE_T WHERE Text = 'Hello'
a query executed against an IEnumerable - in your case - will be executed by fetching all the entries of your table and then applying code-wise the specified filter.