I'm using the Entity Framework with a large database (made up of more than 200 tables).
Trying to create a generic method that returns the DbSet<T> of a specific table T (i.e. class, which can be TableA).
The entity class that was (automatically) created using the entity data model looks like so:
public partial class sqlEntities : DbContext
{
public virtual DbSet<TableA> TableA { get; set; }
public virtual DbSet<TableB> TableB { get; set; }
public virtual DbSet<TableC> TableC { get; set; }
... // other methods
}
My main class is like this
public class TableModifier
{
// Should return first 10 elements from a table of that type T
public IQueryable<T> GetFromDatabase<T>() where T : EntityObject
{
try
{
using (sqlEntities ctx = new sqlEntities())
{
// Get the DbSet of the type T from the entities model (i.e. DB)
DbSet<T> dbSet = ctx.Set<T>();
return dbSet.Take(10);
}
}
catch (Exception ex)
{
// Invalid type was provided (i.e. table does not exist in database)
throw new ArgumentException("Invalid Entity", ex);
}
}
... // other methods
}
I have to set a constraint where T : EntityObject on T to be within the EntityObject bounds. If there was no such constraint then the DbSet<T> dbSet would complain (i.e. T must be a reference type) that it might be getting more than it expects in terms of types (based on this).
The problem occurs when I try to actually call the method with a specific type.
[TestMethod]
public void Test_GetTest()
{
TableModifier t_modifier = new TableModifier();
// The get method now only accepts types of type EntityObject
IQueryable<TableA> i_q = t_modifier.GetFromDatabase<TableA>();
}
It gives an error:
There is no implicit reference conversion from 'TableMod.TableA' to
'System.Data.Entity.Core.Objects.DataClasses.EntityObject'.
How can I (cast?) the TableA type as an EntityObject if I know it exists for that entity model?
Though this is incorrect syntax (and logic) this is what I'm after:
t_modifier.GetFromDatabase<(EntityObject)TableA>();
How can I define the TableA (along with all the other 200 tables) type to be a part of EntityObject?
A potential solution
Turns out my constraint was too specific, all I needed to change was from where T : IEntity to
where T : class
So the T is what the DbSet<T> initially expected, a class type
Saves the trouble of having to add implementations to the 200+ table classes, TableA, TableB, ...
Then of course there's other problems such as changing the return type from IQueryable<T> to List<T> since the IQueryable would otherwise be returned outside of the scope of DbContext (i.e. sqlEntities) rendering it useless.
Why don't you try changing your constrain to class instead of EntityObject
public IQueryable<T> GetFromDatabase<T>() where T : class
I had the same requirement and solved it by using the following:
public static void GetEntitiesGeneric<TEntity>()// where TEntity : System.Data.Entity.Core.Objects.DataClasses.EntityObject <-- NO LONGER NEEDED
{
try
{
var key = typeof(TEntity).Name;
var adapter = (IObjectContextAdapter)MyDbContext;
var objectContext = adapter.ObjectContext;
// 1. we need the container for the conceptual model
var container = objectContext.MetadataWorkspace.GetEntityContainer(
objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
// 2. we need the name given to the element set in that conceptual model
var name = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
// 3. finally, we can create a basic query for this set
var query = objectContext.CreateQuery<TEntity>("[" + name + "]");
// Work with your query ...
}
catch (Exception ex)
{
throw new ArgumentException("Invalid Entity Type supplied for Lookup", ex);
}
}
The code was taken from Using Generics for Lookup Tables in Entity Framework and adapted for EF 6 using the DbContext (first part of the method where the objectcontext is extracted from the dbcontext
Hope it helps
I don't know how you have created your model, and thus how your entities look like. But, if it's Code First, the entity classes don't inherit from a common base class, so you cannot add a type constraint to your generic.
I don't recommend using a base class to be able to specify a constraint. It's much better to do it using an interface. An empty interface will allow you to specify a constraint without having to change your classes at all.
So, what you can do is define an interface like this:
public interface IEntity {};
And then:
implement it in all classes, which can be done in partial classes files, modifying a T4 template or in some other way depending on how your model looks like
use it to specify the generic type constrain with where IEntity
This is the cleanest way to do it, without any interference to your classes.
Issue
I suppose your TableA class doesn't implement EntityObject. That's why you're getting this error. To solve this you can have an abstract class/interface which will be base for all context entities (i.e. IContextEntity which will have unique Id definition):
public class TableA : IContextEntity
{
...
}
Then same method but with new interface instead of EntityObject and you can mock/test it easily
public IQueryable<T> GetFromDatabase<T>() where T : IContextEntity
{
...
}
Usage(Tests)
Second important thing is the way you want to use this method. In case of Entity Framework context it is really important to have separation between integration and unit tests. In code you provided you're trying to reach database which means that this test will be integration:
using (sqlEntities ctx = new sqlEntities()) // This will open a DB connection
Connecting to a databases or external sources is usually a bad practice unless you know what you do and this is exactly it. If you just need some fake/dummy data to perform an action on it - use Stubs.
For any future googlers, my colleague and I just hacked this out in Visual Basic (EF 6 version). It works for our use case of simply getting a list back out but will probably work for the other use cases. No try catch or checking in this one.
Private Class getList(Of T As Class)
Public Shared Function getList() As List(Of T)
Using ctx As New MVPBTEntities()
' Get the DbSet of the type T from the entities model (i.e. DB)
Dim dbSet = ctx.Set(Of T)()
Return dbSet.ToList
End Using
End Function
End Class
Related
I have a base abstract class (baseEntity), derived from an Interface. From this, I have many sub-classes - each actually represents a database table for NHibernate. Some sub-classes contain properties that are other baseEntity classes, and some contain properties that are lists of other baseEntity classes. For some of these list properties I converted from using ILIst/List to ISet/HashSet, to stop an NHIbernate exception seen after upgrading from an ancient version
Cannot simultaneously fetch multiple bags
My question is not to do with NHIbernate
:
public interface INhEntity;
public abstract class BaseEntity;
public class OrderItems : baseEntity
{
// properties
}
public class Order : baseEntity
{
public virtual ISet<OrderItems> OrderItems { get; set;}
public Order()
{
OrderItems = new HashSet<OrderItems>();
}
}
We use ISet/HashSet because this seemed an easy and safe way to get rid of the multi-bag problem (The HashSet will quietly stop duplicates)
We also use messaging to send data between sites. Basically, each baseEntity is serialized. At the other end, we deserialize, use PropertyInfo, Type details etc to reconstruct the baseEntity sub-class
Referring to the Order/OrderItem classes above, I need to create/access/get access to the OrderItem property. This is where the fun arrives:
When I am processing a Set property, I try accessing as per below:
private void ProcessSet(object entity, ListItem listItem, PropertyInfo property)
{
//entity is an instance of the Order entity
//listitem contains info I will use to update the order item
//property is the OrderItems property. It has a vale of:
//ISet`1[Full.NameSpace.OrderItem] OrderItem}
var setInstance = GetOrCreateSetInstance(entity, property);
...processing
setInstance.Add(AnOrderItemInstance); //NHibernate knows to link the 2 database tables (OrderItems class does not need/have a FK)
}
private static ISet<baseEntity> GetOrCreateSetInstance(object entity, PropertyInfo prop)
{
var listInstance = prop.GetValue(entity, null); // This looks just what I want
var result = (ISet<baseEntity>)listINstance //cannot cast type OrderItem to baseEntity
return result
}
I have similar code to deal with lists. Because I can return a non-generic Ilist, this works a treat. Prior to upgrading NHibernate, I was using the Iesi Isets, so my GetOrCreateINstance had this signature:
private static Iesi.Collection.ISet GetOrCreateSetInstance(object entity, PropertyInfo prop)
Is there anyway I can have a method to return the various subclass instances. Note I have tried returning HashSet<baseEntity>, Iset/HashSet<INhEntity>
I have tried Casting in various methods, but usually end up with '.. is a variable but is used like a type',
I actually don't think it's possible, but any feedback most welcome
I have these POCO classes, they're mapped using Fluent API with a TPT (Table per Type) strategy.
public class Base
{
...
}
public class Derived : Base
{
...
public virtual ICollection<Foo> Foos {get; set;} // One-to-Many
}
public class Foo
{
...
public virtual ICollection<Bar> Bars {get; set;} // One-to-Many
}
public class Bar
{
...
}
My repository looks like this.
public class Repo
{
public void Update(Base item)
{
using (var ctx = new DbContext())
{
ctx.Entry(item).State = EntityState.Modified;
ctx.SaveChanges();
}
}
}
Action:
public void DoStuff()
{
Derived item = repo.GetById(1);
item.SomeProp = "xyz"; // new value
item.Foos = GenerateFoosWithBars(); // change children
repo.Update(item);
}
To my surprise Update actually works if I'm only updating the Base or Derived classes. However things turn ugly when I try to update the One-to-Many relations. I found a tutorial on how to Update One-to-Many Entities in EF4. I was really expecting EF to be way smarter then this, I mean I have to do it manually... that's so unlike everything else in EF.
So I started out trying to use Entry cause I wanted it to be generic (being able to update any Base derived class) using Entry.OriginalValues to avoid having to write a query myself. But now shit really hits the fan! Entry.OriginalValues fails with an exception saying that DbSet<Derived> doesn't exists. It's totally right, it doesn't. But it shouldn't as the the Derived is mapped to DbSet<Base> via inheritance.
Clearly I must be doing something wrong or something so different from everyone else as I'm unable to find anything useful on the matter. Haven't EF5 improved on this in anyway?
Any suggestions on how I could approach this problem?
Firstly, I think an Update method is not necessary in the Repository since EF tracks changes and applies then when you call SaveChanges() on the context.
Secondly, the problem might be that you're assigning a new collection to the Foos poperty when yo do: item.Foos = GenerateFoosWithBars(); You shouldn't do that since when EF materializes an object of the Derived type it actually returns a proxy which overrides the virtual Foos collection to use a special kind of lazy loaded collection that it tracks. If you assign a different collection of your own that will not be bound to the context. (I don't think that EF will handle that very well). What you should do is modify the collection items not the collection itself! Hope it helps!
I am having difficulty understanding why I am getting an InvalidCastException
Here is the error message that I'm getting:
Unable to cast object of type 'System.Data.Entity.DynamicProxies.Man_58184D79075BC811252680D7866D3D69D0C46FD038D3B123A5E3B102E1FC77A2' to type 'System.Data.Entity.DbSet`1[ConsoleApplication1.Man]'.
Man is the name of the entity class being passed to TEntity
I've marked the location of the error with a comment in the code.
The purpose of this repository class is to hold a given DbContext and DbSet, and call its methods to delete records and/or display all records of the given DbSet.
public class Repository<TEntity> where TEntity : class
{
private DbContext dbContext;
private DbSet<TEntity> dbSet { get; set; }
public Repository(DbContext dbContext)
{
this.dbContext = dbContext;
this.dbSet = this.dbContext.Set<TEntity>();
}
public void Delete(int id)
{
TEntity entity = dbSet.Find(id);
dbSet.Remove(entity);
dbContext.SaveChanges();
}
public void DisplayAll()
{
IQueryable Query = from item in dbContext.Set<TEntity>()
where true
select item;
foreach (DbSet<TEntity> x in Query) //InvalidCastException
{
// print entity fields to console here.
}
}
}
These are the lines in my main program that I use to create a Repository Object:
TestDataBaseEntities is the type of DbContext object I use to refer to my entity model.
class Program
{
static void Main(string[] args)
{
int myID;
var dbEntities = new TestDatabaseEntities();
Repository<Man> ManTracker = new Repository<Man>(dbEntities);
ManTracker.Display(); // exception thown in this method.
Console.WriteLine("Choose ID to delete from Men list");
myID = int.Parse(Console.ReadLine());
ManTracker.Delete(myID);
ManTracker.Display();
Console.ReadLine();
}
}
I think I figured out the answer. I was using the wrong type in the for each statement.
Instead of doing DbSet I should have used TEntity instead.
But this is misleading because even though i'm able to typecast to TEntity, I would need to have code that prints the fields of the TEntity object, and TEntity doesn't have access to the fields I need to print. Instead of using a separate repository class, I think i should use an interface that defines a function called display, and gets implemented by each entity class somehow without modifying the auto-generated code from creating the model from the database. Perhaps by creating classes that inherit from the entity classes, and then implement the interface. I had the thought to do it this way because to keep things object-oriented, it seemed to make sense to me that an object shouldn't rely on another object to display its contents to the user, it should display them itself.
So, I'm making a generic provider for my repositories implementation which will be used by my BaseController (ASP.NET MVC 2) for low-level objects. These objects have common operations, such as Activate/Deactivate/Delete/Edit, so I'll always be working with the same property on each. The problem is, since I don't know what T is, I obviously don't have access to its properties.
So, my question is, can someone show me how to get the properties I need out of the objects. I've seen some people talking about Reflection, others Expression Trees, neither of which I know how to use.
I do have a generic repository which I believe uses Expression Trees (copied it from some website), but again, I don't know what I'm doing with it... If it helps, here's what I've got so far:
public class Provider<T> where T : class {
private readonly Repository<T> Repository = null;
public Provider(
Repository<T> Repository) {
this.Repository = Repository;
}
public void Activate(
int Id) {
T Entity = this.Repository.Select(Id);
// Need to get the property here, change it and move on...
this.Repository.Submit();
}
}
I'd appreciate any help on this.
If those classes have common operations, sounds like they should inherit from the same base or implement the same interface, correct? If so, use that interface/base as the constraint for T
public class Provider<T> where T : ICommonInterface
You will then have compile-time access to the shared members provided by the interface or base class.
You could make an action
public void Activate(int Id, Action<T> doSomething)
{
T Entity = this._repository.Select(Id);
// Need to get the property here, change it and move on...
doSomething(Entity);
_repository.Submit();
}
Then using the Action delegate (in this example via a lambda) the properties will be known when activate is called:
prov.Activate(5, x => x.Address = "fgfgf");
The best solution will be to give the objects a common base type, and constrain the type parameter T to be of that type. Then you'll have access to the methods or properties of the common base type at compile time:
public class Provider<T> where T : ICommon
{
...
}
or
public class Provider<T> where T : CommonBase
{
...
}
If that's not possible, then without a common base type the best you can do is reflect upon the objects to look for and invoke the property that you are interested in:
public void Activate(int Id)
{
T entity = this.Repository.Select(Id);
// Interrogate the type of the entity and get the property called "MyPropertyName"
PropertyInfo pi = entity.GetType().GetProperty("MyPropertyName");
// Invoke the property against the entity instance - this retrieves the
// value of the property.
var value = (YourType)(pi.GetValue(entity, null));
// Do somethign with the value...
this.Repository.Submit();
}
I should add that reflection is comparatively expensive and you also lose the compile time verification. But it's handy in cases like these.
You can get a MethodInfo object for working with methods by calling:
MethodInfo mi = entity.GetType().GetMethod("MyMethodName");
I am attempting to use the Microsoft enterprise Validation methods to perform validation in my entities. In my base class I have the following method:
public class BaseEntity
{
public bool IsValid()
{
return Validate().IsValid;
}
public ValidationResults Validate()
{
return Validation.Validate<this.GetType()>(this);
}
The problem with this is that even when a subclass of BaseEntity calls IsValid, this.GetType() always returns BaseEntity, not the Subclass's type. I don't want to have to rewrite this code for every entity, as that seems very un-OO like. Is there any other way to do this?
I did have the idea to have a protected variable protected Type _validationType, and in every entity set it to that entity's .GetType value, but it seems like there has to be a better way to do this.
Update
Nevermind apparently. this.GetType() seems to be working as I was hoping. Not sure why it wasn't before.
I also changed my Validate() method to use the following code:
return ValidationFactory.CreateValidator(this.GetType()).Validate(this);
When you use an O/RM mapper such as LINQ to SQL, NHibernate or LINQ to Entities (ADO.NET Entity Framework) I'd go with another approach of validating. I'd keep the entities completely clean of validation (so no BaseEntity.Validate() in there. You can move this validation logic to the ObjectContext (EF) / DataContext (L2S) / Session (NH) and trigger validation during a database submit. Look at the example below for LINQ to SQL:
public partial class NorthwindDataContext
{
public override void SubmitChanges(ConflictMode failureMode)
{
var invalidResults = (
from entity in this.GetChangedEntities()
let type = entity.GetType()
let validator = ValidationFactory.CreateValidator(type)
let results = validator.Validate(entity)
where !results.IsValid
from result in results
select result).ToArray();
if (invalidResults.Length > 0)
{
// You should define this exception type
throw new ValidationException(invalidResults);
}
base.SubmitChanges(failureMode);
}
private IEnumerable<object> GetChangedEntities()
{
ChangeSet changes = this.GetChangeSet();
return changes.Inserts.Concat(changes.Updates);
}
}
In the case your entities are invalid, an ValidationException will be thrown. You can catch that specific exception and iterate the InvalidResults property that you'd define on it.
When you need more information, I advise you to read this article. It also describes how to do this with Entity Framework.
Good luck.
Its very un-OO like for the base class to know anything about the subclasses. This works the way it should work.
If you want to know something about the subclasses, then you need to override this method.
Apart from all that stuff about validation, there's a way to code a baseclass' method to let it know which actual subclass type it is called on.
Well... a way to pretend, at least (without breaking any OO rule, Gabriel). It is very elegant and works perfectly well :
public ValidationResults Validate<TEntity>(this TEntity e) where TEntity : BaseEntity
{
return Validation.Validate<TEntity>(e);
}
A great benefit of the extension method among others... :)
You can make IsValid() and Validate() a virtual method to provide some custom definitions in sub classes.
Moving the type logic out of BaseEntity is cleaner.
public class BaseEntity
{
public bool IsValid()
{
return Validate().IsValid;
}
public ValidationResults Validate()
{
return Validation.Validate(this);
}
}
public class Validation
{
public static ValidatorResults Validator<T>( T entity )
where T : BaseEntity
{
return ValidationFactory.CreateValidator(entity.GetType()).Validate(entity);
}
}